00001
00014 #include <stdlib.h>
00015 #include <stdio.h>
00016 #include <errno.h>
00017 #include <math.h>
00018 #include "residential.h"
00019 #include "house_e.h"
00020 #include "thermal_storage.h"
00021
00023
00025 struct s_thermal_default_schedule_list {
00026 char *schedule_name;
00027 char *schedule_definition;
00028 } thermal_default_schedule_list[] =
00029 {
00030 { "thermal_storage_discharge_default",
00031 "dischargetime {"
00032 "* 0-10 * * * 0.0;"
00033 "* 11-20 * * * 1.0;"
00034 "* 21-23 * * * 0.0;"
00035 "}"
00036 },
00037 { "thermal_storage_charge_default",
00038 "chargetime {"
00039 "* 0-10 * * * 1.0;"
00040 "* 11-20 * * * 0.0;"
00041 "* 21-23 * * * 1.0;"
00042 "}"
00043 }
00044 };
00045
00047
00049 CLASS* thermal_storage::oclass = NULL;
00050 CLASS* thermal_storage::pclass = NULL;
00051
00052
00053 thermal_storage::thermal_storage(MODULE *mod)
00054 : residential_enduse(mod)
00055 {
00056
00057 if (oclass==NULL)
00058 {
00059 pclass = residential_enduse::oclass;
00060
00061
00062 oclass = gl_register_class(mod,"thermal_storage",sizeof(thermal_storage),PC_BOTTOMUP|PC_AUTOLOCK);
00063 if (oclass==NULL)
00064 GL_THROW("unable to register class thermal_storage");
00065
00066
00067
00068
00069
00070
00071 if (gl_publish_variable(oclass,
00072 PT_INHERIT, "residential_enduse",
00073 PT_double, "total_capacity[Btu]", PADDR(total_capacity), PT_DESCRIPTION, "total storage capacity of unit",
00074 PT_double, "stored_capacity[Btu]", PADDR(stored_capacity), PT_DESCRIPTION, "amount of capacity that is stored",
00075 PT_double,"recharge_power[kW]",PADDR(recharge_power), PT_DESCRIPTION, "installed compressor power usage",
00076 PT_double,"discharge_power[kW]",PADDR(discharge_power), PT_DESCRIPTION, "installed pump power usage",
00077 PT_double,"recharge_pf",PADDR(recharge_power_factor), PT_DESCRIPTION, "installed compressor power factor",
00078 PT_double,"discharge_pf",PADDR(discharge_power_factor), PT_DESCRIPTION, "installed pump power factor",
00079 PT_enumeration, "discharge_schedule_type", PADDR(discharge_schedule_type),PT_DESCRIPTION,"Scheduling method for discharging",
00080 PT_KEYWORD, "INTERNAL", (enumeration)INTERNAL,
00081 PT_KEYWORD, "EXTERNAL", (enumeration)EXTERNAL,
00082 PT_enumeration, "recharge_schedule_type", PADDR(recharge_schedule_type),PT_DESCRIPTION,"Scheduling method for charging",
00083 PT_KEYWORD, "INTERNAL", (enumeration)INTERNAL,
00084 PT_KEYWORD, "EXTERNAL", (enumeration)EXTERNAL,
00085 PT_double, "recharge_time", PADDR(recharge_time), PT_DESCRIPTION, "Flag indicating if recharging is available at the current time (1 or 0)",
00086 PT_double, "discharge_time", PADDR(discharge_time), PT_DESCRIPTION, "Flag indicating if discharging is available at the current time (1 or 0)",
00087 PT_double, "discharge_rate[Btu/h]", PADDR(discharge_rate), PT_DESCRIPTION, "rating of discharge or cooling",
00088 PT_double, "SOC[%]", PADDR(state_of_charge), PT_DESCRIPTION, "state of charge as percentage of total capacity",
00089 PT_double, "k[W/m/K]", PADDR(k), PT_DESCRIPTION, "coefficient of thermal conductivity (W/m/K)",
00090 NULL)<1) GL_THROW("unable to publish properties in %s",__FILE__);
00091
00092
00093
00094
00095 }
00096 }
00097
00098
00099 int thermal_storage::create(void)
00100 {
00101 int res = residential_enduse::create();
00102
00103
00104 load.name = oclass->name;
00105 load.power = 0;
00106 load.power_factor = 1;
00107 load.heatgain = 0;
00108
00109 total_capacity = 0;
00110 stored_capacity = -1;
00111 recharge_power = 0;
00112 discharge_power = 0;
00113 recharge_power_factor = 0;
00114 discharge_power_factor = 0;
00115 recharge_time = 0;
00116 discharge_time = 0;
00117 discharge_rate = 0;
00118 state_of_charge = -1;
00119 k = -1;
00120
00121
00122 thermal_storage_available = NULL;
00123 thermal_storage_active = NULL;
00124
00125
00126 recharge_time_ptr = NULL;
00127 discharge_time_ptr = NULL;
00128
00129
00130 recharge_schedule_vals = NULL;
00131 discharge_schedule_vals = NULL;
00132
00133
00134 discharge_schedule_type=INTERNAL;
00135 recharge_schedule_type=INTERNAL;
00136
00137 return res;
00138 }
00139
00140 int thermal_storage::init(OBJECT *parent)
00141 {
00142 if(parent != NULL){
00143 if((parent->flags & OF_INIT) != OF_INIT){
00144 char objname[256];
00145 gl_verbose("thermal_storage::init(): deferring initialization on %s", gl_name(parent, objname, 255));
00146 return 2;
00147 }
00148 }
00149 OBJECT *hdr = OBJECTHDR(this);
00150 hdr->flags |= OF_SKIPSAFE;
00151 gld_property *design_cooling_capacity_prop;
00152 double design_cooling_capacity;
00153
00154
00155 if (!(gl_object_isa(parent,"house","residential")))
00156 {
00157 GL_THROW("thermal_storage:%s must be parented to a house!",hdr->name);
00158
00159
00160
00161 }
00162
00163
00164
00165
00166 design_cooling_capacity_prop = new gld_property(parent,"design_cooling_capacity");
00167
00168
00169 if ((design_cooling_capacity_prop->is_valid() != true) || (design_cooling_capacity_prop->is_double() != true))
00170 {
00171 GL_THROW("thermal_storage:%d - %s - Unable to map house interface property",hdr->id,(hdr->name ? hdr->name : "Unnamed"));
00172
00173
00174
00175
00176 }
00177
00178
00179 outside_temperature = new gld_property(parent,"outdoor_temperature");
00180
00181
00182 if ((outside_temperature->is_valid() != true) || (outside_temperature->is_double() != true))
00183 {
00184 GL_THROW("thermal_storage:%d - %s - Unable to map house interface property",hdr->id,(hdr->name ? hdr->name : "Unnamed"));
00185
00186 }
00187
00188
00189 thermal_storage_available = new gld_property(parent,"thermal_storage_present");
00190
00191
00192 if ((thermal_storage_available->is_valid() != true) || (thermal_storage_available->is_bool() != true))
00193 {
00194 GL_THROW("thermal_storage:%d - %s - Unable to map house interface property",hdr->id,(hdr->name ? hdr->name : "Unnamed"));
00195
00196 }
00197
00198
00199 thermal_storage_active = new gld_property(parent,"thermal_storage_in_use");
00200
00201
00202 if ((thermal_storage_active->is_valid() != true) || (thermal_storage_active->is_bool() != true))
00203 {
00204 GL_THROW("thermal_storage:%d - %s - Unable to map house interface property",hdr->id,(hdr->name ? hdr->name : "Unnamed"));
00205
00206 }
00207
00208
00209 design_cooling_capacity = design_cooling_capacity_prop->get_double();
00210
00211
00212 if (design_cooling_capacity == 0)
00213 {
00214 gl_warning("\'design_cooling_capacity\' not specified in parent ~ default to 5 ton or 60,000 Btu/hr");
00215
00216
00217
00218
00219 discharge_rate = 5 * 12000;
00220 water_capacity = 1.7413;
00221 } else {
00222 discharge_rate = design_cooling_capacity;
00223 water_capacity = 1.7413 * (discharge_rate / (5 * 12000));
00224 }
00225
00226
00227 delete design_cooling_capacity_prop;
00228
00229 surface_area = 6 * pow(water_capacity, 0.6667);
00230
00231 if (total_capacity == 0) total_capacity = (30 / 5) * discharge_rate;
00232
00233 if (state_of_charge < 0 && stored_capacity < 0)
00234 {
00235 stored_capacity = total_capacity;
00236 state_of_charge = 100;
00237 } else if (state_of_charge < 0 && stored_capacity >= 0)
00238 {
00239 state_of_charge = stored_capacity / total_capacity;
00240 } else if (state_of_charge >= 0 && stored_capacity < 0)
00241 {
00242 stored_capacity = (state_of_charge / 100) * total_capacity;
00243 } else if (state_of_charge >= 0 && stored_capacity >= 0)
00244 {
00245 stored_capacity = (state_of_charge / 100) * total_capacity;
00246 gl_warning("stored_capacity and SOC are both defined, SOC being used for initial energy state");
00247
00248
00249
00250
00251
00252 }
00253
00254 if (recharge_power == 0) recharge_power = (3.360 * discharge_rate) / (5 * 12000);
00255 if (discharge_power == 0) discharge_power = (0.300 * discharge_rate) / (5 * 12000);
00256 if (recharge_power_factor == 0) recharge_power_factor = 0.97;
00257 if (discharge_power_factor == 0) discharge_power_factor = 1;
00258 if (k < 0) k = 0;
00259 k = k * 0.00052667;
00260
00261
00262 if (recharge_schedule_type==INTERNAL)
00263 {
00264
00265 recharge_schedule_vals = gl_schedule_find(thermal_default_schedule_list[1].schedule_name);
00266
00267
00268 if (recharge_schedule_vals == NULL)
00269 {
00270
00271 recharge_schedule_vals = gl_schedule_create(thermal_default_schedule_list[1].schedule_name,thermal_default_schedule_list[1].schedule_definition);
00272
00273
00274 if (recharge_schedule_vals==NULL)
00275 {
00276 GL_THROW("Failure to create default charging schedule");
00277
00278
00279
00280
00281 }
00282 }
00283
00284 gl_verbose("thermal_storage charging defaulting to internal schedule");
00285
00286
00287
00288
00289
00290
00291 recharge_time_ptr = &recharge_schedule_vals->value;
00292 }
00293 else
00294 {
00295
00296 recharge_time_ptr = &recharge_time;
00297 }
00298
00299
00300 if (discharge_schedule_type==INTERNAL)
00301 {
00302
00303 discharge_schedule_vals = gl_schedule_find(thermal_default_schedule_list[0].schedule_name);
00304
00305
00306 if (discharge_schedule_vals == NULL)
00307 {
00308
00309 discharge_schedule_vals = gl_schedule_create(thermal_default_schedule_list[0].schedule_name,thermal_default_schedule_list[0].schedule_definition);
00310
00311
00312 if (discharge_schedule_vals==NULL)
00313 {
00314 GL_THROW("Failure to create default discharging schedule");
00315
00316
00317
00318
00319 }
00320 }
00321
00322 gl_verbose("thermal_storage discharging defaulting to internal schedule");
00323
00324
00325
00326
00327
00328
00329 discharge_time_ptr = &discharge_schedule_vals->value;
00330 }
00331 else
00332 {
00333
00334 discharge_time_ptr = &discharge_time;
00335 }
00336
00337
00338 return residential_enduse::init(parent);
00339 }
00340
00341 int thermal_storage::isa(char *classname)
00342 {
00343 return (strcmp(classname,"thermal_storage")==0 || residential_enduse::isa(classname));
00344 }
00345
00346 TIMESTAMP thermal_storage::sync(TIMESTAMP t0, TIMESTAMP t1)
00347 {
00348 double val = 0.0;
00349 TIMESTAMP t2 = TS_NEVER;
00350 next_timestep = TS_NEVER;
00351 double actual_recharge_power, outside_temperature_val;
00352 gld_wlock *test_rlock;
00353 bool temp_bool_val;
00354
00355
00356 if (t0 != 0)
00357 {
00358
00359 outside_temperature_val = outside_temperature->get_double();
00360
00361 if (*recharge_time_ptr == 1 && *discharge_time_ptr == 1)
00362 {
00363 gl_warning("recharge and discharge can not both be scheduled to be concurrently on ~ defaulting to recharge");
00364
00365
00366
00367
00368
00369 *discharge_time_ptr = 0;
00370 }
00371
00372 if (*recharge_time_ptr == 1 && *discharge_time_ptr != 1 && state_of_charge < 100)
00373 {
00374 if (recharge == 0) last_timestep = t0;
00375 recharge = 1;
00376 } else {
00377 recharge = 0;
00378 }
00379
00380
00381
00382
00383
00384
00385
00386
00387 if (stored_capacity > 0)
00388 {
00389 surface_area = 6 * pow(water_capacity, 0.6667);
00390 stored_capacity = stored_capacity + ((k * surface_area * (32 - outside_temperature_val) / 0.05) * (t0 - last_timestep));
00391 state_of_charge = stored_capacity / total_capacity * 100;
00392 if (stored_capacity < 0)
00393 {
00394 stored_capacity = 0;
00395 state_of_charge = 0;
00396 }
00397 if (stored_capacity >= total_capacity)
00398 {
00399 stored_capacity = total_capacity;
00400 state_of_charge = 100;
00401 }
00402 }
00403
00404
00405 actual_recharge_power = recharge_power * (1 + (75 - outside_temperature_val) * 0.0106);
00406 if (recharge && *recharge_time_ptr == 1 && outside_temperature_val >= 15 && outside_temperature_val <=115)
00407
00408 {
00409
00410 temp_bool_val = false;
00411 thermal_storage_available->setp<bool>(temp_bool_val,*test_rlock);
00412
00413 actual_recharge_power = recharge_power * (1 + (75 - outside_temperature_val) * 0.0106);
00414 if (last_timestep != t0)
00415 {
00416 stored_capacity = stored_capacity + (((total_capacity / 30) / (9 * outside_temperature_val + 705)) * (t0 - last_timestep));
00417 }
00418 if (stored_capacity >= total_capacity)
00419 {
00420 recharge = 0;
00421 stored_capacity = total_capacity;
00422 state_of_charge = 100;
00423 load.power = 0;
00424 load.power_factor = recharge_power_factor;
00425 load.heatgain = 0;
00426 } else
00427 {
00428 recharge = 1;
00429 state_of_charge = stored_capacity / total_capacity * 100;
00430 next_timestep = (TIMESTAMP)((total_capacity - stored_capacity) / ((total_capacity / 30) / (9 * outside_temperature_val + 705)));
00431 if (next_timestep == 0) next_timestep = 1;
00432 load.power = actual_recharge_power;
00433 load.power_factor = recharge_power_factor;
00434 load.heatgain = 0;
00435 }
00436 }
00437
00438 if (*recharge_time_ptr != 1 && *discharge_time_ptr == 1 && state_of_charge > 0 && outside_temperature_val >=15)
00439
00440 {
00441 thermal_storage_active->getp<bool>(temp_bool_val,*test_rlock);
00442 if (temp_bool_val == false) last_timestep = t0;
00443
00444
00445 temp_bool_val = true;
00446 thermal_storage_available->setp<bool>(temp_bool_val,*test_rlock);
00447
00448 } else {
00449 temp_bool_val = false;
00450 thermal_storage_available->setp<bool>(temp_bool_val,*test_rlock);
00451 }
00452
00453
00454 thermal_storage_active->getp<bool>(temp_bool_val,*test_rlock);
00455
00456
00457 if (recharge == 0 && *discharge_time_ptr == 1 && (temp_bool_val == true))
00458 {
00459 if (stored_capacity > 0)
00460 {
00461
00462 temp_bool_val = true;
00463 thermal_storage_available->setp<bool>(temp_bool_val,*test_rlock);
00464
00465 if (last_timestep != t0)
00466 {
00467 stored_capacity = stored_capacity - (discharge_rate * (t0 - last_timestep) / 3600);
00468 }
00469 state_of_charge = stored_capacity / total_capacity * 100;
00470 next_timestep = (TIMESTAMP)((stored_capacity / discharge_rate) * 3600);
00471 if (next_timestep == 0) next_timestep = 1;
00472 load.power = discharge_power;
00473 load.power_factor = discharge_power_factor;
00474 load.heatgain = -discharge_rate;
00475 } else
00476 {
00477
00478 temp_bool_val = false;
00479 thermal_storage_available->setp<bool>(temp_bool_val,*test_rlock);
00480
00481 state_of_charge = 0;
00482 load.power = 0;
00483 load.power_factor = discharge_power_factor;
00484 load.heatgain = 0;
00485 }
00486 if (stored_capacity <= 0) stored_capacity = 0;
00487 } else {
00488 load.heatgain = 0;
00489 if (*discharge_time_ptr == 1 && *recharge_time_ptr != 1)
00490 {
00491 load.power = 0;
00492 load.power_factor = 1;
00493 }
00494 }
00495
00496 t2 = residential_enduse::sync(t0,t1);
00497 if (t2 > t0 + next_timestep) t2 = t0 + next_timestep;
00498
00499 last_timestep = t0;
00500 }
00501 else
00502 last_timestep = t1;
00503
00504 return t2;
00505 }
00506
00508
00510
00511 EXPORT int create_thermal_storage(OBJECT **obj, OBJECT *parent)
00512 {
00513 *obj = gl_create_object(thermal_storage::oclass);
00514 if (*obj!=NULL)
00515 {
00516 thermal_storage *my = OBJECTDATA(*obj,thermal_storage);
00517 gl_set_parent(*obj,parent);
00518 try {
00519 my->create();
00520 }
00521 catch (char *msg)
00522 {
00523 gl_error("%s::%s.create(OBJECT **obj={name='%s', id=%d},...): %s", (*obj)->oclass->module->name, (*obj)->oclass->name, (*obj)->name, (*obj)->id, msg);
00524
00525
00526
00527
00528 return 0;
00529 }
00530 return 1;
00531 }
00532 return 0;
00533 }
00534
00535 EXPORT int init_thermal_storage(OBJECT *obj)
00536 {
00537 thermal_storage *my = OBJECTDATA(obj,thermal_storage);
00538 try {
00539 return my->init(obj->parent);
00540 }
00541 catch (char *msg)
00542 {
00543 gl_error("%s::%s.init(OBJECT *obj={name='%s', id=%d}): %s", obj->oclass->module->name, obj->oclass->name, obj->name, obj->id, msg);
00544
00545
00546
00547
00548 return 0;
00549 }
00550 }
00551
00552 EXPORT int isa_thermal_storage(OBJECT *obj, char *classname)
00553 {
00554 if(obj != 0 && classname != 0){
00555 return OBJECTDATA(obj,thermal_storage)->isa(classname);
00556 } else {
00557 return 0;
00558 }
00559 }
00560
00561 EXPORT TIMESTAMP sync_thermal_storage(OBJECT *obj, TIMESTAMP t1)
00562 {
00563 thermal_storage *my = OBJECTDATA(obj,thermal_storage);
00564 try {
00565 TIMESTAMP t2 = my->sync(obj->clock, t1);
00566 obj->clock = t1;
00567 return t2;
00568 }
00569 catch (char *msg)
00570 {
00571 DATETIME dt;
00572 char ts[64];
00573 gl_localtime(t1,&dt);
00574 gl_strtime(&dt,ts,sizeof(ts));
00575 gl_error("%s::%s.init(OBJECT **obj={name='%s', id=%d},TIMESTAMP t1='%s'): %s", obj->oclass->module->name, obj->oclass->name, obj->name, obj->id, ts, msg);
00576
00577
00578
00579
00580 return 0;
00581 }
00582 }
00583