00001
00010 #include <stdlib.h>
00011 #include <stdio.h>
00012 #include <errno.h>
00013 #include <math.h>
00014
00015 #include "generators.h"
00016
00017 #include "energy_storage.h"
00018 #include "battery.h"
00019 #include "gridlabd.h"
00020
00021
00022
00023
00024 #define HOUR 3600 * TS_SECOND
00025
00026 CLASS *battery::oclass = NULL;
00027 battery *battery::defaults = NULL;
00028
00029 static PASSCONFIG passconfig = PC_BOTTOMUP|PC_POSTTOPDOWN;
00030 static PASSCONFIG clockpass = PC_BOTTOMUP;
00031
00032
00033 battery::battery(MODULE *module)
00034 {
00035 if (oclass==NULL)
00036 {
00037 oclass = gl_register_class(module,"battery",sizeof(battery),PC_PRETOPDOWN|PC_BOTTOMUP|PC_POSTTOPDOWN);
00038 if (oclass==NULL)
00039 GL_THROW("unable to register object class implemented by %s", __FILE__);
00040
00041 if (gl_publish_variable(oclass,
00042 PT_enumeration,"generator_mode",PADDR(gen_mode_v),
00043 PT_KEYWORD,"UNKNOWN",GM_UNKNOWN,
00044 PT_KEYWORD,"CONSTANT_V",GM_CONSTANT_V,
00045 PT_KEYWORD,"CONSTANT_PQ",GM_CONSTANT_PQ,
00046 PT_KEYWORD,"CONSTANT_PF",GM_CONSTANT_PF,
00047 PT_KEYWORD,"SUPPLY_DRIVEN",GM_SUPPLY_DRIVEN,
00048 PT_KEYWORD,"POWER_DRIVEN",GM_POWER_DRIVEN,
00049 PT_KEYWORD,"VOLTAGE_CONTROLLED",GM_VOLTAGE_CONTROLLED,
00050 PT_KEYWORD,"POWER_VOLTAGE_HYBRID",GM_POWER_VOLTAGE_HYBRID,
00051
00052 PT_enumeration,"additional_controls",PADDR(additional_controls),
00053 PT_KEYWORD,"NONE",AC_NONE,
00054 PT_KEYWORD,"LINEAR_TEMPERATURE",AC_LINEAR_TEMPERATURE,
00055
00056 PT_enumeration,"generator_status",PADDR(gen_status_v),
00057 PT_KEYWORD,"OFFLINE",OFFLINE,
00058 PT_KEYWORD,"ONLINE",ONLINE,
00059
00060 PT_enumeration,"rfb_size", PADDR(rfb_size_v),
00061 PT_KEYWORD, "HOUSEHOLD", HOUSEHOLD,
00062 PT_KEYWORD, "SMALL", SMALL,
00063 PT_KEYWORD, "MED_COMMERCIAL", MED_COMMERCIAL,
00064 PT_KEYWORD, "MED_HIGH_ENERGY", MED_HIGH_ENERGY,
00065 PT_KEYWORD, "LARGE", LARGE,
00066
00067 PT_enumeration,"power_type",PADDR(power_type_v),
00068 PT_KEYWORD,"AC",AC,
00069 PT_KEYWORD,"DC",DC,
00070
00071 PT_enumeration,"battery_state",PADDR(battery_state),
00072 PT_KEYWORD,"CHARGING",BS_CHARGING,
00073 PT_KEYWORD,"DISCHARGING",BS_DISCHARGING,
00074 PT_KEYWORD,"WAITING",BS_WAITING,
00075 PT_KEYWORD,"FULL",BS_FULL,
00076 PT_KEYWORD,"EMPTY",BS_EMPTY,
00077 PT_KEYWORD,"CONFLICTED",BS_CONFLICTED,
00078
00079 PT_double, "number_battery_state_changes", PADDR(no_of_cycles),
00080 PT_double, "monitored_power[W]", PADDR(check_power),
00081 PT_double, "power_set_high[W]", PADDR(power_set_high),
00082 PT_double, "power_set_low[W]", PADDR(power_set_low),
00083 PT_double, "power_set_high_highT[W]", PADDR(power_set_high_highT),
00084 PT_double, "power_set_low_highT[W]", PADDR(power_set_low_highT),
00085 PT_double, "check_power_low[W]", PADDR(check_power_low),
00086 PT_double, "check_power_high[W]", PADDR(check_power_high),
00087 PT_double, "voltage_set_high[V]", PADDR(voltage_set_high),
00088 PT_double, "voltage_set_low[V]", PADDR(voltage_set_low),
00089 PT_double, "deadband[V]", PADDR(deadband),
00090 PT_double, "Rinternal[Ohm]", PADDR(Rinternal),
00091 PT_double, "V_Max[V]", PADDR(V_Max),
00092 PT_complex, "I_Max[A]", PADDR(I_Max),
00093 PT_double, "E_Max[Wh]", PADDR(E_Max),
00094 PT_double, "P_Max[W]", PADDR(Max_P),
00095 PT_double, "Energy[Wh]",PADDR(Energy),
00096 PT_double, "efficiency[unit]", PADDR(efficiency),
00097 PT_double, "base_efficiency[unit]", PADDR(base_efficiency),
00098 PT_double, "parasitic_power_draw[W]", PADDR(parasitic_power_draw),
00099 PT_double, "sensitivity", PADDR(sensitivity),
00100 PT_double, "high_temperature", PADDR(high_temperature),
00101 PT_double, "midpoint_temperature", PADDR(midpoint_temperature),
00102 PT_double, "low_temperature", PADDR(low_temperature),
00103
00104
00105
00106 PT_double, "Rated_kVA[kVA]", PADDR(Rated_kVA),
00107
00108 PT_complex, "V_Out[V]", PADDR(V_Out),
00109 PT_complex, "I_Out[A]", PADDR(I_Out),
00110 PT_complex, "VA_Out[VA]", PADDR(VA_Out),
00111 PT_complex, "V_In[V]", PADDR(V_In),
00112 PT_complex, "I_In[A]", PADDR(I_In),
00113 PT_complex, "V_Internal[V]", PADDR(V_Internal),
00114 PT_complex, "I_Internal[A]",PADDR(I_Internal),
00115 PT_complex, "I_Prev[A]", PADDR(I_Prev),
00116
00117
00118
00119 PT_set, "phases", PADDR(phases),
00120
00121 PT_KEYWORD, "A",(set)PHASE_A,
00122 PT_KEYWORD, "B",(set)PHASE_B,
00123 PT_KEYWORD, "C",(set)PHASE_C,
00124 PT_KEYWORD, "N",(set)PHASE_N,
00125 PT_KEYWORD, "S",(set)PHASE_S,
00126 NULL)<1) GL_THROW("unable to publish properties in %s",__FILE__);
00127 defaults = this;
00128 memset(this,0,sizeof(battery));
00129
00130 }
00131 }
00132
00133
00134 int battery::create(void)
00135 {
00136 memcpy(this,defaults,sizeof(*this));
00137
00138 gen_status_v = ONLINE;
00139
00140
00141 Rinternal = 10;
00142 V_Max = 0;
00143 I_Max = 0;
00144 E_Max = 0;
00145 Energy = -1;
00146 recalculate = true;
00147 margin = 1000;
00148 number_of_phases_out = 0;
00149 no_of_cycles = 0;
00150 deadband = 0;
00151 parasitic_power_draw = 0;
00152 additional_controls = AC_NONE;
00153 midpoint_temperature = 50;
00154 low_temperature = 0;
00155 high_temperature = 100;
00156 sensitivity = 0.5;
00157
00158 Max_P = 0;
00159 Min_P = 0;
00160
00161
00162
00163 Rated_kVA = 1;
00164
00165
00166 efficiency = 0;
00167 base_efficiency = 0;
00168 Iteration_Toggle = false;
00169
00170 E_Next = 0;
00171 connected = true;
00172 complex VA_Internal;
00173
00174
00175 return 1;
00176 }
00177
00178
00179 int battery::init(OBJECT *parent)
00180 {
00181 OBJECT *obj = OBJECTHDR(this);
00182
00183 static complex default_line123_voltage[1], default_line1_current[1];
00184 int i;
00185
00186
00187 if (parent!=NULL && gl_object_isa(parent,"meter"))
00188 {
00189
00190 struct {
00191 complex **var;
00192 char *varname;
00193 }
00194 map[] = {
00195
00196 {&pCircuit_V, "voltage_A"},
00197 {&pLine_I, "current_A"},
00198 {&pPower, "measured_power"},
00199 };
00201
00202 for (i=0; i<sizeof(map)/sizeof(map[0]); i++)
00203 *(map[i].var) = get_complex(parent,map[i].varname);
00204
00205 node *par = OBJECTDATA(parent, node);
00206 number_of_phases_out = 0;
00207 if (par->has_phase(PHASE_A))
00208 number_of_phases_out += 1;
00209 if (par->has_phase(PHASE_B))
00210 number_of_phases_out += 1;
00211 if (par->has_phase(PHASE_C))
00212 number_of_phases_out += 1;
00213 }
00214 else if (parent!=NULL && gl_object_isa(parent,"triplex_meter"))
00215 {
00216 number_of_phases_out = 4;
00217 struct {
00218 complex **var;
00219 char *varname;
00220 }
00221 map[] = {
00222
00223 {&pCircuit_V, "voltage_12"},
00224 {&pLine_I, "current_1"},
00225 {&pLine12, "current_12"},
00226 {&pPower, "measured_power"},
00227 };
00228
00229
00230 NR_mode = get_bool(parent,"NR_mode");
00231
00232
00233 for (i=0; i<sizeof(map)/sizeof(map[0]); i++)
00234 {
00235 if ((*(map[i].var) = get_complex(parent,map[i].varname))==NULL)
00236 {
00237 GL_THROW("%s (%s:%d) does not implement triplex_meter variable %s for %s (battery:%d)",
00238
00239
00240
00241
00242
00243
00244 parent->name?parent->name:"unnamed object", parent->oclass->name, parent->id, map[i].varname, obj->name?obj->name:"unnamed", obj->id);
00245 }
00246 }
00247 }
00248 else if (parent != NULL && strcmp(parent->oclass->name,"inverter") == 0)
00249 {
00250 number_of_phases_out = 0;
00251 struct {
00252 complex **var;
00253 char *varname;
00254 }
00255
00256 map[] = {
00257
00258 {&pCircuit_V, "V_In"},
00259 {&pLine_I, "I_In"},
00260 };
00261
00262
00263 for (i=0; i<sizeof(map)/sizeof(map[0]); i++)
00264 {
00265 *(map[i].var) = get_complex(parent,map[i].varname);
00266 }
00267 }
00268 else if ((parent != NULL && strcmp(parent->oclass->name,"meter") != 0)||(parent != NULL && strcmp(parent->oclass->name,"triplex_meter") != 0)||(parent != NULL && strcmp(parent->oclass->name,"inverter") != 0))
00269 {
00270 throw("Battery must have a meter or triplex meter or inverter as it's parent");
00271
00272
00273
00274
00275
00276 }
00277 else
00278 {
00279 number_of_phases_out = 3;
00280 struct {
00281 complex **var;
00282 char *varname;
00283 }
00284 map[] = {
00285
00286 {&pCircuit_V, "voltage_A"},
00287 {&pLine_I, "current_A"},
00288 };
00289
00290 gl_warning("Battery:%d has no parent meter object defined; using static voltages", obj->id);
00291
00292
00293 *(map[0].var) = &default_line123_voltage[0];
00294 *(map[1].var) = &default_line1_current[0];
00295
00296
00297 default_line123_voltage[0] = complex(480,0);
00298 default_line123_voltage[1] = complex(480*cos(2*PI/3),480*sin(2*PI/3));
00299 default_line123_voltage[2] = complex(480*cos(-2*PI/3),480*sin(-2*PI/3));
00300 }
00301
00302 if (gen_mode_v==GM_UNKNOWN)
00303 {
00304 throw("Generator (id:%d) generator_mode is not specified",obj->id);
00305 }
00306 else if (gen_mode_v == GM_VOLTAGE_CONTROLLED)
00307 {
00308 GL_THROW("VOLTAGE_CONTROLLED generator_mode is not yet implemented.");
00309 }
00310
00311 if (additional_controls == AC_LINEAR_TEMPERATURE)
00312 {
00313 static FINDLIST *climates = NULL;
00314 int not_found = 0;
00315 if (climates==NULL && not_found==0)
00316 {
00317 climates = gl_find_objects(FL_NEW,FT_CLASS,SAME,"climate",FT_END);
00318 if (climates==NULL)
00319 {
00320 not_found = 1;
00321 gl_warning("battery: no climate data found, using static data");
00322
00323
00324 static double tout=59.0, solar=1000;
00325 pTout = &tout;
00326 pSolar = &solar;
00327 }
00328 else if (climates->hit_count>1)
00329 {
00330 gl_warning("battery: %d climates found, using first one defined", climates->hit_count);
00331 }
00332 }
00333 if (climates!=NULL)
00334 {
00335 if (climates->hit_count==0)
00336 {
00337
00338 static double tout=59.0, solar=1000;
00339 pTout = &tout;
00340 pSolar = &solar;
00341 }
00342 else
00343 {
00344 OBJECT *clim = gl_find_next(climates,NULL);
00345 if (clim->rank<=obj->rank)
00346 gl_set_dependent(clim,obj);
00347 pTout = (double*)GETADDR(clim,gl_get_property(clim,"temperature"));
00348 pSolar = (double*)GETADDR(clim,gl_get_property(clim,"solar_flux"));
00349 }
00350 }
00351 }
00352
00353 if (gen_status_v == OFFLINE)
00354 {
00355
00356 gl_warning("Generator (id:%d) is out of service!",obj->id);
00357 }
00358 else
00359 {
00360 switch(rfb_size_v)
00361 {
00362 case HOUSEHOLD:
00363 V_Max = 260;
00364 I_Max = 15;
00365 Max_P = V_Max.Mag()*I_Max.Mag();
00366 E_Max = 6*Max_P;
00367 base_efficiency = 0.9;
00368 break;
00369 case SMALL:
00370 V_Max = 75.2;
00371 I_Max = 250;
00372 Max_P = 18800;
00373 E_Max = 160000;
00374 base_efficiency = 0.7;
00375 break;
00376 case MED_COMMERCIAL:
00377 V_Max = 115;
00378 I_Max = 435;
00379 Max_P = 50000;
00380 E_Max = 175000;
00381 base_efficiency = 0.8;
00382 break;
00383 case MED_HIGH_ENERGY:
00384 V_Max = 115;
00385 I_Max = 435;
00386 Max_P = 50000;
00387 E_Max = 400000;
00388 base_efficiency = 0.8;
00389 break;
00390 case LARGE:
00391 V_Max = 8000;
00392 I_Max = 30;
00393 Max_P = V_Max.Mag()*I_Max.Mag();
00394 E_Max = 24*Max_P;
00395 base_efficiency = 0.9;
00396 break;
00397 default:
00398 if (V_Max == 0)
00399 {
00400 gl_warning("V_max was not given -- using default value (battery: %d)",obj->id);
00401 V_Max = 115;
00402 }
00403 if (I_Max == 0)
00404 {
00405 gl_warning("I_max was not given -- using default value (battery: %d)",obj->id);
00406 I_Max = 435;
00407 }
00408 if (Max_P == 0)
00409 {
00410 gl_warning("Max_P was not given -- using default value (battery: %d)",obj->id);
00411 Max_P = V_Max.Re()*I_Max.Re();
00412 }
00413 if (E_Max == 0)
00414 {
00415 gl_warning("E_max was not given -- using default value (battery: %d)",obj->id);
00416 E_Max = 6*Max_P;
00417 }
00418 if (base_efficiency == 0)
00419 {
00420 gl_warning("base_efficiency was not given -- using default value (battery: %d)",obj->id);
00421 base_efficiency = 0.8;
00422 }
00423 break;
00424 }
00425 }
00426 if (power_set_high <= power_set_low)
00427 gl_warning("power_set_high is less than power_set_low -- oscillations will most likely occur");
00428
00429 if (Energy < 0)
00430 {
00431 gl_warning("Initial Energy was not set, or set to a negative number. Randomizing initial value.");
00432 Energy = gl_random_normal(E_Max/2,E_Max/20);
00433 }
00434
00435 recalculate = true;
00436 power_transferred = 0;
00437
00438 first_time_step = 0;
00439
00440 return 1;
00441 }
00442
00443
00444 complex battery::calculate_v_terminal(complex v, complex i){
00445 return v - (i * Rinternal);
00446 }
00447
00448
00449 complex *battery::get_complex(OBJECT *obj, char *name)
00450 {
00451 PROPERTY *p = gl_get_property(obj,name);
00452 if (p==NULL || p->ptype!=PT_complex)
00453 return NULL;
00454 return (complex*)GETADDR(obj,p);
00455 }
00456
00457
00458 TIMESTAMP battery::rfb_event_time(TIMESTAMP t0, complex power, double e){
00459 if((e < margin) && (power < 0)){
00460 return TS_NEVER;
00461 }
00462 if((e > E_Max - margin)&& (power > 0)){
00463 return TS_NEVER;
00464 }
00465
00466 if(power < 0){
00467 double t1 = (e) / power.Re();
00468 t1 = t1 * 60 * 60;
00469 return t0 + (TIMESTAMP)(t1 * TS_SECOND);
00470 }else if(power > 0){
00471 double t1 = (E_Max - e) / power.Re();
00472 t1 = t1 * 60 * 60;
00473 return t0 + (TIMESTAMP)(t1 * TS_SECOND);
00474 }else{
00475 return TS_NEVER;
00476 }
00477
00478 }
00479
00480
00481 double battery::calculate_efficiency(complex voltage, complex current){
00482 if(voltage.Mag() == 0 || current.Mag() == 0){
00483 return 1;
00484 }
00485 else if(current.Mag() < 5){
00486 return 0.95;
00487 }
00488 else if(current.Mag() < 10){
00489 return 0.9;
00490 }
00491 else if(current.Mag() < 20){
00492 return 0.8;
00493 }else{
00494 return 0.7;
00495 }
00496
00497 }
00498
00499
00500
00501
00502 TIMESTAMP battery::presync(TIMESTAMP t0, TIMESTAMP t1)
00503 {
00504
00505 TIMESTAMP t2 = TS_NEVER;
00506
00507 return t2;
00508 }
00509
00510
00511 TIMESTAMP battery::sync(TIMESTAMP t0, TIMESTAMP t1)
00512 {
00513
00514
00515 if (gen_mode_v == GM_POWER_DRIVEN || gen_mode_v == GM_POWER_VOLTAGE_HYBRID)
00516 {
00517 if (number_of_phases_out == 3)
00518 {
00519 if (gen_mode_v == GM_POWER_VOLTAGE_HYBRID)
00520 GL_THROW("generator_mode POWER_VOLTAGE_HYBRID is not supported in 3-phase yet (only split phase is supported");
00521
00522 complex volt[3];
00523 TIMESTAMP dt,t_energy_limit;
00524
00525 for (int i=0;i<3;i++)
00526 {
00527 volt[i] = pCircuit_V[i];
00528 }
00529
00530 if (first_time_step == 0)
00531 {
00532 dt = 0;
00533 }
00534 else if (prev_time == 0)
00535 {
00536 prev_time = t1;
00537 dt = 0;
00538 }
00539 else if (prev_time < t1)
00540 {
00541 dt = t1 - prev_time;
00542 prev_time = t1;
00543 }
00544 else
00545 dt = 0;
00546
00547 if (prev_state == -1)
00548 {
00549 Energy = Energy - (1 / base_efficiency) * power_transferred * (double)dt / 3600;
00550 if (Energy < 0)
00551 Energy = 0;
00552 }
00553 else if (prev_state == 1)
00554 Energy = Energy + base_efficiency * power_transferred * (double)dt / 3600;
00555
00556 check_power = (*pPower).Mag();
00557
00558 if (first_time_step > 0)
00559 {
00560 if (volt[0].Mag() > V_Max.Mag() || volt[1].Mag() > V_Max.Mag() || volt[2].Mag() > V_Max.Mag())
00561 {
00562 gl_warning("The voltages at the batteries meter are higher than rated. No power output.");
00563 battery_state = BS_WAITING;
00564
00565 pLine_I[0] += complex(0,0);
00566 pLine_I[1] += complex(0,0);
00567 pLine_I[2] += complex(0,0);
00568
00569 last_current[0] = complex(0,0);
00570 last_current[1] = complex(0,0);
00571 last_current[2] = complex(0,0);
00572
00573 return TS_NEVER;
00574 }
00575 else if (check_power > power_set_high && Energy > 0)
00576 {
00577 prev_state = -1;
00578 battery_state = BS_DISCHARGING;
00579
00580 pLine_I[0] += complex(-I_Max.Mag()/3,0,A);
00581 pLine_I[1] += complex(-I_Max.Mag()/3,-2*PI/3,A);
00582 pLine_I[2] += complex(-I_Max.Mag()/3,2*PI/3,A);
00583
00584 last_current[0] = complex(-I_Max.Mag()/3,0,A);;
00585 last_current[1] = complex(-I_Max.Mag()/3,-2*PI/3,A);
00586 last_current[2] = complex(-I_Max.Mag()/3,2*PI/3,A);
00587
00588 power_transferred = 0;
00589
00590 for (int i=0;i<3;i++)
00591 {
00592 power_transferred += last_current[i].Mag()*volt[i].Mag();
00593 }
00594
00595 VA_Out = power_transferred;
00596
00597 t_energy_limit = TIMESTAMP(3600 * Energy / power_transferred);
00598 if (t_energy_limit == 0)
00599 t_energy_limit = 1;
00600
00601 return -(t1 + t_energy_limit);
00602 }
00603 else if (check_power < power_set_low && Energy < E_Max)
00604 {
00605 prev_state = 1;
00606 battery_state = BS_CHARGING;
00607
00608 pLine_I[0] += complex(I_Max.Mag()/3,0,A);
00609 pLine_I[1] += complex(I_Max.Mag()/3,-2*PI/3,A);
00610 pLine_I[2] += complex(I_Max.Mag()/3,2*PI/3,A);
00611
00612 last_current[0] = complex(I_Max.Mag()/3,0,A);
00613 last_current[1] = complex(I_Max.Mag()/3,-2*PI/3,A);
00614 last_current[2] = complex(I_Max.Mag()/3,2*PI/3,A);
00615
00616 power_transferred = 0;
00617
00618 for (int i=0;i<3;i++)
00619 {
00620 power_transferred += last_current[i].Mag()*volt[i].Mag();
00621 }
00622
00623 VA_Out = power_transferred;
00624
00625 t_energy_limit = TIMESTAMP(3600 * (E_Max - Energy) / power_transferred);
00626 if (t_energy_limit == 0)
00627 t_energy_limit = 1;
00628
00629 return -(t1 + t_energy_limit);
00630 }
00631 else if (check_power < (power_set_low + Max_P) && prev_state == 1 && Energy < E_Max)
00632 {
00633 prev_state = 1;
00634 battery_state = BS_CHARGING;
00635
00636 pLine_I[0] += complex(I_Max.Mag()/3,0,A);
00637 pLine_I[1] += complex(I_Max.Mag()/3,-2*PI/3,A);
00638 pLine_I[2] += complex(I_Max.Mag()/3,2*PI/3,A);
00639
00640 last_current[0] = complex(I_Max.Mag()/3,0,A);
00641 last_current[1] = complex(I_Max.Mag()/3,-2*PI/3,A);
00642 last_current[2] = complex(I_Max.Mag()/3,2*PI/3,A);
00643
00644 power_transferred = 0;
00645
00646 for (int i=0;i<3;i++)
00647 {
00648 power_transferred += last_current[i].Mag()*volt[i].Mag();
00649 }
00650
00651 VA_Out = power_transferred;
00652
00653 t_energy_limit = TIMESTAMP(3600 * (E_Max - Energy) / power_transferred);
00654 if (t_energy_limit == 0)
00655 t_energy_limit = 1;
00656
00657 return -(t1 + t_energy_limit);
00658 }
00659 else if (check_power > (power_set_high - Max_P) && prev_state == -1 && Energy > 0)
00660 {
00661 prev_state = -1;
00662 battery_state = BS_DISCHARGING;
00663
00664 pLine_I[0] += complex(-I_Max.Mag()/3,0,A);
00665 pLine_I[1] += complex(-I_Max.Mag()/3,-2*PI/3,A);
00666 pLine_I[2] += complex(-I_Max.Mag()/3,2*PI/3,A);
00667
00668 last_current[0] = complex(-I_Max.Mag()/3,0,A);
00669 last_current[1] = complex(-I_Max.Mag()/3,-2*PI/3,A);
00670 last_current[2] = complex(-I_Max.Mag()/3,2*PI/3,A);
00671
00672 power_transferred = 0;
00673
00674 for (int i=0;i<3;i++)
00675 {
00676 power_transferred += last_current[i].Mag()*volt[i].Mag();
00677 }
00678
00679 VA_Out = -power_transferred;
00680
00681 t_energy_limit = TIMESTAMP(3600 * Energy / power_transferred);
00682
00683 if (t_energy_limit == 0)
00684 t_energy_limit = 1;
00685
00686 return -(t1 + t_energy_limit);
00687 }
00688 else
00689 {
00690 if (Energy <= 0)
00691 battery_state = BS_EMPTY;
00692 else if (Energy >= E_Max)
00693 battery_state = BS_FULL;
00694 else
00695 battery_state = BS_WAITING;
00696
00697 pLine_I[0] += complex(0,0);
00698 pLine_I[1] += complex(0,0);
00699 pLine_I[2] += complex(0,0);
00700
00701 last_current[0] = complex(0,0);
00702 last_current[1] = complex(0,0);
00703 last_current[2] = complex(0,0);
00704
00705 prev_state = 0;
00706 power_transferred = 0;
00707 VA_Out = power_transferred;
00708 return TS_NEVER;
00709 }
00710 }
00711 else
00712 {
00713 if (Energy <= 0)
00714 battery_state = BS_EMPTY;
00715 else if (Energy >= E_Max)
00716 battery_state = BS_FULL;
00717 else
00718 battery_state = BS_WAITING;
00719
00720 first_time_step = 1;
00721 return TS_NEVER;
00722 }
00723 }
00724 else if (number_of_phases_out == 4)
00725 {
00726 if (*NR_mode == false)
00727 {
00728 complex volt;
00729 TIMESTAMP dt,t_energy_limit;
00730
00731 volt = pCircuit_V[0];
00732
00733 if (first_time_step == 0)
00734 {
00735 dt = 0;
00736 }
00737 else if (prev_time == 0)
00738 {
00739 prev_time = t1;
00740 dt = 0;
00741 }
00742 else if (prev_time < t1)
00743 {
00744 dt = t1 - prev_time;
00745 prev_time = t1;
00746 }
00747 else
00748 dt = 0;
00749
00750 if (prev_state == -1)
00751 {
00752 Energy = Energy - (1 / base_efficiency) * power_transferred * (double)dt / 3600;
00753 if (Energy < 0)
00754 Energy = 0;
00755 }
00756 else if (prev_state == 1)
00757 {
00758 Energy = Energy + base_efficiency * power_transferred * (double)dt / 3600;
00759 if (Energy > E_Max)
00760 Energy = E_Max;
00761 }
00762 check_power = (*pPower).Mag();
00763
00764 if (additional_controls == AC_LINEAR_TEMPERATURE)
00765 {
00766 double sens2 = (1 - sensitivity)/(-sensitivity);
00767
00768
00769 double slope1_hi = power_set_high_highT / (high_temperature - midpoint_temperature * sens2);
00770 double yint1_hi = -midpoint_temperature * sens2 * slope1_hi;
00771
00772
00773 double slope1_lo = (power_set_high - (slope1_hi * midpoint_temperature + yint1_hi)) / (low_temperature - midpoint_temperature);
00774 double yint1_lo = power_set_high - low_temperature * slope1_lo;
00775
00776
00777 double slope2_hi = power_set_low_highT / (high_temperature - midpoint_temperature * sens2);
00778 double yint2_hi = -midpoint_temperature * sens2 * slope2_hi;
00779
00780
00781 double slope2_lo = (power_set_low - (slope2_hi * midpoint_temperature + yint2_hi)) / (low_temperature - midpoint_temperature);
00782 double yint2_lo = power_set_low - low_temperature * slope2_lo;
00783
00784 if (*pTout > midpoint_temperature)
00785 {
00786 check_power_high = slope1_hi * (*pTout) + yint1_hi;
00787 check_power_low = slope2_hi * (*pTout) + yint2_hi;
00788 }
00789 else
00790 {
00791 check_power_high = slope1_lo * (*pTout) + yint1_lo;
00792 check_power_low = slope2_lo * (*pTout) + yint2_lo;
00793 }
00794 }
00795 else
00796 {
00797 check_power_high = power_set_high;
00798 check_power_low = power_set_low;
00799 }
00800
00801 if (first_time_step > 0)
00802 {
00803 if (volt.Mag() > V_Max.Mag() || volt.Mag()/240 < 0.9 || volt.Mag()/240 > 1.1)
00804 {
00805 gl_verbose("The voltages at the batteries meter are higher than rated, or outside of ANSI emergency specifications. No power output.");
00806 battery_state = BS_WAITING;
00807
00808 last_current[0].SetPolar(parasitic_power_draw/volt.Mag(),volt.Arg());
00809 *pLine12 += last_current[0];
00810
00811
00812
00813 return TS_NEVER;
00814 }
00815
00816
00817 else if ((check_power > check_power_high || (volt.Mag() < voltage_set_low && gen_mode_v == GM_POWER_VOLTAGE_HYBRID)) && Energy > 0)
00818 {
00819 if (volt.Mag() > voltage_set_high)
00820 {
00821 if (prev_state != 0)
00822 no_of_cycles += 1;
00823
00824 last_current[0].SetPolar(parasitic_power_draw/volt.Mag(),volt.Arg());
00825 *pLine12 += last_current[0];
00826
00827
00828 VA_Out = power_transferred = 0;
00829 battery_state = BS_CONFLICTED;
00830
00831 return TS_NEVER;
00832 }
00833 else
00834 {
00835 if (prev_state != -1)
00836 no_of_cycles +=1;
00837
00838 prev_state = -1;
00839 battery_state = BS_DISCHARGING;
00840 double power_desired = check_power - check_power_high;
00841 if (power_desired <= 0)
00842 {
00843 last_current[0].SetPolar(-I_Max.Mag(),volt.Arg());
00844 *pLine12 += last_current[0];
00845
00846 }
00847 else
00848 {
00849 if (power_desired >= Max_P)
00850 power_desired = Max_P;
00851
00852 double current_desired = -power_desired/volt.Mag();
00853 last_current[0].SetPolar(current_desired,volt.Arg());
00854 *pLine12 += last_current[0];
00855 }
00856
00857 power_transferred = last_current[0].Mag()*volt.Mag();
00858
00859 VA_Out = power_transferred;
00860
00861 t_energy_limit = TIMESTAMP(3600 * Energy / power_transferred);
00862 if (t_energy_limit == 0)
00863 t_energy_limit = 1;
00864
00865 return -(t1 + t_energy_limit);
00866 }
00867 }
00868
00869
00870 else if ((check_power < check_power_low || (gen_mode_v == GM_POWER_VOLTAGE_HYBRID && volt.Mag() > voltage_set_high)) && Energy < E_Max)
00871 {
00872 if (volt.Mag() < voltage_set_low)
00873 {
00874 if (prev_state != 0)
00875 no_of_cycles += 1;
00876 last_current[0].SetPolar(parasitic_power_draw/volt.Mag(),volt.Arg());
00877 *pLine12 += last_current[0];
00878
00879 VA_Out = power_transferred = 0;
00880 battery_state = BS_CONFLICTED;
00881
00882 return TS_NEVER;
00883 }
00884 else
00885 {
00886 if (prev_state != 1)
00887 no_of_cycles +=1;
00888
00889 prev_state = 1;
00890 battery_state = BS_CHARGING;
00891 double power_desired = check_power_low - check_power;
00892
00893 if (power_desired <= 0)
00894 {
00895 last_current[0].SetPolar(I_Max.Mag(),volt.Arg());
00896 *pLine12 += last_current[0];
00897
00898 }
00899 else
00900 {
00901 if (power_desired >= Max_P)
00902 power_desired = Max_P;
00903
00904 double current_desired = power_desired/volt.Mag();
00905 last_current[0].SetPolar(current_desired,volt.Arg());
00906 *pLine12 += last_current[0];
00907 }
00908
00909 power_transferred = last_current[0].Mag()*volt.Mag();
00910
00911 VA_Out = power_transferred;
00912
00913 t_energy_limit = TIMESTAMP(3600 * (E_Max - Energy) / power_transferred);
00914 if (t_energy_limit == 0)
00915 t_energy_limit = 1;
00916
00917 return -(t1 + t_energy_limit);
00918 }
00919 }
00920
00921 else if ((check_power < check_power_low || (gen_mode_v == GM_POWER_VOLTAGE_HYBRID && volt.Mag() > voltage_set_high - deadband))&& prev_state == 1 && Energy < E_Max)
00922 {
00923 if (volt.Mag() < voltage_set_low)
00924 {
00925 if (prev_state != 0)
00926 no_of_cycles += 1;
00927
00928 last_current[0].SetPolar(parasitic_power_draw/volt.Mag(),volt.Arg());
00929 *pLine12 += last_current[0];
00930
00931 VA_Out = power_transferred = 0;
00932 battery_state = BS_CONFLICTED;
00933
00934 return TS_NEVER;
00935 }
00936 else
00937 {
00938 if (prev_state != 1)
00939 no_of_cycles +=1;
00940
00941 prev_state = 1;
00942 battery_state = BS_CHARGING;
00943
00944 double power_desired = check_power_low - check_power;
00945
00946 if (power_desired <= 0)
00947 {
00948 last_current[0].SetPolar(I_Max.Mag(),volt.Arg());
00949 *pLine12 += last_current[0];
00950
00951 }
00952 else
00953 {
00954 if (power_desired >= Max_P)
00955 power_desired = Max_P;
00956
00957 double current_desired = power_desired/volt.Mag();
00958 last_current[0].SetPolar(current_desired, volt.Arg());
00959 *pLine12 += last_current[0];
00960 }
00961
00962 power_transferred = last_current[0].Mag()*volt.Mag();
00963
00964 VA_Out = power_transferred;
00965
00966 t_energy_limit = TIMESTAMP(3600 * (E_Max - Energy) / power_transferred);
00967 if (t_energy_limit == 0)
00968 t_energy_limit = 1;
00969
00970 return -(t1 + t_energy_limit);
00971 }
00972 }
00973
00974 else if ((check_power > check_power_high || (gen_mode_v == GM_POWER_VOLTAGE_HYBRID && volt.Mag() < voltage_set_low + deadband)) && prev_state == -1 && Energy > 0)
00975 {
00976 if (volt.Mag() > voltage_set_high)
00977 {
00978 if (prev_state != 0)
00979 no_of_cycles += 1;
00980
00981 last_current[0].SetPolar(parasitic_power_draw/volt.Mag(),volt.Arg());
00982 *pLine12 += last_current[0];
00983
00984 VA_Out = power_transferred = 0;
00985 battery_state = BS_CONFLICTED;
00986
00987 return TS_NEVER;
00988 }
00989 else
00990 {
00991 if (prev_state != -1)
00992 no_of_cycles +=1;
00993
00994 prev_state = -1;
00995 battery_state = BS_DISCHARGING;
00996
00997 double power_desired = check_power - check_power_high;
00998 if (power_desired <= 0)
00999 {
01000 last_current[0].SetPolar(-I_Max.Mag(),volt.Arg());
01001 *pLine12 += last_current[0];
01002
01003 }
01004 else
01005 {
01006 if (power_desired >= Max_P)
01007 power_desired = Max_P;
01008
01009 double current_desired = -power_desired/volt.Mag();
01010 last_current[0].SetPolar(current_desired,volt.Arg());
01011 *pLine12 += last_current[0];
01012 }
01013
01014 power_transferred = last_current[0].Mag()*volt.Mag();
01015
01016 VA_Out = power_transferred;
01017
01018 t_energy_limit = TIMESTAMP(3600 * Energy / power_transferred);
01019
01020 if (t_energy_limit == 0)
01021 t_energy_limit = 1;
01022
01023 return -(t1 + t_energy_limit);
01024 }
01025 }
01026 else
01027 {
01028 if (prev_state != 0)
01029 no_of_cycles +=1;
01030
01031 last_current[0].SetPolar(parasitic_power_draw/volt.Mag(),volt.Arg());
01032 *pLine12 += last_current[0];
01033
01034 prev_state = 0;
01035 if (Energy <= 0)
01036 battery_state = BS_EMPTY;
01037 else if (Energy >= E_Max)
01038 battery_state = BS_FULL;
01039 else
01040 battery_state = BS_WAITING;
01041
01042 power_transferred = 0;
01043 VA_Out = power_transferred;
01044 return TS_NEVER;
01045 }
01046 }
01047 else
01048 {
01049 if (Energy <= 0)
01050 battery_state = BS_EMPTY;
01051 else if (Energy >= E_Max)
01052 battery_state = BS_FULL;
01053 else
01054 battery_state = BS_WAITING;
01055
01056 first_time_step = 1;
01057 return TS_NEVER;
01058 }
01059 }
01060 }
01061 }
01062 else
01063 {
01064
01065
01066
01068 V_Out = pCircuit_V[0];
01069 I_Out = pLine_I[0];
01070
01071
01072
01073
01074
01075 V_In = V_Out;
01076
01077 V_Internal = calculate_v_terminal(V_Out, I_Out);
01078
01079
01080
01081 efficiency = base_efficiency * calculate_efficiency(V_Out, I_Out);
01082
01083
01084
01085
01086 if (I_Out > 0){
01087 I_Internal = I_Out * efficiency;
01088 }
01089
01090 if(I_Out < 0){
01091 I_Internal = I_Out / efficiency;
01092 }
01093
01094
01095
01096
01097
01098
01099 VA_Out = V_Out * (~I_Out);
01100 VA_Internal = V_Internal * I_Internal;
01101
01102
01103
01104
01105
01106
01107 if(!recalculate){
01108
01109 if(t0 == prev_time){
01110
01111 Energy = E_Next;
01112
01113
01114
01115 recalculate = true;
01116
01117 return battery::sync(t0, t1);
01118 }else{
01119
01120
01121
01122 Energy = E_Next;
01123
01124
01125 recalculate = true;
01126
01127 return battery::sync(prev_time, t1);
01128 }
01129
01130 }else{
01132
01133
01134
01135
01136
01138
01139 double t2 = (gl_tohours((TIMESTAMP)t1) - gl_tohours((TIMESTAMP)t0));
01140
01141 if(abs((double)V_Out.Re()) > abs((double)V_Max.Re())){
01142
01143 V_Out = V_Max;
01144 V_In = V_Out;
01145 V_Internal = V_Out - (I_Out * Rinternal);
01146 }
01147
01148 if(abs((double)I_Out.Re()) > abs((double)I_Max.Re())){
01149
01150 I_Out = I_Max;
01151 }
01152
01153 if(abs((double)VA_Out.Re()) > abs((double)Max_P)){
01154
01155 VA_Out = complex(Max_P , 0);
01156 VA_Internal = VA_Out - (I_Out * I_Out * Rinternal);
01157 }
01158
01159
01160
01161 prev_time = t1;
01162
01163 if(VA_Out < 0){
01164
01165 if(Energy == 0 || Energy <= margin){
01166
01167 if(connected){
01168
01169 I_In = I_Max + complex(abs(I_Out.Re()), abs(I_Out.Im()));
01170 I_Prev = I_Max / efficiency;
01171
01172 recalculate = false;
01173 E_Next = Energy + (((I_In - complex(abs(I_Out.Re()), abs(I_Out.Im()))) * V_Internal / efficiency) * t2).Re();
01174 TIMESTAMP t3 = rfb_event_time(t0, (I_In - complex(abs(I_Out.Re()), abs(I_Out.Im()))) * V_Internal / efficiency, Energy);
01175 return t3;
01176 }
01177 else{
01178
01179 I_In = 0;
01180 I_Prev = 0;
01181 V_In = V_Out;
01182 VA_Out = 0;
01183 E_Next = 0;
01184 recalculate = false;
01185 return TS_NEVER;
01186 }
01187 }
01188
01189 if((Energy + (V_Internal * I_Prev.Re()).Re() * t2) <= margin){
01190
01191 if(connected){
01192
01193 I_In = I_Max + complex(abs(I_Out.Re()), abs(I_Out.Im()));
01194 I_Prev = I_Max / efficiency;
01195
01196 E_Next = margin;
01197 recalculate = false;
01198 TIMESTAMP t3 = rfb_event_time(t0, (I_In - complex(abs(I_Out.Re()), abs(I_Out.Im()))) * V_Internal / efficiency, Energy);
01199 return t3;
01200 }else{
01201
01202 TIMESTAMP t3 = rfb_event_time(t0, VA_Internal, Energy);
01203 E_Next = 0;
01204 I_In = 0;
01205 I_Prev = 0;
01206 recalculate = false;
01207 return t3;
01208 }
01209 }else{
01210
01211 E_Next = Energy + (VA_Internal.Re() * t2);
01212 I_In = 0;
01213 I_Prev = 0;
01214 recalculate = false;
01215 TIMESTAMP t3 = rfb_event_time(t0, VA_Internal, Energy);
01216 return t3;
01217 }
01218 }else if (VA_Out > 0){
01219 if(Energy >= (E_Max - margin)){
01220
01221 if(connected){
01222
01223
01224 E_Next = Energy;
01225 I_In = I_Out = 0;
01226 I_Prev = 0;
01227 recalculate = false;
01228 return TS_NEVER;
01229 }else{
01230
01231 I_In = I_Out = 0;
01232 I_Prev = 0;
01233 V_Out = V_Out;
01234 VA_Out = 0;
01235 E_Next = Energy;
01236 return TS_NEVER;
01237 }
01238 }
01239
01240 if(Energy + ((V_Internal * I_Prev.Re()) * efficiency * t2).Re() >= (E_Max - margin)){
01241
01242 TIMESTAMP t3 = rfb_event_time(t0, VA_Internal, Energy);
01243 I_In = 0;
01244 I_Prev = 0;
01245 E_Next = E_Max - margin;
01246 recalculate = false;
01247 return t3;
01248 }else{
01249 if(connected){
01250
01251
01252 I_In = I_Max - I_Out;
01253 I_Prev = I_Max * efficiency;
01254 E_Next = Energy + ((I_Max * V_Internal) * efficiency * t2).Re();
01255 recalculate = false;
01256 TIMESTAMP t3 = rfb_event_time(t0, (I_Max * V_Internal * efficiency), Energy);
01257 return t3;
01258 }else{
01259
01260 I_In = 0;
01261 I_Prev = 0;
01262 E_Next = Energy + (VA_Internal * t2).Re();
01263 recalculate = false;
01264 TIMESTAMP t3 = rfb_event_time(t0, VA_Internal, Energy);
01265 return t3;
01266 }
01267 }
01268 }else{
01269
01270 recalculate = false;
01271 I_In = 0;
01272 I_Prev = 0;
01273 E_Next = Energy;
01274 return TS_NEVER;
01275 }
01276
01277 }
01278 }
01279
01280 return TS_NEVER;
01281 }
01282
01283
01284 TIMESTAMP battery::postsync(TIMESTAMP t0, TIMESTAMP t1)
01285 {
01286
01287 TIMESTAMP result;
01288 if (*NR_mode == false)
01289 {
01290 Iteration_Toggle = !Iteration_Toggle;
01291 if (number_of_phases_out == 3)
01292 {
01293 pLine_I[0] -= last_current[0];
01294 pLine_I[1] -= last_current[1];
01295 pLine_I[2] -= last_current[2];
01296 }
01297 else if (number_of_phases_out == 4)
01298 {
01299 complex temp;
01300 temp = *pLine12;
01301 temp -= last_current[0];
01302 *pLine12 = temp;
01303 }
01304 result = TS_NEVER;
01305 }
01306 else
01307 result = TS_NEVER;
01308
01309 if (Iteration_Toggle == true)
01310 return result;
01311 else
01312 return TS_NEVER;
01313 }
01314
01315 bool *battery::get_bool(OBJECT *obj, char *name)
01316 {
01317 PROPERTY *p = gl_get_property(obj,name);
01318 if (p==NULL || p->ptype!=PT_bool)
01319 return NULL;
01320 return (bool*)GETADDR(obj,p);
01321 }
01322
01324
01326
01327 EXPORT int create_battery(OBJECT **obj, OBJECT *parent)
01328 {
01329 try
01330 {
01331 *obj = gl_create_object(battery::oclass);
01332 if (*obj!=NULL)
01333 {
01334 battery *my = OBJECTDATA(*obj,battery);
01335 gl_set_parent(*obj,parent);
01336 return my->create();
01337 }
01338 }
01339 catch (char *msg)
01340 {
01341 gl_error("create_battery: %s", msg);
01342 }
01343 return 0;
01344 }
01345
01346 EXPORT int init_battery(OBJECT *obj, OBJECT *parent)
01347 {
01348 try
01349 {
01350 if (obj!=NULL)
01351 return OBJECTDATA(obj,battery)->init(parent);
01352 }
01353 catch (char *msg)
01354 {
01355 gl_error("init_battery(obj=%d;%s): %s", obj->id, obj->name?obj->name:"unnamed", msg);
01356 }
01357 return 0;
01358 }
01359
01360 EXPORT TIMESTAMP sync_battery(OBJECT *obj, TIMESTAMP t1, PASSCONFIG pass)
01361 {
01362 TIMESTAMP t2 = TS_NEVER;
01363 battery *my = OBJECTDATA(obj,battery);
01364 try
01365 {
01366 switch (pass) {
01367 case PC_PRETOPDOWN:
01368 t2 = my->presync(obj->clock,t1);
01369 break;
01370 case PC_BOTTOMUP:
01371 t2 = my->sync(obj->clock,t1);
01372 break;
01373 case PC_POSTTOPDOWN:
01374 t2 = my->postsync(obj->clock,t1);
01375 break;
01376 default:
01377 GL_THROW("invalid pass request (%d)", pass);
01378 break;
01379 }
01380 if (pass==clockpass)
01381 obj->clock = t1;
01382 }
01383 catch (char *msg)
01384 {
01385 gl_error("sync_battery(obj=%d;%s): %s", obj->id, obj->name?obj->name:"unnamed", msg);
01386 }
01387 return t2;
01388 }