00001
00019 #include <stdlib.h>
00020 #include <stdio.h>
00021 #include <errno.h>
00022 #include <math.h>
00023 #include <float.h>
00024 #include <memory.h>
00025
00026 #include "zipload.h"
00027
00029
00031 CLASS* ZIPload::oclass = NULL;
00032 CLASS* ZIPload::pclass = NULL;
00033
00034 ZIPload::ZIPload(MODULE *module) : residential_enduse(module)
00035 {
00036
00037 if (oclass==NULL)
00038 {
00039
00040 oclass = gl_register_class(module,"ZIPload",sizeof(ZIPload),PC_BOTTOMUP|PC_AUTOLOCK);
00041 if (oclass==NULL)
00042 GL_THROW("unable to register object class implemented by %s",__FILE__);
00043
00044
00045 if (gl_publish_variable(oclass,
00046 PT_INHERIT, "residential_enduse",
00047 PT_double,"heat_fraction",PADDR(load.heatgain_fraction), PT_DESCRIPTION, "fraction of ZIPload that is transferred as heat",
00048 PT_double,"base_power[kW]",PADDR(base_power), PT_DESCRIPTION, "base real power of the overall load",
00049 PT_double,"power_pf",PADDR(power_pf), PT_DESCRIPTION, "power factor for constant power portion",
00050 PT_double,"current_pf",PADDR(current_pf), PT_DESCRIPTION, "power factor for constant current portion",
00051 PT_double,"impedance_pf",PADDR(impedance_pf), PT_DESCRIPTION, "power factor for constant impedance portion",
00052 PT_bool,"is_240",PADDR(is_240), PT_DESCRIPTION, "load is 220/240 V (across both phases)",
00053 PT_double,"breaker_val[A]",PADDR(breaker_val), PT_DESCRIPTION, "Amperage of connected breaker",
00054 PT_complex,"actual_power[kVA]",PADDR(actual_power),PT_DESCRIPTION,"variable to pull actual load as function of voltage",
00055 PT_double,"multiplier",PADDR(multiplier),PT_DESCRIPTION,"this variable is used to modify the base power as a function of multiplier x base_power",
00056 PT_bool,"heatgain_only",PADDR(heatgain_only),PT_DESCRIPTION,"allows the zipload to generate heat only (no kW), not activated by default",
00057
00058
00059 PT_bool,"demand_response_mode",PADDR(demand_response_mode), PT_DESCRIPTION, "Activates equilibrium dynamic representation of demand response",
00060 PT_int16,"number_of_devices",PADDR(N), PT_DESCRIPTION, "Number of devices to model - base power is the total load of all devices",
00061 PT_int16,"thermostatic_control_range",PADDR(L),PT_DESCRIPTION, "Range of the thermostat's control operation",
00062 PT_double,"number_of_devices_off",PADDR(N_off),PT_DESCRIPTION, "Total number of devices that are off",
00063 PT_double,"number_of_devices_on",PADDR(N_on),PT_DESCRIPTION, "Total number of devices that are on",
00064
00065
00066 PT_double,"rate_of_cooling[K/h]",PADDR(roff),PT_DESCRIPTION, "rate at which devices cool down",
00067 PT_double,"rate_of_heating[K/h]",PADDR(ron),PT_DESCRIPTION, "rate at which devices heat up",
00068
00069
00070
00071 PT_int16,"temperature",PADDR(x),PT_DESCRIPTION, "temperature of the device's controlled media (eg air temp or water temp)",
00072 PT_double,"phi",PADDR(phi),PT_DESCRIPTION, "duty cycle of the device",
00073
00074 PT_double,"demand_rate[1/h]",PADDR(eta),PT_DESCRIPTION, "consumer demand rate that prematurely turns on a device or population",
00075
00076 PT_double,"nominal_power",PADDR(nominal_power),PT_DESCRIPTION, "the rated amount of power demanded by devices that are on",
00077
00078
00079 PT_double,"duty_cycle[pu]",PADDR(duty_cycle),PT_DESCRIPTION, "fraction of time in the on state",
00080 PT_double,"recovery_duty_cycle[pu]",PADDR(recovery_duty_cycle),PT_DESCRIPTION, "fraction of time in the on state, while in recovery interval",
00081 PT_double,"period[h]",PADDR(period),PT_DESCRIPTION, "time interval to apply duty cycle",
00082 PT_double,"phase[pu]",PADDR(phase),PT_DESCRIPTION, "initial phase of load; duty will be assumed to occur at beginning of period",
00083 NULL)<1)
00084
00085 GL_THROW("unable to publish properties in %s",__FILE__);
00086 }
00087 }
00088
00089 ZIPload::~ZIPload()
00090 {
00091 }
00092
00093 int ZIPload::create()
00094 {
00095 int res = residential_enduse::create();
00096
00097
00098 load.name = oclass->name;
00099 load.power = load.admittance = load.current = load.total = 0.0;
00100 base_power = 0.0;
00101 load.heatgain_fraction = 0.90;
00102 load.power_factor = 1.00;
00103 load.power_fraction = 1.0;
00104 load.current_fraction = load.impedance_fraction = 0.0;
00105 load.config = 0x0;
00106 breaker_val = 1000.0;
00107 multiplier = 1;
00108 heatgain_only = false;
00109
00110 demand_response_mode = false;
00111 ron = roff = -1;
00112 phi = eta = 0.2;
00113 next_time = 0;
00114
00115 duty_cycle = recovery_duty_cycle = -1;
00116 period = 0;
00117 phase = 0;
00118
00119
00120 power_pf = current_pf = impedance_pf = 1.0;
00121
00122 load.voltage_factor = 1.0;
00123
00124 first_pass = 1;
00125 return res;
00126 }
00127
00128 int ZIPload::init(OBJECT *parent)
00129 {
00130 if(parent != NULL){
00131 if((parent->flags & OF_INIT) != OF_INIT){
00132 char objname[256];
00133 gl_verbose("zipload::init(): deferring initialization on %s", gl_name(parent, objname, 255));
00134 return 2;
00135 }
00136 }
00137 OBJECT *hdr = OBJECTHDR(this);
00138 hdr->flags |= OF_SKIPSAFE;
00139
00140 if (demand_response_mode == true)
00141 {
00142 gl_warning("zipload_init: The demand response zipload is an experimental model at this time.");
00143
00144
00145
00146
00147
00148
00149
00150 if (abs(eta) > 1)
00151 {
00152 GL_THROW("zipload_init: demand_rate (eta) must be between -1 and 1.");
00153
00154
00155
00156 }
00157 if (phi < 0 || phi > 1)
00158 {
00159 GL_THROW("zipload_init: duty_cycle (phi) must be between 0 and 1.");
00160
00161
00162
00163
00164
00165 }
00166
00167
00168 if (L > 0)
00169 if (L < 70)
00170 drm.nbins = L;
00171 else
00172 {
00173 gl_warning("zipload_init: Using a value for thermostatic_control_range (L) greater than 50 may cause some instabilities.");
00174
00175
00176
00177
00178 }
00179 else
00180 {
00181 GL_THROW("zipload_init: thermostatic_control_range (L) must be greater than 0.");
00182
00183
00184
00185
00186 }
00187
00188 drm.off = (double*)malloc(sizeof(double)*L);
00189 drm.on = (double*)malloc(sizeof(double)*L);
00190 if (drm.off==NULL || drm.on==NULL)
00191 {
00192 GL_THROW("zipload_init: memory allocation error. Please report this error.");
00193
00194
00195
00196
00197 }
00198
00199
00200 if (ron == -1 || roff == -1)
00201 {
00202 if (phi <= 0.5)
00203 {
00204 roff = phi/(1-phi);
00205 ron = 1;
00206 gl_warning("ron or roff was not set. Using phi to calculate. Step changes in demand rates as a function of time will be ignored.");
00207
00208
00209
00210
00211
00212
00213
00214
00215 }
00216 else
00217 {
00218 roff = 1;
00219 ron = (1-phi)/phi;
00220 gl_warning("ron or roff was not set. Using phi to calculate. Step changes in demand rates as a function of time will be ignored.");
00221
00222
00223
00224
00225
00226
00227
00228
00229 }
00230 }
00231 else
00232 phi = roff / (ron + roff);
00233
00234 if (roff < 0 || ron < 0)
00235 {
00236 GL_THROW("zipload_init: rate_of_cooling and rate_of_heating must be greater than or equal to 0.");
00237
00238
00239
00240
00241 }
00242
00243 non = noff = 0;
00244 double test_N = 0;
00245
00246 for (x=0; x<L; x++)
00247 {
00248
00249 if (eta != 0)
00250 {
00251 drm.on[x] = N * eta * (1-phi) * exp(eta*(L-0.5-x)/roff) / (exp(eta*L/roff)-1);
00252 drm.off[x] = drm.on[x] * ron/roff;
00253 test_N += drm.on[x] + drm.off[x];
00254
00255
00256 }
00257
00258
00259 else
00260 {
00261 non += drm.on[x] = N * phi/L;
00262 noff += drm.off[x] = N * (1-phi)/L;
00263 }
00264 }
00265
00266
00267 if (abs(test_N - N) != 0)
00268 {
00269 double extra = N - test_N;
00270 drm.off[0] += extra;
00271 }
00272
00273 }
00274
00275 if (duty_cycle > 1 || duty_cycle < 0)
00276 {
00277 if (duty_cycle != -1)
00278 {
00279 GL_THROW("Value of duty cycle is set to a value outside of 0-1.");
00280
00281
00282
00283
00284 }
00285 }
00286
00287
00288 if (duty_cycle != -1)
00289 {
00290 if (period <= 0)
00291 {
00292 GL_THROW("Period is less than or equal to zero.");
00293
00294
00295
00296 }
00297 if (phase < 0 || phase > 1)
00298 {
00299 GL_THROW("Phase is not between zero and one.");
00300
00301
00302
00303
00304
00305
00306 }
00307 }
00308
00309 if (heatgain_only == true)
00310 {
00311 load.config = EUC_HEATLOAD;
00312 load.power_fraction = load.current_fraction = load.impedance_fraction = 0.0;
00313 }
00314 if (is_240)
00315 {
00316 load.config |= EUC_IS220;
00317 }
00318
00319 load.breaker_amps = breaker_val;
00320
00321 return residential_enduse::init(parent);
00322 }
00323
00324 int ZIPload::isa(char *classname)
00325 {
00326 return (strcmp(classname,"ZIPload")==0 || residential_enduse::isa(classname));
00327 }
00328
00329 TIMESTAMP ZIPload::sync(TIMESTAMP t0, TIMESTAMP t1)
00330 {
00331 TIMESTAMP t2 = TS_NEVER;
00332 double real_power = 0.0;
00333 double imag_power = 0.0;
00334 double angleval;
00335 double temp_voltage_magnitude;
00336
00337 double test = multiplier;
00338
00339 if (demand_response_mode == true && next_time <= t1)
00340 {
00341 double dNon,dNoff;
00342 double hold_off;
00343
00344 N_on = N_off = 0;
00345
00346 for (int jj=0; jj<L; jj++)
00347 {
00348
00349
00350
00351 if (eta > 0)
00352 {
00353 if (jj != (L-1))
00354 dNon = -ron * drm.on[jj] + eta * drm.off[jj] + ron * drm.on[jj+1];
00355 else
00356 dNon = -ron * drm.on[jj] + (1 - eta) * drm.off[jj] * roff + eta * drm.off[jj];
00357
00358 if (jj != 0)
00359 dNoff = -(1 - eta) * drm.off[jj] * roff - eta * drm.off[jj] + (1 - eta) * hold_off * roff;
00360 else
00361 dNoff = -(1 - eta) * drm.off[jj] * roff - eta * drm.off[jj] + ron * drm.on[jj];
00362 }
00363 else
00364 {
00365 if (jj != (L-1))
00366 dNon = -ron * (1 + eta) * drm.on[jj] + eta * drm.on[jj] + ron * (1 + eta) * drm.on[jj+1];
00367 else
00368 dNon = -ron * (1 + eta) * drm.on[jj] + eta * drm.on[jj] + roff * drm.off[jj];
00369
00370 if (jj != 0)
00371 dNoff = -roff * drm.off[jj] - eta * drm.on[jj] + hold_off * roff;
00372 else
00373 dNoff = -roff * drm.off[jj] - eta * drm.on[jj] + ron * (1 + eta) * drm.on[jj];
00374 }
00375
00376 hold_off = drm.off[jj];
00377
00378 drm.on[jj] = drm.on[jj] + dNon;
00379 N_on += drm.on[jj];
00380 drm.off[jj] = drm.off[jj] + dNoff;
00381 }
00382
00383 N_off = N - N_on;
00384 phi = roff / (ron + roff);
00385 nominal_power = base_power * N_on / N;
00386 double R = ron;
00387 int dt = 0;
00388 if (ron < roff)
00389 R = roff;
00390 dt = int(1/R * 3600);
00391
00392 next_time = t1 + dt;
00393 }
00394
00395
00396 if (duty_cycle != -1)
00397 {
00398 double phase_shift = 0;
00399
00400 if (first_pass == 0)
00401 {
00402 phase_shift = (t1 - last_time) / (period * 3600);
00403 phase = phase + phase_shift;
00404 last_time = t1;
00405 }
00406 else
00407 first_pass = 0;
00408 last_time = t1;
00409
00410 if (this->re_override == OV_NORMAL)
00411 {
00412 if (phase >= 1)
00413 phase = 0;
00414
00415
00416 if (t1 >= next_time || last_duty_cycle != duty_cycle)
00417 {
00418 if (duty_cycle > phase)
00419 {
00420 multiplier = 1;
00421 next_time = t1 + (period * 3600) * (duty_cycle - phase) + 1;
00422 }
00423 else
00424 {
00425 multiplier = 0;
00426 next_time = t1 + (period * 3600) * (1 - phase) + 1;
00427 }
00428 }
00429 last_duty_cycle = duty_cycle;
00430 }
00431 else if (this->re_override == OV_OFF)
00432 {
00433 if (phase <= 1 && t1>=next_time)
00434 {
00435 this->re_override = OV_NORMAL;
00436 next_time = t1;
00437 }
00438 else if (t1 >= next_time || next_time == TS_NEVER)
00439 {
00440 if (recovery_duty_cycle > fmod(phase,1))
00441 {
00442 multiplier = 1;
00443
00444 next_time = t1 + (period * 3600) * (recovery_duty_cycle - fmod(phase,1)) + 1;
00445 if (duty_cycle != 0.0)
00446 phase -= 1 * recovery_duty_cycle / duty_cycle * (1 - fmod(phase,1));
00447 else
00448 phase = 0;
00449
00450
00451
00452
00453
00454
00455 }
00456 else
00457 {
00458
00459
00460
00461
00462
00463
00464
00465 multiplier = 0;
00466 next_time = t1 + (period * 3600) * (1 - fmod(phase,1)) + 1;
00467 }
00468 }
00469 last_duty_cycle = duty_cycle;
00470
00471 }
00472 else
00473 {
00474 if (multiplier == 1 && duty_cycle > phase)
00475 {
00476
00477 last_duty_cycle = duty_cycle;
00478 }
00479 else
00480 {
00481 multiplier = 0;
00482 next_time = TS_NEVER;
00483 if (recovery_duty_cycle > 0)
00484 last_duty_cycle = duty_cycle;
00485 else
00486 {
00487 if (phase >= 1)
00488 phase -= 1;
00489 last_duty_cycle = 0;
00490 }
00491 }
00492 }
00493
00494 }
00495
00496 if (pCircuit!=NULL){
00497
00498 temp_voltage_magnitude = (pCircuit->pV->get_complex()).Mag();
00499
00500 if (is_240)
00501 {
00502 load.voltage_factor = temp_voltage_magnitude / (2.0 * default_line_voltage);
00503 }
00504 else
00505 {
00506 load.voltage_factor = temp_voltage_magnitude / default_line_voltage;
00507 }
00508 }
00509
00510 t2 = residential_enduse::sync(t0,t1);
00511
00512 if (pCircuit->status==BRK_CLOSED)
00513 {
00514
00515 double demand_power = multiplier * base_power;
00516
00517 if (demand_response_mode == true)
00518 demand_power = nominal_power;
00519
00520 if (heatgain_only == false)
00521 {
00522
00523 real_power = demand_power * load.power_fraction;
00524
00525 imag_power = (power_pf == 0.0) ? 0.0 : real_power * sqrt(1.0/(power_pf * power_pf) - 1.0);
00526
00527 if (power_pf < 0)
00528 {
00529 imag_power *= -1.0;
00530 }
00531
00532 load.power.SetRect(real_power,imag_power);
00533
00534
00535 real_power = demand_power * load.current_fraction;
00536
00537 imag_power = (current_pf == 0.0) ? 0.0 : real_power * sqrt(1.0/(current_pf * current_pf) - 1.0);
00538
00539 if (current_pf < 0)
00540 {
00541 imag_power *= -1.0;
00542 }
00543
00544 load.current.SetRect(real_power,imag_power);
00545
00546
00547 real_power = demand_power * load.impedance_fraction;
00548
00549 imag_power = (impedance_pf == 0.0) ? 0.0 : real_power * sqrt(1.0/(impedance_pf * impedance_pf) - 1.0);
00550
00551 if (impedance_pf < 0)
00552 {
00553 imag_power *= -1.0;
00554 }
00555
00556 load.admittance.SetRect(real_power,imag_power);
00557
00558
00559 load.total = load.power + load.current + load.admittance;
00560 actual_power = load.power + load.current * load.voltage_factor + load.admittance * load.voltage_factor * load.voltage_factor;
00561
00562
00563 angleval = load.total.Arg();
00564 load.power_factor = (angleval < 0) ? -1.0 * cos(angleval) : cos(angleval);
00565
00566
00567 load.heatgain = load.total.Re() * load.heatgain_fraction * BTUPHPKW;
00568 }
00569 else
00570 {
00571 load.power = load.current = load.admittance = actual_power = load.total = 0.0;
00572 load.heatgain = demand_power * BTUPHPKW;
00573 return TS_NEVER;
00574 }
00575 }
00576 else
00577 {
00578 load.total = 0.0;
00579 load.power = 0.0;
00580 load.current = 0.0;
00581 load.admittance = 0.0;
00582 load.heatgain = 0.0;
00583 load.power_factor = 0.0;
00584 }
00585
00586 if (next_time < t2 && next_time > 0)
00587 t2 = next_time;
00588 return t2;
00589 }
00590
00592
00594
00595 EXPORT int create_ZIPload(OBJECT **obj, OBJECT *parent)
00596 {
00597 try
00598 {
00599 *obj = gl_create_object(ZIPload::oclass);
00600 if (*obj!=NULL)
00601 {
00602 ZIPload *my = OBJECTDATA(*obj,ZIPload);;
00603 gl_set_parent(*obj,parent);
00604 my->create();
00605 return 1;
00606 }
00607 else
00608 return 0;
00609 }
00610 CREATE_CATCHALL(ZIPload);
00611 }
00612
00613 EXPORT int init_ZIPload(OBJECT *obj)
00614 {
00615 try
00616 {
00617 ZIPload *my = OBJECTDATA(obj,ZIPload);
00618 return my->init(obj->parent);
00619 }
00620 INIT_CATCHALL(ZIPload);
00621 }
00622
00623 EXPORT int isa_ZIPload(OBJECT *obj, char *classname)
00624 {
00625 if(obj != 0 && classname != 0){
00626 return OBJECTDATA(obj,ZIPload)->isa(classname);
00627 } else {
00628 return 0;
00629 }
00630 }
00631
00632
00633 EXPORT TIMESTAMP sync_ZIPload(OBJECT *obj, TIMESTAMP t0)
00634 {
00635 try
00636 {
00637 ZIPload *my = OBJECTDATA(obj, ZIPload);
00638 TIMESTAMP t1 = my->sync(obj->clock, t0);
00639 obj->clock = t0;
00640 return t1;
00641 }
00642 SYNC_CATCHALL(ZIPload);
00643 }
00644