public interface ILoadFlowGenerator {
	Complex[] genFlow(Complex[] voltage);
}
 public interface IQChecker {
	
	boolean correct(AclfCase aclfCase);
	
	void setQLimitSparseFactor(double qLimitSparseFactor);
}
 public interface ISpecialBusChecker {
	
	void correct(AclfCase aclfCase);

	String getMethodName(); 
}
 public interface IStatistics {
	
	int getCallTimes();
	long getInitTimeUse();
	long getTimeUse();
	
	double getAverageTimeUse();
	void resetStatistics();
}
 public interface IVgcBuilder {
	VoltageGenCondition nowCondition();
	VoltageGenCondition nextCondition();
}
 public interface IVoltageGenerator {
	Complex[] genVoltage(VoltageGenCondition c);
}
 public abstract class AlgoObject implements IStatistics {
	
	/*
	 * IStatistics
	 */
	private int callTimes = 0;
	protected long initTimeUse = 0;
	protected long timeUse = 0;
	
	/*
	 * method
	 */
	protected RefInfo ref = null;
	protected String methodName = null;

	public AlgoObject(RefInfo refInfo) {
		ref = refInfo;
		
		callTimes = 0;
		initTimeUse = 0;
		timeUse = 0;
		methodName = new String("Unkown_Algo");
	}
	
	public abstract String keyMsg();

	public void report() {
		if (!ref.isPrintReport())
			return;
		System.out.printf("[REPORT] %25s called %10d times, with time use(s) = %10.0f, in which init time use = %10.3f\n",
				methodName, callTimes, timeUse/1000.0, initTimeUse/1000.0);
	}
	
	public String getMethodName() {
		return methodName;
	}
	
	public RefInfo getRefInfo() {
		return ref;
	}

	@Override
	public int getCallTimes() {
		return this.callTimes;
	}

	@Override
	public long getInitTimeUse() {
		return this.initTimeUse;
	}

	@Override
	public long getTimeUse() {
		return this.timeUse;
	}

	@Override
	public double getAverageTimeUse() {
		return this.timeUse/this.callTimes;
	}
	
	@Override
	public void resetStatistics() {
		callTimes = 0;
		initTimeUse = 0;
		timeUse = 0;
		if (!ref.isPrintReport())
			return;
		System.out.printf("[REPORT] method %24s is reset, now called times =  %10d, with time use(s) = %10.0f, in which init time use = %10.3f\n",
				methodName, callTimes, timeUse/1000.0, initTimeUse/1000.0);
	}
	
	protected void addCallTimes() {
		this.callTimes += 1;
	}
	
	protected void addInitTime(long startTime) {
		long timeuse = System.currentTimeMillis() - startTime;
		this.initTimeUse += timeuse;
		this.timeUse += timeuse;
	}
	
	protected void addTimeUse(long startTime) {
		this.timeUse += System.currentTimeMillis() - startTime;
	}

}
 public class LoadFlowGenerator extends AlgoObject implements ILoadFlowGenerator {

	public static final String NAME = "LoadFlowGenerator";
	public static final String NAME_IN_SHORT = "LFG";
	public static final String PARA_NEEDED = "NONE";
	
	private int noBus = 0;
	private Complex[][] yc = null;
	
	public LoadFlowGenerator(RefInfo refInfo) {
		super(refInfo);
		long startTime = System.currentTimeMillis();
		this.methodName = NAME;
		System.out.print("[REPORT] new "+methodName+"...");
		
		this.noBus = ref.getNoBus();
		this.yc = new Complex[noBus][noBus];
		
		Complex[][] y = ref.getY();
		for (int i=0; i<noBus; ++i)
			for (int j=0; j<noBus; ++j) 
				this.yc[i][j] = new Complex(y[i][j].getReal(), 0 - y[i][j].getImaginary());//È¡¹²éî
		
		AclfCase.init(this);
		//report
		System.out.println(" ...ready. Need to input parameter: "+PARA_NEEDED);
		addInitTime(startTime);
	}
	
	/**
	 * Éú³É³±Á÷µÄºËÐÄ·½·¨,¸´ÔÓ¶ÈÎªn^2
	 * @param onGoNet
	 * @return net£¨Ö±½ÓÉú³ÉºóÐÞ¸Äµ½ÍøÂç£©
	 */
	public Complex[] genFlow(Complex[] voltage) {
		long startTime = System.currentTimeMillis();
		addCallTimes();
		
		Complex[] power = new Complex[noBus];
		
		for (int i=0; i<noBus; ++i) {
			power[i] = new Complex(0, 0);
			for (int j=0; j<noBus; ++j) {//Y*V*
				power[i] = power[i].add(this.yc[i][j].multiply(voltage[j].conjugate()));
			}
			power[i] = voltage[i].multiply(power[i]);//VY*V*
		}
		
		addTimeUse(startTime);
		return power;
	}

	public int getNoBus() {
		return noBus;
	}

	public Complex[][] getYc() {
		return yc;
	}

	@Override
	public String keyMsg() {
		return NAME_IN_SHORT;
	}

}
 public class QChecker extends AlgoObject implements IQChecker {

	public static final String NAME = "Default_QChecker";
	public static final String NAME_IN_SHORT = "dQC";
	public static final String PARA_NEEDED = "qLimitSparseFactor";
	
	protected static final double DEFAULT_Q_SPARSE = 0.95;

	/**
	 * ÓÉÓÚÊµ¼ÊÖÐÈç¹ûÐ£Õýµ½¼«ÏÞµÄ»°ÍùÍùÔÙÎ¢µ÷ÓÖÓÐ³¬ÏÞÇé¿ö·¢Éú£¬ËùÒÔÐèÒªÉèËÉ³ÚÒò×Ó
	 */
	protected double qLimitSparseFactor = 0;
	
	/*
	 * ´ÓrefµÃµ½µÄÖ¸Õë£¬ÊÇ±ÜÃâ¶à²ãÒýÓÃºÍ±ÜÃâÖµÖØ¸´µÄÍ×Ð­
	 */
	protected int noBus = 0;
	protected double[][] b11r = null;
	protected int swingNo = 0;
	protected String[] busCode = null;
	protected int[] busType = null;
	protected double[] maxQGenLimit = null;
	protected double[] minQGenLimit = null;
	
	public QChecker(RefInfo refInfo) {
		super(refInfo);
		long startTime = System.currentTimeMillis();
		this.methodName = "Default_QChecker";
		System.out.print("[REPORT] new "+methodName+"...");
		
		this.qLimitSparseFactor = QChecker.DEFAULT_Q_SPARSE;
		
		noBus = ref.getNoBus();
		maxQGenLimit = ref.getMaxQGenLimit();
		minQGenLimit = ref.getMinQGenLimit();
		b11r = ref.getB11r();
		busCode = ref.getBusCode();
		busType = ref.getBusType();
		swingNo = ref.getSwingNo();
		
		//report
		System.out.println(" ...ready. Need to input parameter: "+PARA_NEEDED);
		addInitTime(startTime);
	}
	
	/**
	 * µ±Óöµ½Ô½ÏÞµÄÇé¿öÊ±µ÷ÓÃµÄ·½·¨£¬Ó¦ÓÃPQ·Ö½â·¨£¬ÓÃB¾ØÕóµÄÄæÇóµçÑ¹ÐÞÕýÁ¿
	 * Ö»ÐÞ¸ÄbusCode==GenPVµÄ½Úµã
	 * ÒÑÔÚLFTest()ÖÐÈ·ÈÏ£¬min/maxQGenLimit ¾ùÎª±êçÛÖµ
	 * -dV = br * dQ/V
	 * V = V - dV
	 * TODO£º×¢Òâ£¡£¡£¡£¡£¡£¡£¡£¡£¡£¡£¡£¡£¡£¡£¡£¡£¡£¡£¡£¡£¡£¡£¡£¡£¡£¡£¡£¡£¡£¡£¡£¡£¡£¡£¡£¡£¡£¡£¡£¡£¡£¡£¡£¡ÕâÀïÏÞÖÆÁËPQ+PV½Úµã£¬µ«ÎÒÈÏÎªÖ»ÄÜÏÞÖÆ´¿PV½Úµã
	 * @param voltage µçÑ¹Öµ
	 * @param power µ±Ç°¹¦ÂÊ·Ö²¼£¬ÓÃÀ´ÌáÈ¡ÎÞ¹¦ÐÞÕýÁ¿
	 * @return
	 * @throws InterpssException
	 */
	@Override
	public boolean correct(AclfCase aclfCase) {
		long startTime = System.currentTimeMillis();
		
		Complex[] power = aclfCase.getPower();
		//get dQ[]
		boolean qOverflow = false;
		double[] dQ = new double[noBus];
		for (int i=0; i<noBus; ++i) {
			double q = power[i].getImaginary();
			if (busType[i] == RefInfo.ONLY_PV_BUS_TYPE /*busCode[i].equals("GenPV")*/) {
				if (q > maxQGenLimit[i]) {
					dQ[i] = maxQGenLimit[i] * this.qLimitSparseFactor - q;
					qOverflow = true;
				}else if (q < minQGenLimit[i]) {
					dQ[i] = minQGenLimit[i] * this.qLimitSparseFactor - q;
					qOverflow = true;
				}else
					dQ[i] = 0;
			}else {
				dQ[i] = 0;
			}
		}
		//Èç¹ûÃ»Ô½ÏÞÔòÌø¹ýÏÂÃæ£¬Ö±½Ó·µ»ØÃ»Ô½ÏÞ¾ÍÐÐÁË
		if (!qOverflow) {
			addTimeUse(startTime);
			return true;
		}
		addCallTimes();
		
		//Èç¹ûÔ½ÏÞÁË£¬ÐÞÕýµçÑ¹
		Complex[] voltage = aclfCase.getVoltage();
		//get dQ/V
		double[] dqv = new double[noBus]; 
		for (int i=0; i<noBus; ++i) {
			dqv[i] = dQ[i] / voltage[i].abs();
		}
		//¾ØÕó³Ë·¨  -dV = br * dQ/V
		double[] dV = new double[noBus]; 
		for (int i=0; i<noBus; ++i) {
			dV[i] = 0;
			if (i != swingNo && !busCode[i].equals("GenPV"))//×¢Òâµ½ÀíÂÛÉÏµÄb11r¾ØÕóÊÇÕâÀïµÄb11rÈ¥µôswingºÍPVÐÐÁÐ
				for (int j=0; j<noBus; ++j)
					if (j != swingNo && !busCode[i].equals("GenPV"))
						dV[i] -= b11r[i][j] * dqv[j];
		}
				
		//correct v += dV, theta = theta
		for (int i=0; i<noBus; ++i) 
			if (i != swingNo){
				double v = voltage[i].abs() + dV[i];
				voltage[i] = voltage[i].multiply(v/voltage[i].abs());
			}
		
		aclfCase.setVoltage(voltage);
		
		addTimeUse(startTime);
		return false;
	}

	/**
	 * Ö§³ÖÐÞ¸ÄÄ¬ÈÏÖµ
	 */
	@Override
	public void setQLimitSparseFactor(double qLimitSparseFactor) {
		this.qLimitSparseFactor = qLimitSparseFactor;
		System.out.println("[REPORT] "+methodName+" paramter set. qLimitSparseFactor = "+qLimitSparseFactor);
	}

	public int getNoBus() {
		return noBus;
	}

	public double[][] getB11r() {
		return b11r;
	}

	public String[] getBusCode() {
		return busCode;
	}

	public double[] getMaxQGenLimit() {
		return maxQGenLimit;
	}

	public double[] getMinQGenLimit() {
		return minQGenLimit;
	}

	public double getQLimitSparseFactor() {
		return qLimitSparseFactor;
	}

	@Override
	public String keyMsg() {
		return NAME_IN_SHORT+"-qf-"+this.qLimitSparseFactor;
	}
	
}
 public class SpecialBusChecker extends AlgoObject implements ISpecialBusChecker {

	public static final String NAME = "Default_SpecialBusChecker";
	public static final String NAME_IN_SHORT = "dSBC";
	public static final String PARA_NEEDED = "NONE";
	
	/**
	 * ³£Êý¾ØÕó,ÕâÊÇ¸úÐ£ÕýÓÐ¹ØµÄ¾ßÌå²ÎÊý£¬²»±£´æÔÚRefNetInfoÖÐ
	 */
	protected Complex[][] vt = null;
	
	/*
	 * ´ÓrefµÃµ½µÄÖ¸Õë£¬ÊÇ±ÜÃâ¶à²ãÒýÓÃºÍ±ÜÃâÖµÖØ¸´µÄÍ×Ð­
	 */
	protected int noBus = 0;
	protected int swingNo = 0;
	

	public SpecialBusChecker(RefInfo refInfo) {
		super(refInfo);
		long startTime = System.currentTimeMillis();
		this.methodName = NAME;
		System.out.print("[REPORT] new "+methodName+"...");
		
		this.noBus = ref.getNoBus();
		this.swingNo = ref.getSwingNo();
		
		Complex[][] yr = ref.getYr();
		int[] busType = ref.getBusType();
		//¼ÆËãm01, m01 = -1/yr00 * yr01, dI0 == m01 * dI1
		Complex[] m01 = new Complex[noBus];
		for (int i=0; i<noBus; ++i) {
			m01[i] = new Complex(0, 0);
			if (busType[i] == RefInfo.CONNECT_BUS_TYPE) //ÕâÀïËµÃ÷m01½öÔÚ1´¦ÓÐÖµ
				m01[i] = yr[swingNo][i].divide(yr[swingNo][swingNo]).multiply(-1);
		}
		//¼ÆËãz¾ØÕó, z = yr12,0 * m01 + yr12,1, dV = z * dI
		Complex[][] z = new Complex[noBus][noBus];
		for (int i=0; i<noBus; ++i)
			for (int j=0; j<noBus; ++j) {
				z[i][j] = new Complex(0.0, 0.0);
				if (i != swingNo && busType[j] == RefInfo.CONNECT_BUS_TYPE) //¶ÔiËµÃ÷z¹²12ÐÐ£¬¶ÔjËµÃ÷z¹²1ÁÐ
					z[i][j] = yr[i][swingNo].multiply(m01[j]).add(yr[i][j]);
			}
		
		//Éú³Évt, vt = z * -Y1, dV12 = vt * V012
		this.vt = new Complex[noBus][noBus];
		Complex[][] y = ref.getY();
		for (int i=0; i<noBus; ++i)
			for (int j=0; j<noBus; ++j) {
				vt[i][j] = new Complex(0.0, 0.0);
				if (i != swingNo)//ÕâÀïËµÃ÷vt¹²1+2ÐÐ£¬¶ÔjÃ»ÅÐ¶ÏËµÃ÷vt¹²0+1+2ÐÐ
					for (int k=0; k<noBus; ++k)
						if (busType[k] == RefInfo.CONNECT_BUS_TYPE)
							vt[i][j] = vt[i][j].subtract(z[i][k].multiply(y[k][j]));
			}
		
		//report
		System.out.println(" ...ready. Need to input parameter: "+PARA_NEEDED);
		addInitTime(startTime);
	}
	
	/**
	 * ±¾ÀàµÄÖ÷·½·¨£¬ÓÃÓÚ½«ÊäÈëÍøÂçµÄÁªÂç½Úµã¹¦ÂÊÐ£ÕýÎª0
	 * Ð£ÕýºóµÄµçÑ¹ÏòÁ¿Í¬Ê±ÐÞ¸Äµ½ÍøÂç
	 * @param net
	 * @return Ð£ÕýºóµÄµçÑ¹ÏòÁ¿
	 */
	@Override
	public void correct(AclfCase aclfCase) {
		long startTime = System.currentTimeMillis();
		addCallTimes();
		
		Complex[] voltage = aclfCase.getVoltage();
		
		//¾ØÕó³Ë·¨
		Complex[] dV = new Complex[noBus]; 
		for (int i=0; i<noBus; ++i) {
			dV[i] = new Complex(0.0, 0.0);
			if (i != swingNo)
				for (int j=0; j<noBus; ++j)
					dV[i] = dV[i].add(vt[i][j].multiply(voltage[j]));
		}
		
		//correct v += dV, theta = theta
		for (int i=0; i<noBus; ++i) {
			voltage[i] = voltage[i].add(dV[i]);
		}
		
		//ÏÔÊ½¸³Öµ£¬´¥·¢case.coincide£½false
		aclfCase.setVoltage(voltage);

		addTimeUse(startTime);
	}

	public int getNoBus() {
		return noBus;
	}

	public Complex[][] getVt() {
		return vt;
	}

	@Override
	public String keyMsg() {
		return NAME_IN_SHORT;
	}
}
 public class VoltageGenerator extends AlgoObject implements IVoltageGenerator {

	public static final String NAME = "Default_VoltageGenerator";
	public static final String NAME_IN_SHORT = "dVG";
	public static final String PARA_NEEDED = "NONE";
	
	protected double[][] b1r = null;
	protected int noBus = 0;
	protected int swingNo = 0;
	
	public VoltageGenerator(RefInfo refInfo) {
		super(refInfo);
		long startTime = System.currentTimeMillis();
		this.methodName = NAME;
		System.out.print("[REPORT] new "+methodName+"...");
		
		this.b1r = ref.getB1r();
		this.noBus = ref.getNoBus();
		this.swingNo = ref.getSwingNo();
		
		//report
		System.out.println(" ...ready. Need to input parameter: "+PARA_NEEDED);
		addInitTime(startTime);
	}
	
	/**
	 * ÓÐÄ¿µÄÐÔµÄÉú³É
	 * Éú³ÉµçÑ¹µÄÖ÷·½·¨£¬ÓÉ·ùÖµºÍÓÐ¹¦Éú³É¡££¨ÒòÎª¸ø¶¨ÓÐ¹¦Ò»¸öÊÇ·ûºÏ¹¤³ÌÉÏµÄÐèÒª£¬Ò»¸öÊÇ·ûºÏÊµ¼ÊÉú³ÉÖÐÏ÷¼õ¿Õ¼ä£¬Ìá¸ßÓÐÐ§ÐÔµÄ±ØÒª£©
	 * note: ÓÐ¹¦Ë®Æ½¸üÎªÖØÒª£¬·Å²ÎÊý±íÇ°Ãæ
	 * @param v
	 * @param p
	 * @return
	 */
	@Override
	public Complex[] genVoltage(VoltageGenCondition c) {
		long startTime = System.currentTimeMillis();
		addCallTimes();
		
		Complex[] voltage;
		if (c.isThVMethod()) {
			voltage = c.getVoltage();
		}else {
			voltage = toComplexV(c.getV(), genTheta(c.getP()));
		}
		
		addTimeUse(startTime);
		return voltage;
	}
	
	/**
	 * ¸ù¾Ý¸ø¶¨µÄp£¬ÀûÓÃÖ±Á÷³±Á÷µÃµ½Ïà½Ç£¬Ð´ËÀµÄ¡£
	 * P=B¦È => ¦È = B^-1 * P
	 * @param p
	 * @return
	 */
	private double[] genTheta(double[] p) {
		double[] theta = new double[noBus];
		
		for (int i=0; i<noBus; ++i) {
			theta[i] = 0;
			if (i != swingNo)
				for (int j=0; j<noBus; ++j) 
					if (j != swingNo)
					theta[i] -= this.b1r[i][j] * p[j];
		}
		
		return theta;
	}

	/**
	 * ¸ù¾ÝµçÑ¹·ùÖµºÍÏà½ÇÉú³ÉµçÑ¹ÏàÁ¿µÄÎ¨Ò»£¨»®ÖØµã£©·½·¨£¬Ö»ÐèÒªÔÚ´ËÏÔÊ½µØ½«swing½ÚµãµçÑ¹¸Ä»ØÀ´¾ÍºÃÁË
	 * @param v
	 * @param th
	 * @return
	 */
	 private Complex[] toComplexV(double[] v, double[] th) {
		Complex[] voltage = new Complex[noBus];
		for (int i=0; i<noBus; ++i) {
			voltage[i] = new Complex(v[i]*Math.cos(th[i]), v[i]*Math.sin(th[i]));
		}
//		//ÏÔÊ½µØ½«swing·ùÖµÐ£Õý
//		if (!Complex.equals(voltage[this.swingNo], new Complex(1, 0))) {
//			//Debug
//			System.out.println("[DEBUG] VoltageGenerator.toComplexV: voltage[swingNo] != (1, 0), == "+voltage[this.swingNo]+"\t, but it is already fixed to (1, 0)!");
//			String errStr = new String("");
//			for (int i=0; i<noBus; ++i) 
//				errStr += "Bus "+i+" V = "+voltage[i]+"\t";
//			System.out.println(errStr);
//			
//			voltage[this.swingNo] = new Complex(1, 0);
//		}
		return voltage;
	}
	
	public double[][] getB1r() {
		return b1r;
	}

	public int getNoBus() {
		return noBus;
	}

	public int getSwingNo() {
		return swingNo;
	}

	@Override
	public String keyMsg() {
		return NAME_IN_SHORT;
	}
}
 public class VThScanConditionBuilder extends AlgoObject implements IVgcBuilder {

	public static final String NAME = "VTHScanBuilder";
	public static final String NAME_IN_SHORT = "VTH";
	public static final String PARA_NEEDED = "vSparseFactor";

	private static final double DEFAULT_BOUNDARY_FACTOR = 1.0;
	private static final int DEFAULT_BEGIN_STATUS = -1;
	
	private VoltageGenCondition c = null;
	
	private int noBus = 0;
	private int swingNo = 0;
	
	private int status = 0;
	
	private double[] baseTheta = null;
	private double[] baseV = null;
	private double[] voltageMagError = null;
	private double boundaryFactor = 1;
	
	private Complex[] voltage = null;
	
	public VThScanConditionBuilder(RefInfo refInfo) {
		super(refInfo);
		long startTime = System.currentTimeMillis();
		this.methodName = NAME;
		System.out.print("[REPORT] new "+methodName+"...");
		
		c = new VoltageGenCondition(true);
		this.noBus = ref.getNoBus();
		this.swingNo = ref.getSwingNo();
		boundaryFactor = DEFAULT_BOUNDARY_FACTOR;
		this.status = DEFAULT_BEGIN_STATUS;

		baseTheta = new double[noBus];
		baseV = new double[noBus];
		voltageMagError = new double[noBus];
		voltage = new Complex[noBus];
		
		EList<AclfBus> busList = ref.getNet().getBusList();
		for (int i=0; i<noBus; ++i) {
			baseTheta[i] = busList.get(i).getVoltageAng();
			baseV[i] = busList.get(i).getVoltageMag();
			voltageMagError[i] = getVoltageMagError(busList.get(i).getBaseVoltage());
		}

		//report
		System.out.println(" ...ready. Need to input parameter: "+PARA_NEEDED);
		System.out.println("[WARNING] this method is only used in IEEE 14 bus system!");
		addInitTime(startTime);
	}
	
	public void setVBoundaryFactor(double f) {
		this.boundaryFactor = f;
		System.out.println("[REPORT] "+methodName+" paramter set. vBoundaryFactor = "+boundaryFactor);
	}

	@Override
	public VoltageGenCondition nowCondition() {
		return c;
	}

	@Override
	public VoltageGenCondition nextCondition() {
		long startTime = System.currentTimeMillis();
		
		this.status += 1;
		for (int i=0; i<noBus; ++i) 
			if (i != swingNo) {
				//Î»ÔËËã
				double mag = baseV[i] * (1 - boundaryFactor * voltageMagError[i] * (1 - 2 * (status & 1)));
				status = status>>>1;
				//ÕâÀïÊÇÓÐÎóµÄ...²»ÄÜÓÃµçÑ¹µÄ·ùÖµÀ´ÏÞÖÆ·ø½Ç
				double ang = baseTheta[i] * (1 - boundaryFactor * voltageMagError[i] * (1 - 2 * (status & 1)));
				status = status>>>1;
			
				voltage[i] = new Complex(mag * Math.cos(ang), mag * Math.sin(ang));
			}else
				voltage[i] = new Complex(1.0, 0);
		
		//ÏÔÊ½¸³Öµ£¬Æ½ºâ½Úµã»¹ÊÇ²»±äµÄ
		voltage[swingNo] = new Complex(1.0, 0.0);
		if (status == 1<<26)
			status = -1;
		c.setVoltage(voltage);
		
		addTimeUse(startTime);
		return c;
	}
	
	public VoltageGenCondition getC() {
		return c;
	}

	public int getNoBus() {
		return noBus;
	}

	public int getSwingNo() {
		return swingNo;
	}

	public int getStatus() {
		return status;
	}

	public double[] getBaseTheta() {
		return baseTheta;
	}

	public double[] getBaseV() {
		return baseV;
	}

	public double[] getVoltageMagError() {
		return voltageMagError;
	}

	public double getBoundaryFactor() {
		return boundaryFactor;
	}

	public Complex[] getVoltage() {
		return voltage;
	}

	/**
	 * ¶Ô¸ø¶¨µÄµçÑ¹µÈ¼¶·µ»ØÔÊÐíµÄµçÑ¹Æ«²î°Ù·Ö±È£¬Ref£ºµçÁ¦ÏµÍ³·ÖÎöÉÏ²á£¬P5.
	 * note: IEEE¸ñÊ½ÖÐ³ýÁË14busÓÐ135ºÍ35£¬10Ö®Íâ£¬ÆäËûµÄ¾ùÎª100
	 * note: ½öÔÚ¹¹Ôìº¯ÊýÖÐÖ´ÐÐÒ»´Î
	 * @param voltageLevel
	 * @return
	 */
	protected static double getVoltageMagError(double voltageLevel) {
		if (voltageLevel < 34)
			return 0.07;
		return 0.07;
	}

	@Override
	public String keyMsg() {
		return NAME_IN_SHORT+"-vsf-"+boundaryFactor;
	}
}
 public class AclfCase {
	
	/**
	 * ÓÃÓÚµçÑ¹·¢Éú±ä»¯Ö®ºó£¬ÐèÒªµ÷ÓÃpowerÊ±£¬×Ô¶¯¸üÐÂpower
	 */
	private static LoadFlowGenerator lfg = null;
	
	/**
	 * Ñù±¾×´Ì¬
	 * 0 - ÐÂµÄV£¬PV¶Ô²»ÉÏ
	 * 1 - V²»±ä£¬PV¶ÔµÃÉÏ
	 */
	private boolean coincide = false;
	
	private Complex[] voltage = null;
	private Complex[] power = null;

	public AclfCase(Complex[] voltage) {
		this.voltage = voltage;
		this.power = new Complex[voltage.length];//Ó¦µ±×¢Òâ´ËÊ±ÊÇ¿ÕµÄ
		this.coincide = false;
	}

	public boolean getCoincide() {
		return coincide;
	}

	public Complex[] getVoltage() {
		return voltage;
	}

	public void setVoltage(Complex[] voltage) {
		this.voltage = voltage;
		this.coincide = false;
	}

	public Complex[] getPower() {
		if (!this.coincide) {
			power = lfg.genFlow(voltage);
			coincide = true;
		}
		return power;
	}
	
	@Override
	public String toString() {
		String str = new String("");
		String[] busCode = lfg.getRefInfo().getBusCode();
		for (int i=0; i<voltage.length; ++i) {
			str += "Bus "+i+"\t"+busCode[i]+"\t";
			if (busCode[i].equals("Swing")) {
				str += getVoltage()[i];
			}else if (busCode[i].equals("GenPV")) {
				str += getPower()[i].getReal()+"\t"+getVoltage()[i].abs();
			}else if (busCode[i].equals("GenPQ")) {
				str += getPower()[i];
			}
			str += "\n";
		}
		return str;
	}
	
	/**
	 * ÔÚ³ÌÐò³õÊ¼»¯½×¶Î¸øÕâ¸ö¸³Öµ£¬Ö®ºó²»ÓÃÏÔÊ½µ÷ÓÃlfg
	 * @param lfg
	 */
	public static void init(LoadFlowGenerator lfg) {
		AclfCase.lfg = lfg;
	}
	
}
 public class VoltageGenCondition {
	
	private static RefInfo ref = null;
	
	private double[] p = null;

	private double[] v = null;
	
	private Complex[] voltage = null;

	/**
	 * Ö¸Ê¾ÀàÖÐµÄÊý×é´æµÄÊÇÊýÖµ»¹ÊÇÒòÊý
	 */
	private boolean pIsFactor = false;

	private boolean vIsFactor = false;

	/**
	 * ÊÇ·ñÊôÓÚÖ±½Ó¸ø¶¨VthetaµÄ£¬·ñÔòÊôÓÚPV·½·¨¡£
	 */
	private boolean isThVMethod = false;
	
	private int bitState = 0;

	/**
	 * ÊÇ·ñÊÇÎ»ÔËËã·½·¨£¨TraversalVoltageGenerator¶ÀÓÐ£©
	 */
	private boolean isBit = false;
	
	public VoltageGenCondition(boolean isThVMethod) {
		BuildCondition(isThVMethod, null, false, null, false, false, 0, true);
	}
	
	public VoltageGenCondition(boolean isThVMethod, int bitState) {
		BuildCondition(isThVMethod, null, false, null, false, true, bitState, true);
	}

	public VoltageGenCondition(boolean isThVMethod, double[] p, boolean pIsFactor) {
		BuildCondition(isThVMethod, p, pIsFactor, null, false, false, 0, false);
	}
	
	public VoltageGenCondition(boolean isThVMethod, double[] p, boolean pIsFactor, double[] v, boolean vIsFactor, boolean isThV) {
		BuildCondition(isThVMethod, p, pIsFactor, v, vIsFactor, isThV, 0, false);
	}
	
	public VoltageGenCondition(boolean isThVMethod, double[] p, boolean pIsFactor, double[] v, boolean vIsFactor, boolean isThV, int bitState, boolean isBit) {
		BuildCondition(isThVMethod, p, pIsFactor, v, vIsFactor, isThV, bitState, isBit);
	}
	
	private void BuildCondition(boolean isThVMethod, double[] p, boolean pIsFactor, double[] v, boolean vIsFactor, boolean isThV, int bitState, boolean isBit) {
		if (p != null) 
			this.p = p;
		else
			this.p = new double[ref.getNoBus()];
		this.pIsFactor = pIsFactor;
		if (v != null)
			this.v = v;
		else
			this.v = new double[ref.getNoBus()];
		this.voltage = new Complex[ref.getNoBus()];
		this.vIsFactor = vIsFactor;
		this.isThVMethod = isThVMethod;
		this.bitState = bitState;
		this.isBit = isBit;
	}

	public double[] getP() {
		return p;
	}

	public void setP(double[] p) {
		this.p = p;
	}

	public void setPi(double p, int index) {
		this.p[index] = p;
	}
	public double[] getV() {
		return v;
	}

	public void setV(double[] v) {
		this.v = v;
	}
	
	public Complex[] getVoltage() {
		return voltage;
	}

	public void setVoltage(Complex[] voltage) {
		this.voltage = voltage;
	}

	public void setVi(double v, int index) {
		this.v[index] = v;
	}
	public boolean pIsFactor() {
		return pIsFactor;
	}

	public boolean vIsFactor() {
		return vIsFactor;
	}

	public boolean isThVMethod() {
		return isThVMethod;
	}

	public int getBitState() {
		return bitState;
	}
	
	public void setBitState(int bitState) {
		this.bitState = bitState;
	}
	
	public boolean isBit() {
		return isBit;
	}
	
	public static void init(RefInfo ref) {
		VoltageGenCondition.ref = ref;
	}
}
 public class RefInfo {
	
	public static int SWING_BUS_TYPE = 0;
	public static int ONLY_PV_BUS_TYPE = 1;
	public static int CONNECT_BUS_TYPE = 2;
	public static int OTHER_BUS_TYPE = 3;
	public static int PV_PQ_BUS_TYPE = 4;
	
	private int noBus = 0;
	private int swingNo = 0;
	private int[] busType = null;
	private String[] busCode = null;
	private double[][] b = null;
	private double[][] br = null;
	private double[][] b1 = null;
	private double[][] b1r = null;
	private double[][] b11 = null;
	private double[][] b11r = null;
	private Complex[][] y = null;
	private Complex[][] yr = null;
	
	/**
	 * Ô¤·ÀÓÐÊ²Ã´Ã»Ïëµ½µÄÐÅÏ¢»¹Òª´ÓÕâÀï»ñÈ¡
	 */
	private AclfNetwork net = null;
	
	/**
	 * ÐèÒª´æ´¢ÎÞ¹¦ÏÞÖÆÏòÁ¿À´¼ÓËÙ
	 * ÒòÎªÍøÂçÊÇÈ·¶¨µÄ£¬½ÚµãµÄÎÞ¹¦ÏÞÖÆÒ²ÊÇÈ·¶¨µÄ
	 */
	private double[] maxQGenLimit = null;
	private double[] minQGenLimit = null;
	
	public AclfNetwork getNet() {
		return net;
	}
	
	private boolean printReport = false;

	public boolean isPrintReport() {
		return printReport;
	}

	public void setPrintReport(boolean printReport) {
		this.printReport = printReport;
	}

	public RefInfo(AclfNetwork refNet) throws InterpssException {

		//report
		System.out.println("[REPORT] new NetInfoImpl...");
		
		this.printReport = true;
		
		this.net = refNet;
		this.noBus = refNet.getNoBus();
		
		this.busType = new int[noBus];
		this.busCode = new String[noBus];
		for (int i=0; i<noBus; ++i) {
			AclfBus bus = refNet.getBusList().get(i);
			String genCode = bus.getGenCode().getName();
			if (genCode.equals("Swing")) {//0 - Æ½ºâ½Úµã
				this.busType[i] = RefInfo.SWING_BUS_TYPE;
				this.swingNo = i;
			}else if (genCode.equals("GenPV")) {//1 - ´¿PV½Úµã
				if (Complex.equals(bus.getLoadPQ(), Complex.ZERO))
					this.busType[i] = RefInfo.ONLY_PV_BUS_TYPE;
				else
					this.busType[i] = RefInfo.PV_PQ_BUS_TYPE;
			}else if (genCode.equals("GenPQ") && bus.getGenP() == 0 && bus.getGenQ() == 0 && bus.getLoadP() == 0 && bus.getLoadQ() == 0) {//2 - ÁªÂç½Úµã
				this.busType[i] = RefInfo.CONNECT_BUS_TYPE;
			}else//3 - ÆäËûPQ½Úµã
				this.busType[i] = RefInfo.OTHER_BUS_TYPE;
			this.busCode[i] = genCode;
		}
		
		/*
		 * get Y matrix
		 * AX=B: 
		 * 	A--Y
		 * 	X--0
		 * 	B--0
		 */
		this.y = new Complex[noBus][noBus];
		this.b = new double[noBus][noBus];
		this.yr = new Complex[noBus][noBus];
		ISparseEqnComplex yeqn = refNet.formYMatrix();
		for (int i=0; i<noBus; ++i)
			for (int j=0; j<noBus; ++j) {
				this.y[i][j] = yeqn.getA(i, j);
				this.b[i][j] = y[i][j].getImaginary();
			}
		for (int i=0; i<noBus; ++i) {
			//°´ÁÐÇóÄæ
			for (int j=0; j<noBus; ++j)
				yeqn.setBi(new Complex(0.0, 0.0), j);
			yeqn.setBi(new Complex(1.0, 0.0), i);
			
			try {
				yeqn.solveEqn();
			} catch (IpssNumericException e) {
				e.printStackTrace();
			}
			
			for (int j=0; j<refNet.getNoBus(); ++j)
				this.yr[j][i] = yeqn.getX(j);
		}
		
		/*
		 * get B matrix
		 * AX=B: 
		 * 	A--B
		 * 	X--0
		 * 	B--0
		 */
		this.b1 = new double[noBus][noBus];
		this.b1r = new double[noBus][noBus];
		ISparseEqnDouble beqn = refNet.formB1Matrix();
		for (int i=0; i<noBus; ++i)
			for (int j=0; j<noBus; ++j)
				this.b1[i][j] = beqn.getAij(i, j);
		for (int i=0; i<noBus; ++i) {
			//°´ÁÐÇóÄæ
			for (int j=0; j<noBus; ++j)
				beqn.setBi(0.0, j);
			beqn.setBi(1, i);
			
			try {
				beqn.solveEqn();
			} catch (IpssNumericException e) {
				e.printStackTrace();
			}
			
			for (int j=0; j<noBus; ++j)
				this.b1r[j][i] = beqn.getXi(j);
		}
		this.br = new double[noBus][noBus];
		for (int i=0; i<noBus; ++i)
			for (int j=0; j<noBus; ++j)
				beqn.setAij(b[i][j], i, j);
		for (int i=0; i<noBus; ++i) {
			//°´ÁÐÇóÄæ
			for (int j=0; j<noBus; ++j)
				beqn.setBi(0.0, j);
			beqn.setBi(1, i);
			
			try {
				beqn.solveEqn();
			} catch (IpssNumericException e) {
				e.printStackTrace();
			}
			
			for (int j=0; j<noBus; ++j)
				this.br[j][i] = beqn.getXi(j);
		}

		/*
		 * get B matrix
		 * AX=B: 
		 * 	A--B
		 * 	X--0
		 * 	B--0
		 */
		this.b11 = new double[noBus][noBus];
		this.b11r = new double[noBus][noBus];
		ISparseEqnDouble b11eqn = refNet.formB11Matrix();
		for (int i=0; i<noBus; ++i)
			for (int j=0; j<noBus; ++j)
				this.b11[i][j] = b11eqn.getAij(i, j);
		for (int i=0; i<noBus; ++i) {
			//°´ÁÐÇóÄæ
			for (int j=0; j<noBus; ++j)
				b11eqn.setBi(0.0, j);
			b11eqn.setBi(1, i);
			
			try {
				b11eqn.solveEqn();
			} catch (IpssNumericException e) {
				e.printStackTrace();
			}
			
			for (int j=0; j<noBus; ++j)
				this.b11r[j][i] = b11eqn.getXi(j);
		}
		
		maxQGenLimit = new double[noBus];
		minQGenLimit = new double[noBus];
		EList<AclfBus> busList = refNet.getBusList();
		for (int i=0; i<noBus; ++i) {
			AclfBus bus = busList.get(i);
			if (busType[i] == RefInfo.ONLY_PV_BUS_TYPE || busType[i] == RefInfo.PV_PQ_BUS_TYPE/*PV½Úµã*/) {
				maxQGenLimit[i] = bus.getQGenLimit().getMax();
				minQGenLimit[i] = bus.getQGenLimit().getMin();
			}else {
				maxQGenLimit[i] = -1;
				minQGenLimit[i] = -1;
			}
		}
	}

	public int getNoBus() {
		return noBus;
	}

	public int getSwingNo() {
		return swingNo;
	}

	public int[] getBusType() {
		return busType;
	}

	public double[][] getB() {
		return b;
	}

	public double[][] getB1() {
		return b1;
	}

	public double[][] getBr() {
		return br;
	}

	public double[][] getB1r() {
		return b1r;
	}

	public double[][] getB11() {
		return b11;
	}

	public double[][] getB11r() {
		return b11r;
	}

	public String[] getBusCode() {
		return busCode;
	}

	public Complex[][] getY() {
		return y;
	}

	public Complex[][] getYr() {
		return yr;
	}

	public double[] getMaxQGenLimit() {
		return maxQGenLimit;
	}

	public double[] getMinQGenLimit() {
		return minQGenLimit;
	}

}

import org.apache.commons.math3.complex.Complex;
import org.interpss.CorePluginFactory;
import org.interpss.IpssCorePlugin;
import org.interpss.display.AclfOutFunc;
import org.interpss.fadapter.IpssFileAdapter;
import com.interpss.CoreObjectFactory;
import com.interpss.common.exp.InterpssException;
import com.interpss.core.aclf.AclfBus;
import com.interpss.core.aclf.AclfNetwork;
import com.interpss.core.algo.LoadflowAlgorithm;
import lfgen.algo.ILoadFlowGenerator;
import lfgen.algo.IQChecker;
import lfgen.algo.ISpecialBusChecker;
import lfgen.algo.IStatistics;
import lfgen.algo.IVgcBuilder;
import lfgen.algo.IVoltageGenerator;
import lfgen.algo.impl.AlgoObject;
import lfgen.algo.impl.LoadFlowGenerator;
import lfgen.algo.impl.QChecker;
import lfgen.algo.impl.SpecialBusChecker;
import lfgen.algo.impl.VoltageGenerator;
import lfgen.condition_gen.VThScanConditionBuilder;
import lfgen.datatype.AclfCase;
import lfgen.datatype.VoltageGenCondition;
import lfgen.datatype.RefInfo;

package lfgen.platform;






/**
* @author JeremyChenlk
* @version 2019Äê1ÔÂ24ÈÕ ÏÂÎç2:48:16
*
* Class description:
*	Ìá¹©Ò»¸öÉú³É·½·¨µÄÆ½Ì¨£¬ÒªÓÐ¸÷ÖÖÄ£¿éµÄÖ¸Õë
*
* Step1. initialization
* Description:
* 	provide V0 and theta0
* 
* Step2. generate case
* Description:
* 	generate P and Q with given V and theta
* 
* Step3. Qchecker
* Description:
* 	return power correction info
*
* Step4. connect bus power checker
* Description:
*  correct voltage to fit the power limitation of connect bus 
*
* Step6. go to step3, if fits, output this case
*/

public abstract class LoadFlowCaseGenerator implements ILoadFlowCaseGenerator, IStatistics {

	public static final String NAME = "LoadFlowCaseGenerator";
	public static final String NAME_IN_SHORT = "dLFG";
	public static final String PARA_NEEDED = "NONE";
	public static final double DEFAULT_PU_ERROR = 1e-10;
	
	/**
	 * Îª¶à´Îµ÷ÓÃÉè¼Æ£¬ÓÃÀ´Í³¼Æ×ÜµÄµ÷ÓÃ´ÎÊýºÍ×ÜµÄµü´ú´ÎÊý
	 */
	protected int callTimes = 0;
	protected int totalIterTimes = 0;
	/**
	 * ÓÃÓÚÍ³¼Æµü´úÊýÁ¿,ÔÚinitÖÐ
	 */
	protected int[] iter = null;
	
	/**
	 * µ÷ÓÃÊ±¼äÍ³¼Æ
	 */
	protected long timeUse = 0;
	protected long initTimeUse = 0;
	protected long[] iterTimeUse = null;
	
	
	/*
	 * ¸÷ÖÖÄ£¿éµÄÖ¸Õë
	 * 1. ³õÖµÉú³ÉÆ÷	VoltageGenerator
	 * 2. ºËÐÄÉú³ÉÆ÷	LoadFlowGenerator
	 * 3. ¹¦ÂÊÔ½ÏÞ¼ì²éÆ÷	QChecker
	 * 4. ÁªÂç½Úµã¹¦ÂÊ¼ì²éÆ÷	ConnectBusChecker
	 */
	protected IVoltageGenerator voltageGenerator = null;
	protected ILoadFlowGenerator loadFlowGenerator = null;
	protected IQChecker qChecker = null;
	protected ISpecialBusChecker specialBusChecker = null;

	protected IVgcBuilder voltageGenConditionBuilder = null;
