00001
00018
00019 #include <stdlib.h>
00020 #include <stdio.h>
00021 #include <errno.h>
00022 #include <math.h>
00023
00024 #include "substation.h"
00025 #include "timestamp.h"
00026
00028
00030
00031 CLASS* substation::oclass = NULL;
00032 CLASS* substation::pclass = NULL;
00033
00034
00035 substation::substation(MODULE *mod) : node(mod)
00036 {
00037
00038 if (oclass==NULL)
00039 {
00040
00041 pclass = node::oclass;
00042
00043
00044 oclass = gl_register_class(mod,"substation",sizeof(substation),PC_PRETOPDOWN|PC_BOTTOMUP|PC_POSTTOPDOWN|PC_UNSAFE_OVERRIDE_OMIT|PC_AUTOLOCK);
00045 if (oclass==NULL)
00046 throw "unable to register class substation";
00047 else
00048 oclass->trl = TRL_PROTOTYPE;
00049
00050
00051 if (gl_publish_variable(oclass,
00052 PT_INHERIT, "node",
00053
00054 PT_complex, "zero_sequence_voltage[V]", PADDR(seq_mat[0]), PT_DESCRIPTION, "The zero sequence representation of the voltage for the substation object.",
00055 PT_complex, "positive_sequence_voltage[V]", PADDR(seq_mat[1]), PT_DESCRIPTION, "The positive sequence representation of the voltage for the substation object.",
00056 PT_complex, "negative_sequence_voltage[V]", PADDR(seq_mat[2]), PT_DESCRIPTION, "The negative sequence representation of the voltage for the substation object.",
00057 PT_double, "base_power[VA]", PADDR(base_power), PT_DESCRIPTION, "The 3 phase VA power rating of the substation.",
00058 PT_double, "power_convergence_value[VA]", PADDR(power_convergence_value), PT_DESCRIPTION, "Default convergence criterion before power is posted to pw_load objects if connected, otherwise ignored",
00059 PT_enumeration, "reference_phase", PADDR(reference_phase), PT_DESCRIPTION, "The reference phase for the positive sequence voltage.",
00060 PT_KEYWORD, "PHASE_A", (enumeration)R_PHASE_A,
00061 PT_KEYWORD, "PHASE_B", (enumeration)R_PHASE_B,
00062 PT_KEYWORD, "PHASE_C", (enumeration)R_PHASE_C,
00063 PT_complex, "transmission_level_constant_power_load[VA]", PADDR(average_transmission_power_load), PT_DESCRIPTION, "the average constant power load to be posted directly to the pw_load object.",
00064 PT_complex, "transmission_level_constant_current_load[A]", PADDR(average_transmission_current_load), PT_DESCRIPTION, "the average constant current load at nominal voltage to be posted directly to the pw_load object.",
00065 PT_complex, "transmission_level_constant_impedance_load[Ohm]", PADDR(average_transmission_impedance_load), PT_DESCRIPTION, "the average constant impedance load at nominal voltage to be posted directly to the pw_load object.",
00066 PT_complex, "distribution_load[VA]", PADDR(distribution_load), PT_DESCRIPTION, "The total load of all three phases at the substation object.",
00067 PT_complex, "distribution_power_A[VA]", PADDR(distribution_power_A),
00068 PT_complex, "distribution_power_B[VA]", PADDR(distribution_power_B),
00069 PT_complex, "distribution_power_C[VA]", PADDR(distribution_power_C),
00070 PT_complex, "distribution_voltage_A[V]", PADDR(voltageA),
00071 PT_complex, "distribution_voltage_B[V]", PADDR(voltageB),
00072 PT_complex, "distribution_voltage_C[V]", PADDR(voltageC),
00073 PT_complex, "distribution_voltage_AB[V]", PADDR(voltageAB),
00074 PT_complex, "distribution_voltage_BC[V]", PADDR(voltageBC),
00075 PT_complex, "distribution_voltage_CA[V]", PADDR(voltageCA),
00076 PT_complex, "distribution_current_A[A]", PADDR(current_inj[0]),
00077 PT_complex, "distribution_current_B[A]", PADDR(current_inj[1]),
00078 PT_complex, "distribution_current_C[A]", PADDR(current_inj[2]),
00079 PT_double, "distribution_real_energy[Wh]", PADDR(distribution_real_energy),
00080
00081 NULL)<1) GL_THROW("unable to publish properties in %s",__FILE__);
00082
00083 if (gl_publish_function(oclass, "interupdate_pwr_object", (FUNCTIONADDR)interupdate_substation)==NULL)
00084 GL_THROW("Unable to publish substation deltamode function");
00085 if (gl_publish_function(oclass, "pwr_object_swing_swapper", (FUNCTIONADDR)swap_node_swing_status)==NULL)
00086 GL_THROW("Unable to publish substation swing-swapping function");
00087 if (gl_publish_function(oclass, "pwr_current_injection_update_map", (FUNCTIONADDR)node_map_current_update_function)==NULL)
00088 GL_THROW("Unable to publish substation current injection update mapping function");
00089 if (gl_publish_function(oclass, "pwr_object_reset_disabled_status", (FUNCTIONADDR)node_reset_disabled_status) == NULL)
00090 GL_THROW("Unable to publish substation island-status-reset function");
00091 }
00092 }
00093
00094 int substation::isa(char *classname)
00095 {
00096 return strcmp(classname,"substation")==0 || node::isa(classname);
00097 }
00098
00099
00100 int substation::create()
00101 {
00102 int result = node::create();
00103 reference_phase = R_PHASE_A;
00104 has_parent = 0;
00105 seq_mat[0] = 0;
00106 seq_mat[1] = 0;
00107 seq_mat[2] = 0;
00108 volt_A = 0;
00109 volt_B = 0;
00110 volt_C = 0;
00111 base_power = 0;
00112 power_convergence_value = 0.0;
00113 pTransNominalVoltage = NULL;
00114 return result;
00115 }
00116
00117 void substation::fetch_complex(complex **prop, char *name, OBJECT *parent){
00118 OBJECT *hdr = OBJECTHDR(this);
00119 *prop = gl_get_complex_by_name(parent, name);
00120 if(*prop == NULL){
00121 char tname[32];
00122 char *namestr = (hdr->name ? hdr->name : tname);
00123 char msg[256];
00124 sprintf(tname, "substation:%i", hdr->id);
00125 if(*name == NULL)
00126 sprintf(msg, "%s: substation unable to find property: name is NULL", namestr);
00127 else
00128 sprintf(msg, "%s: substation unable to find %s", namestr, name);
00129 throw(msg);
00130 }
00131 }
00132
00133 void substation::fetch_double(double **prop, char *name, OBJECT *parent){
00134 OBJECT *hdr = OBJECTHDR(this);
00135 *prop = gl_get_double_by_name(parent, name);
00136 if(*prop == NULL){
00137 char tname[32];
00138 char *namestr = (hdr->name ? hdr->name : tname);
00139 char msg[256];
00140 sprintf(tname, "substation:%i", hdr->id);
00141 if(*name == NULL)
00142 sprintf(msg, "%s: substation unable to find property: name is NULL", namestr);
00143 else
00144 sprintf(msg, "%s: substation unable to find %s", namestr, name);
00145 throw(msg);
00146 }
00147 }
00148
00149
00150 int substation::init(OBJECT *parent)
00151 {
00152 OBJECT *hdr = OBJECTHDR(this);
00153 int i,n;
00154
00155
00156 if(base_power <= 0){
00157 gl_warning("substation:%i is using the default base power of 100 VA. This could cause instability on your system.", hdr->id);
00158 base_power = 100;
00159 }
00160
00161
00162 if (power_convergence_value<=0.0)
00163 {
00164 gl_warning("power_convergence_value not set - defaulting to 0.01 base_power");
00165
00166
00167
00168
00169
00170
00171 power_convergence_value = 0.01*base_power;
00172 }
00173
00174
00175 if (parent != NULL)
00176 {
00177 if (gl_object_isa(parent,"pw_load","network"))
00178 {
00179
00180 if((parent->flags & OF_INIT) != OF_INIT){
00181 char objname[256];
00182 gl_verbose("substation::init(): deferring initialization on %s", gl_name(parent, objname, 255));
00183
00184 return 2;
00185 }
00186
00187
00188 fetch_complex(&pPositiveSequenceVoltage,"load_voltage",parent);
00189 fetch_complex(&pConstantPowerLoad,"load_power",parent);
00190 fetch_complex(&pConstantCurrentLoad,"load_current",parent);
00191 fetch_complex(&pConstantImpedanceLoad,"load_impedance",parent);
00192 fetch_double(&pTransNominalVoltage,"bus_nom_volt",parent);
00193
00194
00195 if (fabs(*pTransNominalVoltage-nominal_voltage)>0.001)
00196 {
00197 gl_error("Nominal voltages of tranmission node (%.1f V) and substation (%.1f) do not match!",*pTransNominalVoltage,nominal_voltage);
00198
00199
00200
00201
00202
00203
00204
00205 return 0;
00206 }
00207
00208
00209 if (bustype != SWING)
00210 {
00211 gl_warning("substation attached to pw_load and not a SWING bus - forcing to SWING");
00212
00213
00214
00215
00216
00217 bustype = SWING;
00218 }
00219
00220
00221 has_parent = 1;
00222 }
00223 else
00224 {
00225 has_parent = 2;
00226 }
00227 }
00228 else
00229 {
00230
00231 if ((seq_mat[0] != 0.0) || (seq_mat[1] != 0.0) || (seq_mat[2] != 0.0))
00232 {
00233
00234 if (bustype != SWING)
00235 {
00236 gl_warning("substation is not a SWING bus, so answers may be unexpected");
00237
00238
00239
00240
00241
00242 }
00243
00244
00245 has_parent = 0;
00246 }
00247 else
00248 {
00249 has_parent = 2;
00250
00251
00252 gl_warning("substation:%s is set up as a normal node, no sequence values will be calculated",hdr->name);
00253
00254
00255
00256
00257 }
00258 }
00259
00260
00261 if (has_parent != 2)
00262 {
00263
00264
00265 if (!has_phase(PHASE_A|PHASE_B|PHASE_C))
00266 {
00267 gl_error("substation needs to have all three phases!");
00268
00269
00270
00271
00272
00273 return 0;
00274 }
00275 }
00276
00277 if(reference_phase == R_PHASE_A){
00278 reference_number.SetPolar(1,0);
00279 } else if(reference_phase == R_PHASE_B){
00280 reference_number.SetPolar(1,2*PI/3);
00281 } else if(reference_phase == R_PHASE_C){
00282 reference_number.SetPolar(1,-2*PI/3);
00283 }
00284
00285
00286 for(i=0; i<3; i++){
00287 for(n=0; n<3; n++){
00288 if((i==1 && n==1) || (i==2 && n==2)){
00289 transformation_matrix[i][n].SetPolar(1,-2*PI/3);
00290 } else if((i==2 && n==1) || (i==1 && n==2)){
00291 transformation_matrix[i][n].SetPolar(1,2*PI/3);
00292 } else {
00293 transformation_matrix[i][n].SetPolar(1,0);
00294 }
00295 }
00296 }
00297
00298 return node::init(parent);
00299 }
00300
00301
00302 TIMESTAMP substation::presync(TIMESTAMP t0, TIMESTAMP t1)
00303 {
00304 double dt = 0;
00305 double total_load;
00306
00307 if(t1 != t0 && t0 != 0 && (last_power_A.Re() != 0 && last_power_B.Re() != 0 && last_power_C.Re() != 0)){
00308 total_load = last_power_A.Re() + last_power_B.Re() + last_power_C.Re();
00309 dt = ((double)(t1 - t0))/(3600 * TS_SECOND);
00310 distribution_real_energy += total_load*dt;
00311 }
00312 return node::presync(t1);
00313 }
00314 TIMESTAMP substation::sync(TIMESTAMP t0, TIMESTAMP t1)
00315 {
00316 OBJECT *obj = OBJECTHDR(this);
00317 TIMESTAMP t2;
00318 double dist_power_A_diff = 0;
00319 double dist_power_B_diff = 0;
00320 double dist_power_C_diff = 0;
00321
00322 if((solver_method == SM_NR || solver_method == SM_FBS)){
00323 if(has_parent == 1){
00324 seq_mat[1] = *pPositiveSequenceVoltage;
00325 seq_mat[0] = 0.0;
00326 seq_mat[2] = 0.0;
00327 }
00328
00329
00330 if (seq_mat[0].Mag() != 0 || seq_mat[1].Mag() != 0 || seq_mat[2].Mag() != 0)
00331 {
00332 voltageA = (seq_mat[0] + seq_mat[1] + seq_mat[2]) * reference_number;
00333 voltageB = (seq_mat[0] + transformation_matrix[1][1] * seq_mat[1] + transformation_matrix[1][2] * seq_mat[2]) * reference_number;
00334 voltageC = (seq_mat[0] + transformation_matrix[2][1] * seq_mat[1] + transformation_matrix[2][2] * seq_mat[2]) * reference_number;
00335 }
00336 }
00337
00338 t2 = node::sync(t1);
00339
00340
00341 if(solver_method == SM_NR){
00342 int result = node::NR_current_update(false);
00343
00344 if(result != 1){
00345 GL_THROW("Attempt to update current/power on substation:%s failed!",obj->name);
00346
00347 }
00348 }
00349
00350 if((solver_method == SM_NR && NR_admit_change == false) || solver_method == SM_FBS){
00351 distribution_power_A = voltageA * (~current_inj[0]);
00352 distribution_power_B = voltageB * (~current_inj[1]);
00353 distribution_power_C = voltageC * (~current_inj[2]);
00354 distribution_load = distribution_power_A + distribution_power_B + distribution_power_C;
00355 dist_power_A_diff = distribution_power_A.Mag() - last_power_A.Mag();
00356 dist_power_B_diff = distribution_power_B.Mag() - last_power_B.Mag();
00357 dist_power_C_diff = distribution_power_C.Mag() - last_power_C.Mag();
00358 last_power_A = distribution_power_A;
00359 last_power_B = distribution_power_B;
00360 last_power_C = distribution_power_C;
00361
00362
00363 if (has_parent == 1)
00364 {
00365 if((fabs(dist_power_A_diff) + fabs(dist_power_B_diff) + fabs(dist_power_C_diff)) <= power_convergence_value)
00366 {
00367
00368 *pConstantCurrentLoad = ((~average_transmission_current_load) * (*pTransNominalVoltage)) / 1000000;
00369 if(average_transmission_impedance_load.Mag() > 0){
00370 *pConstantImpedanceLoad = (complex((*pTransNominalVoltage * (*pTransNominalVoltage))) / (~average_transmission_impedance_load) / 1000000);
00371 } else {
00372 *pConstantImpedanceLoad = 0;
00373 }
00374 *pConstantPowerLoad = (average_transmission_power_load + (distribution_power_A + distribution_power_B + distribution_power_C)) / 1000000;
00375 }
00376 }
00377 }
00378 return t2;
00379 }
00380
00381 SIMULATIONMODE substation::inter_deltaupdate_substation(unsigned int64 delta_time, unsigned long dt, unsigned int iteration_count_val,bool interupdate_pos)
00382 {
00383 double total_load;
00384 OBJECT *obj = OBJECTHDR(this);
00385 double dist_power_A_diff = 0;
00386 double dist_power_B_diff = 0;
00387 double dist_power_C_diff = 0;
00388 if (interupdate_pos == false)
00389 {
00390
00391
00392 if(iteration_count_val == 0){
00393 total_load = last_power_A.Re() + last_power_B.Re() + last_power_C.Re();
00394 distribution_real_energy += total_load*((double)dt/(3600.0*DT_SECOND));
00395 }
00396 NR_node_presync_fxn(0);
00397
00398
00399
00400 if((solver_method == SM_NR || solver_method == SM_FBS)){
00401 if(has_parent == 1){
00402 seq_mat[1] = *pPositiveSequenceVoltage;
00403 seq_mat[0] = 0.0;
00404 seq_mat[2] = 0.0;
00405 }
00406
00407
00408 if (seq_mat[0].Mag() != 0 || seq_mat[1].Mag() != 0 || seq_mat[2].Mag() != 0)
00409 {
00410 voltageA = (seq_mat[0] + seq_mat[1] + seq_mat[2]) * reference_number;
00411 voltageB = (seq_mat[0] + transformation_matrix[1][1] * seq_mat[1] + transformation_matrix[1][2] * seq_mat[2]) * reference_number;
00412 voltageC = (seq_mat[0] + transformation_matrix[2][1] * seq_mat[1] + transformation_matrix[2][2] * seq_mat[2]) * reference_number;
00413 }
00414 }
00415 NR_node_sync_fxn(obj);
00416 return SM_DELTA;
00417 }
00418 else
00419 {
00420
00421 BOTH_node_postsync_fxn(obj);
00422
00423 if((solver_method == SM_NR && NR_admit_change == false) || solver_method == SM_FBS){
00424 distribution_power_A = voltageA * (~current_inj[0]);
00425 distribution_power_B = voltageB * (~current_inj[1]);
00426 distribution_power_C = voltageC * (~current_inj[2]);
00427 distribution_load = distribution_power_A + distribution_power_B + distribution_power_C;
00428 dist_power_A_diff = distribution_power_A.Mag() - last_power_A.Mag();
00429 dist_power_B_diff = distribution_power_B.Mag() - last_power_B.Mag();
00430 dist_power_C_diff = distribution_power_C.Mag() - last_power_C.Mag();
00431 last_power_A = distribution_power_A;
00432 last_power_B = distribution_power_B;
00433 last_power_C = distribution_power_C;
00434
00435
00436 if (has_parent == 1)
00437 {
00438 if((fabs(dist_power_A_diff) + fabs(dist_power_B_diff) + fabs(dist_power_C_diff)) <= power_convergence_value)
00439 {
00440
00441 *pConstantCurrentLoad = ((~average_transmission_current_load) * (*pTransNominalVoltage)) / 1000000;
00442 if(average_transmission_impedance_load.Mag() > 0){
00443 *pConstantImpedanceLoad = (complex((*pTransNominalVoltage * (*pTransNominalVoltage))) / (~average_transmission_impedance_load) / 1000000);
00444 } else {
00445 *pConstantImpedanceLoad = 0;
00446 }
00447 *pConstantPowerLoad = (average_transmission_power_load + (distribution_power_A + distribution_power_B + distribution_power_C)) / 1000000;
00448 }
00449 }
00450 }
00451 return SM_EVENT;
00452 }
00453 }
00455
00457
00458 EXPORT int isa_substation(OBJECT *obj, char *classname)
00459 {
00460 return OBJECTDATA(obj,substation)->isa(classname);
00461 }
00462
00463 EXPORT int create_substation(OBJECT **obj, OBJECT *parent)
00464 {
00465 try
00466 {
00467 *obj = gl_create_object(substation::oclass);
00468 if (*obj!=NULL)
00469 {
00470 substation *my = OBJECTDATA(*obj,substation);
00471 gl_set_parent(*obj,parent);
00472 return my->create();
00473 }
00474 else
00475 return 0;
00476 }
00477 CREATE_CATCHALL(substation);
00478 }
00479
00480 EXPORT int init_substation(OBJECT *obj)
00481 {
00482 try {
00483 substation *my = OBJECTDATA(obj,substation);
00484 return my->init(obj->parent);
00485 }
00486 INIT_CATCHALL(substation);
00487 }
00488
00489 EXPORT TIMESTAMP sync_substation(OBJECT *obj, TIMESTAMP t0, PASSCONFIG pass)
00490 {
00491 try {
00492 substation *pObj = OBJECTDATA(obj,substation);
00493 TIMESTAMP t1;
00494 switch (pass) {
00495 case PC_PRETOPDOWN:
00496 return pObj->presync(obj->clock,t0);
00497 case PC_BOTTOMUP:
00498 return pObj->sync(obj->clock,t0);
00499 case PC_POSTTOPDOWN:
00500 t1 = pObj->postsync(t0);
00501 obj->clock = t0;
00502 return t1;
00503 default:
00504 throw "invalid pass request";
00505 }
00506 throw "invalid pass request";
00507 }
00508 SYNC_CATCHALL(substation);
00509 }
00510
00511 EXPORT int notify_substation(OBJECT *obj, int update_mode, PROPERTY *prop, char *value){
00512 substation *n = OBJECTDATA(obj, substation);
00513 int rv = 1;
00514
00515 rv = n->notify(update_mode, prop, value);
00516
00517 return rv;
00518 }
00519
00520
00521 EXPORT SIMULATIONMODE interupdate_substation(OBJECT *obj, unsigned int64 delta_time, unsigned long dt, unsigned int iteration_count_val, bool interupdate_pos)
00522 {
00523 substation *my = OBJECTDATA(obj,substation);
00524 SIMULATIONMODE status = SM_ERROR;
00525 try
00526 {
00527 status = my->inter_deltaupdate_substation(delta_time,dt,iteration_count_val,interupdate_pos);
00528 return status;
00529 }
00530 catch (char *msg)
00531 {
00532 gl_error("interupdate_substation(obj=%d;%s): %s", obj->id, obj->name?obj->name:"unnamed", msg);
00533 return status;
00534 }
00535 }
00536