/*
* @(#)BPABusRecord.java
*
* Copyright (C) 2006-2008 www.interpss.org
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU LESSER GENERAL PUBLIC LICENSE
* as published by the Free Software Foundation; either version 2.1
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* @Author Stephen Hau, Mike Zhou
* @Version 1.0
* @Date 02/11/2008
*
* Revision History
* ================
*
*/
package org.ieee.odm.adapter.bpa.lf;
import java.util.Hashtable;
import org.ieee.odm.common.ODMException;
import org.ieee.odm.common.ODMLogger;
import org.ieee.odm.model.IODMModelParser;
import org.ieee.odm.model.aclf.AclfDataSetter;
import org.ieee.odm.model.aclf.AclfParserHelper;
import org.ieee.odm.model.aclf.BaseAclfModelParser;
import org.ieee.odm.model.base.BaseDataSetter;
import org.ieee.odm.model.base.ODMModelStringUtil;
import org.ieee.odm.schema.ActivePowerUnitType;
import org.ieee.odm.schema.AngleUnitType;
import org.ieee.odm.schema.ApparentPowerUnitType;
import org.ieee.odm.schema.BranchXmlType;
import org.ieee.odm.schema.BusXmlType;
import org.ieee.odm.schema.LFGenCodeEnumType;
import org.ieee.odm.schema.LFLoadCodeEnumType;
import org.ieee.odm.schema.LoadflowBusXmlType;
import org.ieee.odm.schema.LoadflowGenDataXmlType;
import org.ieee.odm.schema.NetworkXmlType;
import org.ieee.odm.schema.ReactivePowerUnitType;
import org.ieee.odm.schema.VoltageUnitType;
import org.ieee.odm.schema.YUnitType;
public class BPABusRecord {
private static final int swingBus=1;
private static final int pqBus=2;
private static final int pvBus=3;
private static final int pvBusNoQLimit=4;
private static final int supplementaryBusInfo=5;
/*
* BPA data format does not have bus number, only has bus name.
* Bus number is generated and a looupTable for busName -> BusId
*/
private static long busCnt = 0;
private static Hashtable<String,String> busIdLookupTable = null;
/**
* reset the bus count and lookup table
*/
public static void resetBusCnt() {
busCnt = 0;
busIdLookupTable = new Hashtable<String,String>();
}
/**
* get bus Id and add an item to the lookup table for busName -> busId
*
* @param busName
* @return
*/
private static String createBusId(String busName) {
String id = IODMModelParser.BusIdPreFix + ++busCnt;
busIdLookupTable.put(busName.trim(), id);
return id;
}
/**
* get busId from busName using the lookup table
*
* @param busName
* @return
*/
public static String getBusId(String busName) throws ODMException {
String id = busIdLookupTable.get(busName.trim());
if (id == null) {
throw new ODMException("Bus id not found, bus name: " + busName);
}
return id;
}
public void processBusData(final String str, BaseAclfModelParser<? extends NetworkXmlType> parser) throws Exception {
final double baseMVA = parser.getNet().getBasePower().getValue();
// parse the input data line
final String[] strAry = getBusDataFields(str);
//busType
int busType=0;
String stemp = strAry[0];
if(stemp.equals("B")||stemp.equals("BC")||stemp.equals("BT")||stemp.equals("BV")){
busType=pqBus;
}
else if(stemp.equals("BQ")||stemp.equals("BG")||stemp.equals("BF")){
busType=pvBus;
}
else if(stemp.equals("BE")){
busType=pvBusNoQLimit;
}
else if(stemp.equals("BS")){
busType=swingBus;
}
else if (stemp.equals("+")){
busType=supplementaryBusInfo;
}
/*
* set bus record attributes
* =========================
*/
// modification code
final String modCode=strAry[1];
//owner code
final String ownerName=strAry[2];
//Name
final String busName = strAry[3];
LoadflowBusXmlType busRec = null;
if(busType==pqBus||busType==pvBus||busType==pvBusNoQLimit||busType==swingBus){
final String busId = createBusId(busName);
ODMLogger.getLogger().fine("Bus data loaded, busName: " + busId);
try {
busRec = (LoadflowBusXmlType)parser.createBus(busId);
busRec.setName(busName);
} catch (ODMException e) {
ODMLogger.getLogger().severe(e.toString());
return;
}
busRec.setId(busId);
busRec.setName(busName);
//Add bus number, according to input sequence
busRec.setNumber(busCnt);
// TODO set bus owner
//busRec.getOwnerList().getOwner().get(0).setName(ownerName);
//basekv
double baseKv=ODMModelStringUtil.getDouble(strAry[4], 100.0);
busRec.setBaseVoltage(BaseDataSetter.createVoltageValue(baseKv, VoltageUnitType.KV));
// TODO area name??
//busRec.setAreaName(value);
//zone name
final String zoneName= strAry[5];
busRec.setZoneName(zoneName);
/*
* Parse Loadflow data
* ===================
*/
//load mw and mvar
double loadMw=ODMModelStringUtil.getDouble(strAry[6], 0.0);
double loadMvar=ODMModelStringUtil.getDouble(strAry[7], 0.0);
//Shunt mw--> G
//Shunt var B -->B
double shuntMw=ODMModelStringUtil.getDouble(strAry[8], 0.0);
double shuntVar=ODMModelStringUtil.getDouble(strAry[9], 0.0);
final double g=ODMModelStringUtil.getNumberFormat(shuntMw/baseMVA);
final double b=ODMModelStringUtil.getNumberFormat(shuntVar/baseMVA);
// set pGenMax
double pGenMax=ODMModelStringUtil.getDouble(strAry[10], 0.0);
double pGen=ODMModelStringUtil.getDouble(strAry[11], 0.0);
// qGen for PQ bus, qGenMax for PV bus
double qGenOrQGenMax=ODMModelStringUtil.getDouble(strAry[12], 0.0);
double qGenMin=ODMModelStringUtil.getDouble(strAry[13], 0.0);
//for swing bus, this value is vpu, for others it is vpuorvmax.
double vpu=ODMModelStringUtil.getDouble(strAry[14], 0.0);
if(!strAry[14].contains(".")){
vpu/=1000;
}
//for swing bus, this value is angle(degrees), for others it is vmin.
double vMinOrAngDeg=ODMModelStringUtil.getDouble(strAry[15], 0.0);
if(!strAry[15].contains(".")){
vMinOrAngDeg/=1000;
}
double varSupplied=ODMModelStringUtil.getDouble(strAry[18], 0.0);
/*
* process data and map to the ODM bus record
* ==========================================
*/
//if(loadMw != 0.0 || loadMvar != 0.0 ||
// pGen!=0.0|| qGenOrQGenMax!=0.0 ||vpu!=0.0||
// vMinOrAngDeg!=0.0||pGenMax!=0.0
// ||g!=0||b!=0) {
// set G B
if (g != 0.0 || b != 0.0) {
busRec.getShuntYData().setEquivY(BaseDataSetter.createYValue(g, b,YUnitType.PU));
}
// set load
if (loadMw != 0.0 || loadMvar != 0.0) {
AclfDataSetter.setLoadData(busRec,
LFLoadCodeEnumType.CONST_P, loadMw,
loadMvar, ApparentPowerUnitType.MVA);
}
LoadflowGenDataXmlType defaultGen = AclfParserHelper.getDefaultGen(busRec.getGenData());
if(busType==swingBus){
// set bus voltage
busRec.setVoltage(BaseDataSetter.createVoltageValue(vpu, VoltageUnitType.PU));
// set bus angle
busRec.setAngle(BaseDataSetter.createAngleValue(vMinOrAngDeg, AngleUnitType.DEG));
//set gen data
AclfDataSetter.setGenData(busRec,
LFGenCodeEnumType.SWING,
vpu, VoltageUnitType.PU, pGen,0, ApparentPowerUnitType.MVA);
// set Q limit
if(qGenOrQGenMax!=0.0||qGenMin!=0.0){
defaultGen.setQLimit(BaseDataSetter.createReactivePowerLimit(
qGenOrQGenMax, qGenMin, ReactivePowerUnitType.MVAR));
}
// set P limit
if(pGenMax!=0.0){
defaultGen.setPLimit(BaseDataSetter.createActivePowerLimit(
pGenMax, 0, ActivePowerUnitType.MW));
}
}
else if(busType==pqBus){
AclfDataSetter.setGenData(busRec, LFGenCodeEnumType.NONE_GEN);
if(pGen!=0.0||qGenOrQGenMax!=0.0){
busRec.getGenData().setCode(LFGenCodeEnumType.PQ);
defaultGen.setPower(BaseDataSetter.createPowerValue(
pGen, qGenOrQGenMax, ApparentPowerUnitType.MVA));
// for a PQ Bus, it is not proper to set the Vlimit;
// // set V limit
// if(vpu!=0 ||vMinOrAngDeg!=0){
// busRec.getGenData().getEquivGen()
// .setVoltageLimit(BaseDataSetter.createVoltageLimit(
// vpu, vMinOrAngDeg, VoltageUnitType.PU));
// }
if(pGen!=0.0&&vpu!=0){
ODMLogger.getLogger().info("This bus seems to be a GenPV bus: "+ busId+","+busName
+" ,please check! ");
}
}
}
else if(busType==pvBus || busType==pvBusNoQLimit){
// set bus voltage
busRec.setVoltage(BaseDataSetter.createVoltageValue(vpu, VoltageUnitType.PU));
// set gen data
AclfDataSetter.setGenData(busRec,
LFGenCodeEnumType.PV,
vpu, VoltageUnitType.PU,
pGen, 0.0, ApparentPowerUnitType.MVA);
// set Q limit
if(qGenOrQGenMax!=0.0||qGenMin!=0.0){
defaultGen.setQLimit(BaseDataSetter.createReactivePowerLimit(
qGenOrQGenMax, qGenMin, ReactivePowerUnitType.MVAR));
// for "BE" type the limit if disabled
if (busType==pvBusNoQLimit)
defaultGen.getQLimit().setActive(false);
//TODO BPA automatically balance the shuntVar at BE Type Bus,
// considering Ipss does not support such function, set it to zero here.
// AclfDataSetter.setBusShuntVar(busRec, 0, YUnitType.PU);
}
// set P limit
if(pGenMax!=0.0){
defaultGen.setPLimit(BaseDataSetter.createActivePowerLimit(
pGenMax, 0, ActivePowerUnitType.MW));
}
}//end of PV bus data parsing
}//end of load flow data mapping
// }//end of normal type bus data processing.
/*a bus recored starting with"+", is to supplement
info(e.g.,ZIP type load and generation) to an existing bus record with the same busId
*/
if(busType==supplementaryBusInfo){
BusXmlType Bus=parser.getBus(getBusId(busName));
final String loadType=strAry[5];
//loadType: *I or 01 for constI, and *P or 02 for constP
final double p=ODMModelStringUtil.getDouble(strAry[6], 0.0);
final double q=ODMModelStringUtil.getDouble(strAry[7], 0.0);
//TODO how to set constI type load
if(!strAry[9].equals("")||!strAry[8].equals("")){
final double ShuntG=ODMModelStringUtil.getDouble(strAry[8], 0.0);
final double ShuntB=ODMModelStringUtil.getDouble(strAry[9], 0.0);
//System.out.println("Shunt G +B="+ShuntG+","+ShuntB);
double re=ODMModelStringUtil.getNumberFormat(ShuntG/baseMVA); // x(pu)=Var/baseMVA
double im=ODMModelStringUtil.getNumberFormat(ShuntB/baseMVA);
if(re!=0.0||im!=0.0){
AclfDataSetter.addBusShuntY((LoadflowBusXmlType)Bus, re, im, YUnitType.PU);
}
//System.out.println("Im="+im+",Shunt B="+Bus.getShuntY().getIm());
}
}
/*for BG and BX, controlled bus name and voltage
desired bus voltage is specified in strAry[14], equals to vpu
*/
final String controlledBus= strAry[16];
double controlledBusRatedVol=ODMModelStringUtil.getDouble(strAry[17], 0.0);
LoadflowGenDataXmlType defaultGen = AclfParserHelper.getDefaultGen(busRec.getGenData());
if(strAry[0].equals("BG")||strAry[0].equals("BX")){
if(!controlledBus.equals("")) {
defaultGen.getRemoteVoltageControlBus().setIdRef(controlledBus);
defaultGen.setDesiredVoltage(BaseDataSetter.createVoltageValue(
controlledBusRatedVol, VoltageUnitType.PU));
}
}
}
private static String[] getBusDataFields(final String str) throws Exception {
final String[] strAry = new String[19];
/* sample data
B XIANLS= 500.XX305.3 -215.
*/
//Columns 1- 2 Bus type
strAry[0] = ODMModelStringUtil.getStringReturnEmptyString(str,1, 2);
//Columns 3 code for modification
strAry[1] = ODMModelStringUtil.getStringReturnEmptyString(str,3, 3).trim();
//Columns 3-5 owner code
strAry[2] = ODMModelStringUtil.getStringReturnEmptyString(str,4, 6).trim();
//to process the Chinese characters first, if any.
String tem=ODMModelStringUtil.getStringReturnEmptyString(str,7, 14).trim();
int chineseCharNum=ODMModelStringUtil.getChineseCharNum(tem);
//Columns 6-13 busName
strAry[3] = ODMModelStringUtil.getStringReturnEmptyString(str,7, 14-chineseCharNum).trim();
String str2=chineseCharNum==0?str:ODMModelStringUtil.replaceChineseChar(str);
//14-17 rated voltage
strAry[4] = ODMModelStringUtil.getStringReturnEmptyString(str2,15, 18).trim();
//Columns 18-19 zone name for Bus card, load type for complementary Bus card.
strAry[5] = ODMModelStringUtil.getStringReturnEmptyString(str2,19, 20).trim();
//Columns 21-25 Load MW [F] *
//Columns 26-30 Load MVAR [F] *
strAry[6] = ODMModelStringUtil.getStringReturnEmptyString(str2,21, 25).trim();
strAry[7] = ODMModelStringUtil.getStringReturnEmptyString(str2,26, 30).trim();
//Columns 31-34 shunt MW [F] *
//Columns 35-38 shunt MVAR [F] *
strAry[8] = ODMModelStringUtil.getStringReturnEmptyString(str2,31, 34).trim();
strAry[9] = ODMModelStringUtil.getStringReturnEmptyString(str2,35, 38).trim();
// Columns 38-41 pmax
// Columns 42-46 pgen
strAry[10] = ODMModelStringUtil.getStringReturnEmptyString(str2,39, 42).trim();
strAry[11] = ODMModelStringUtil.getStringReturnEmptyString(str2,43, 47).trim();
//Qmax Qmin
strAry[12]= ODMModelStringUtil.getStringReturnEmptyString(str2,48, 52).trim();
strAry[13]= ODMModelStringUtil.getStringReturnEmptyString(str2,53, 57).trim();
//scheduled V or Vmax, Vmin
strAry[14]= ODMModelStringUtil.getStringReturnEmptyString(str2,58, 61).trim();
strAry[15]=ODMModelStringUtil.getStringReturnEmptyString(str2,62, 65).trim();
//remoted busName, rated voltage
strAry[16]= ODMModelStringUtil.getStringReturnEmptyString(str2,66, 73).trim();
strAry[17]= ODMModelStringUtil.getStringReturnEmptyString(str2,74, 77).trim();
// used in remoted bus control, var fraction
strAry[18]= ODMModelStringUtil.getStringReturnEmptyString(str2,78, 80).trim();
return strAry;
}
}