00001
00022 #include <stdlib.h>
00023 #include <stdio.h>
00024 #include <errno.h>
00025 #include <math.h>
00026
00027 #include "waterheater.h"
00028
00029 #define TSTAT_PRECISION 0.01
00030 #define HEIGHT_PRECISION 0.01
00031
00032
00033
00034
00035
00036
00037
00038
00039
00040
00041
00042
00043
00044
00045
00046
00047
00048
00049
00050
00051
00052
00053
00054
00055
00056
00057
00058
00059
00060
00061
00062
00063
00064
00065
00066
00067
00068
00069
00070
00071
00072
00073
00074
00075
00076
00077
00078
00080
00082 CLASS* waterheater::oclass = NULL;
00083 CLASS* waterheater::pclass = NULL;
00084
00087 waterheater::waterheater(MODULE *module) : residential_enduse(module){
00088
00089 if (oclass==NULL)
00090 {
00091 pclass = residential_enduse::oclass;
00092
00093 oclass = gl_register_class(module,"waterheater",sizeof(waterheater),PC_PRETOPDOWN|PC_BOTTOMUP|PC_POSTTOPDOWN|PC_AUTOLOCK);
00094 if (oclass==NULL)
00095 GL_THROW("unable to register object class implemented by %s",__FILE__);
00096
00097
00098 if (gl_publish_variable(oclass,
00099 PT_INHERIT, "residential_enduse",
00100 PT_double,"tank_volume[gal]",PADDR(tank_volume), PT_DESCRIPTION, "the volume of water in the tank when it is full",
00101 PT_double,"tank_UA[Btu*h/degF]",PADDR(tank_UA), PT_DESCRIPTION, "the UA of the tank (surface area divided by R-value)",
00102 PT_double,"tank_diameter[ft]",PADDR(tank_diameter), PT_DESCRIPTION, "the diameter of the water heater tank",
00103 PT_double,"tank_height[ft]",PADDR(tank_height), PT_DESCRIPTION, "the height of the water heater tank",
00104 PT_double,"water_demand[gpm]",PADDR(water_demand), PT_DESCRIPTION, "the hot water draw from the water heater",
00105 PT_double,"heating_element_capacity[kW]",PADDR(heating_element_capacity), PT_DESCRIPTION, "the power of the heating element",
00106 PT_double,"inlet_water_temperature[degF]",PADDR(Tinlet), PT_DESCRIPTION, "the inlet temperature of the water tank",
00107 PT_enumeration,"waterheater_model",PADDR(current_model), PT_DESCRIPTION, "the water heater model to use",
00108 PT_KEYWORD,"ONEZNODE",(enumeration)ONENODE,
00109 PT_KEYWORD,"TWONODE",(enumeration)TWONODE,
00110 PT_KEYWORD,"FORTRAN",(enumeration)FORTRAN,
00111 PT_KEYWORD,"NONE",(enumeration)NONE,
00112 PT_enumeration,"heat_mode",PADDR(heat_mode), PT_DESCRIPTION, "the energy source for heating the water heater",
00113 PT_KEYWORD,"ELECTRIC",(enumeration)ELECTRIC,
00114 PT_KEYWORD,"GASHEAT",(enumeration)GASHEAT,
00115 PT_KEYWORD,"HEAT_PUMP",(enumeration)HEAT_PUMP,
00116 PT_enumeration,"location",PADDR(location), PT_DESCRIPTION, "whether the water heater is inside or outside",
00117 PT_KEYWORD,"INSIDE",(enumeration)INSIDE,
00118 PT_KEYWORD,"GARAGE",(enumeration)GARAGE,
00119 PT_double,"tank_setpoint[degF]",PADDR(tank_setpoint), PT_DESCRIPTION, "the temperature around which the water heater will heat its contents",
00120 PT_double,"thermostat_deadband[degF]",PADDR(thermostat_deadband), PT_DESCRIPTION, "the degree to heat the water tank, when needed",
00121 PT_double,"temperature[degF]",PADDR(Tw), PT_DESCRIPTION, "the outlet temperature of the water tank",
00122 PT_double,"height[ft]",PADDR(h), PT_DESCRIPTION, "the height of the hot water column within the water tank",
00123 PT_complex,"demand[kVA]",PADDR(load.total), PT_DESCRIPTION, "the water heater power consumption",
00124 PT_double,"actual_load[kW]",PADDR(actual_load),PT_DESCRIPTION, "the actual load based on the current voltage across the coils",
00125 PT_double,"previous_load[kW]",PADDR(prev_load),PT_DESCRIPTION, "the previous load based on voltage across the coils at the last sync operation",
00126 PT_complex,"actual_power[kVA]",PADDR(waterheater_actual_power), PT_DESCRIPTION, "the actual power based on the current voltage across the coils",
00127 PT_double,"is_waterheater_on",PADDR(is_waterheater_on),PT_DESCRIPTION, "simple logic output to determine state of waterheater (1-on, 0-off)",
00128 PT_double,"gas_fan_power[kW]",PADDR(gas_fan_power),PT_DESCRIPTION, "load of a running gas waterheater",
00129 PT_double,"gas_standby_power[kW]",PADDR(gas_standby_power),PT_DESCRIPTION, "load of a gas waterheater in standby",
00130 PT_double,"heat_pump_coefficient_of_performance[Btu/kWh]",PADDR(HP_COP),PT_DESCRIPTION, "current COP of the water heater pump - currently calculated internally and not an input",
00131 PT_double,"Tcontrol[degF]",PADDR(Tcontrol), PT_DESCRIPTION, "in heat pump operation, defines the blended temperature used for turning on and off HP - currently calculated internally and not an input",
00132 PT_enumeration,"current_tank_status",PADDR(current_tank_state),
00133 PT_KEYWORD,"FULL",(enumeration)FULL,
00134 PT_KEYWORD,"PARTIAL",(enumeration)PARTIAL,
00135 PT_KEYWORD,"EMPTY",(enumeration)EMPTY,
00136
00137 PT_double, "dr_signal", PADDR(dr_signal), PT_DESCRIPTION, "the on/off signal to send to the fortran waterheater model",
00138 PT_double, "COP", PADDR(fwh_cop_current), PT_DESCRIPTION, "the cop of the fortran heat pump water heater model.",
00139 PT_double, "operating_mode", PADDR(operating_mode), PT_DESCRIPTION, "the operating mode the fortran water heater should be using.",
00140 PT_double, "fortran_sim_time[s]", PADDR(simulation_time), PT_DESCRIPTION, "the amount of time the fortran model should simulate.",
00141 PT_double, "waterheater_power[kW]", PADDR(fwh_power_now), PT_DESCRIPTION, "the current power draw from the fortran water heater.",
00142 PT_enumeration,"load_state",PADDR(load_state),
00143 PT_KEYWORD,"DEPLETING",(enumeration)DEPLETING,
00144 PT_KEYWORD,"RECOVERING",(enumeration)RECOVERING,
00145 PT_KEYWORD,"STABLE",(enumeration)STABLE,
00146 PT_double,"actual_voltage",PADDR(actual_voltage),PT_ACCESS,PA_HIDDEN,
00147 PT_double,"nominal_voltage",PADDR(nominal_voltage),PT_ACCESS,PA_HIDDEN,
00148 PT_enumeration,"re_override",PADDR(re_override), PT_DESCRIPTION, "the override setting for the water heater",
00149 PT_KEYWORD,"OV_ON",(enumeration)OV_ON,
00150 PT_KEYWORD,"OV_NORMAL",(enumeration)OV_NORMAL,
00151 PT_KEYWORD,"OV_OFF",(enumeration)OV_OFF,
00152 NULL)<1)
00153 GL_THROW("unable to publish properties in %s",__FILE__);
00154 }
00155 }
00156
00157 waterheater::~waterheater()
00158 {
00159 }
00160
00161 int waterheater::create()
00162 {
00163 int res = residential_enduse::create();
00164
00165
00166 tank_volume = 0.0;
00167 tank_UA = 0.0;
00168 tank_diameter = 0;
00169 tank_height = 0;
00170 Tinlet = 60.0;
00171 water_demand = 0.0;
00172 heating_element_capacity = 0.0;
00173 heat_needed = FALSE;
00174 location = GARAGE;
00175 heat_mode = ELECTRIC;
00176 tank_setpoint = 0.0;
00177 thermostat_deadband = 0.0;
00178 is_waterheater_on = 0;
00179
00180 Tw = 0.0;
00181 prev_load = 0.0;
00182
00183
00184 location = gl_random_bernoulli(RNGSTATE,0.80) ? GARAGE : INSIDE;
00185
00186
00187 tank_setpoint = clip(gl_random_normal(RNGSTATE,130,10),100,160);
00188 thermostat_deadband = clip(gl_random_normal(RNGSTATE,5, 1),1,10);
00189
00190
00191 tank_setpoint = gl_random_normal(RNGSTATE,125,5);
00192 if (tank_setpoint<90) tank_setpoint = 90;
00193 if (tank_setpoint>160) tank_setpoint = 160;
00194
00195
00196 thermostat_deadband = fabs(gl_random_normal(RNGSTATE,2,1))+1;
00197 if (thermostat_deadband>10)
00198 thermostat_deadband = 10;
00199
00200 tank_UA = clip(gl_random_normal(RNGSTATE,2.0, 0.20),0.1,10) * tank_volume/50;
00201 if(tank_UA <= 1.0)
00202 tank_UA = 2.0;
00203
00204
00205 load.name = oclass->name;
00206
00207 load.breaker_amps = 30;
00208 load.config = EUC_IS220;
00209 load.power_fraction = 0.0;
00210 load.impedance_fraction = 1.0;
00211 load.heatgain_fraction = 0.0;
00212
00213 gas_fan_power = -1.0;
00214 gas_standby_power = -1.0;
00215
00216 dr_signal = 1;
00217 return res;
00218
00219 }
00220
00223 int waterheater::init(OBJECT *parent)
00224 {
00225 OBJECT *hdr = OBJECTHDR(this);
00226
00227 nominal_voltage = (2.0 * default_line_voltage);
00228 actual_voltage = nominal_voltage;
00229
00230 if(parent != NULL){
00231 if((parent->flags & OF_INIT) != OF_INIT){
00232 char objname[256];
00233 gl_verbose("waterheater::init(): deferring initialization on %s", gl_name(parent, objname, 255));
00234 return 2;
00235 }
00236 }
00237
00238 hdr->flags |= OF_SKIPSAFE;
00239
00240 static double sTair = 74;
00241 static double sTout = 68;
00242 static double sRH = 0.05;
00243
00244 if(current_model == FORTRAN){
00245 tank_setpoint = 126.05;
00246 thermostat_deadband = 4.05;
00247 }
00248
00249 if(parent){
00250 pTair = gl_get_double_by_name(parent, "air_temperature");
00251 pTout = gl_get_double_by_name(parent, "outdoor_temperature");
00252 pRH = gl_get_double_by_name(parent,"outdoor_rh");
00253 }
00254
00255 if(pTair == 0){
00256 pTair = &sTair;
00257 gl_warning("waterheater parent lacks \'air_temperature\' property, using default");
00258 }
00259 if(pTout == 0){
00260 pTout = &sTout;
00261 gl_warning("waterheater parent lacks \'outside_temperature\' property, using default");
00262 }
00263 if(pRH == 0){
00264 pRH = &sTout;
00265 gl_warning("waterheater parent lacks \'outside_temperature\' property, using default");
00266 }
00267
00268
00269
00270 if(tank_volume <= 0.0){
00271 if (tank_diameter <= 0) {
00272 if (tank_height <= 0) {
00273
00274 gl_warning( "waterheater::init() : tank volume, diameter, and height were not specified, defaulting to 50 gallons and 3.78 ft");
00275
00276 tank_volume = 50;
00277 tank_height = 3.782;
00278 tank_diameter = 2 * sqrt( tank_volume * (1/GALPCF) / (pi * tank_height) );
00279 area = (pi * pow(tank_diameter,2))/4;
00280 } else {
00281
00282 gl_warning( "waterheater::init() : tank volume and diameter were not specified, defaulting to 50 gallons");
00283
00284 tank_volume = 50;
00285 tank_diameter = 2 * sqrt( tank_volume * (1/GALPCF) / (pi * tank_height) );
00286 area = (pi * pow(tank_diameter,2))/4;
00287 }
00288 } else {
00289 if (tank_height <= 0) {
00290
00291 gl_warning( "waterheater::init() : tank volume and height were not specified, defaulting to 50 gallons");
00292
00293 tank_volume = 50;
00294 area = (pi * pow(tank_diameter,2))/4;
00295 tank_height = tank_volume/GALPCF / area;
00296 } else {
00297
00298 gl_verbose( "waterheater::init() : tank volume was not specified, calculating from height and diameter");
00299
00300 area = (pi * pow(tank_diameter,2))/4;
00301 tank_volume = area * tank_height * GALPCF;
00302 }
00303 }
00304 } else {
00305 if (tank_volume > 100.0 || tank_volume < 20.0){
00306 gl_error("watertank volume of %f outside the volume bounds of 20 to 100 gallons.", tank_volume);
00307
00308
00309
00310 }
00311
00312 if (tank_height <= 0) {
00313 if (tank_diameter <= 0) {
00314
00315 gl_warning( "waterheater::init() : height and diameter were not specified, defaulting to 3.78 ft");
00316
00317 tank_height = 3.782;
00318 tank_diameter = 2 * sqrt( tank_volume * (1/GALPCF) / (pi * tank_height) );
00319 area = (pi * pow(tank_diameter,2))/4;
00320 } else {
00321
00322 gl_verbose( "waterheater::init() : tank height was not specified, calculating from volume and diameter");
00323
00324 area = (pi * pow(tank_diameter,2))/4;
00325 tank_height = tank_volume/GALPCF / area;
00326 }
00327 } else {
00328 if (tank_diameter <= 0) {
00329
00330 gl_verbose( "waterheater::init() : diameter was not specified, calculating from volume and height");
00331
00332 tank_diameter = 2 * sqrt( tank_volume * (1/GALPCF) / (pi * tank_height) );
00333 area = (pi * pow(tank_diameter,2))/4;
00334 } else {
00335
00336 double temp_tank_diameter;
00337 temp_tank_diameter = 2 * sqrt( tank_volume * (1/GALPCF) / (pi * tank_height) );
00338
00339 if ( abs(temp_tank_diameter - tank_diameter) > 0.05 ) {
00340 gl_error( "waterheater::init() : tank volume, diameter, and height were all set, but did not agree with each other. Please unspecify one variable to be calculated.");
00341
00342
00343
00344
00345
00346 }
00347 else {
00348 area = (pi * pow(tank_diameter,2))/4;
00349 }
00350 }
00351 }
00352 }
00353
00354 Cw = tank_volume/GALPCF * RHOWATER * Cp;
00355
00356 if (height <= 0) {
00357 gl_verbose("waterheater::init() : setting initial height to tank height.");
00358 height = tank_height;
00359 } else if (height > tank_height) {
00360 gl_warning("waterheater::init() : height of water column was set to value greater than tank height. setting initial height to tank height.");
00361 height = tank_height;
00362 }
00363 if (tank_setpoint<90 || tank_setpoint>160)
00364 gl_error("watertank thermostat is set to %f and is outside the bounds of 90 to 160 degrees Fahrenheit (32.2 - 71.1 Celsius).", tank_setpoint);
00365
00366
00367
00368
00369
00370 if (thermostat_deadband>10 || thermostat_deadband < 0.0)
00371 GL_THROW("watertank deadband of %f is outside accepted bounds of 0 to 10 degrees (5.6 degC).", thermostat_deadband);
00372 if(current_model != FORTRAN){
00373
00374 if (tank_UA <= 0.0)
00375 GL_THROW("Tank UA value is negative.");
00376
00377
00378
00379 if (heating_element_capacity <= 0.0)
00380 {
00381 if (heat_mode == HEAT_PUMP) {
00382 heating_element_capacity = 1;
00383 } else if (tank_volume >= 50)
00384 heating_element_capacity = 4.500;
00385 else {
00386
00387 double randVal = gl_random_uniform(RNGSTATE,0,1);
00388 if (randVal < 0.33)
00389 heating_element_capacity = 3.200;
00390 else if (randVal < 0.67)
00391 heating_element_capacity = 3.500;
00392 else
00393 heating_element_capacity = 4.500;
00394 }
00395 }
00396
00397
00398 if(0 > gas_fan_power){
00399 gas_fan_power = heating_element_capacity * 0.01;
00400 }
00401
00402 if(0 > gas_standby_power){
00403 gas_standby_power = 0.0;
00404 }
00405 }
00406
00407
00408 if(Tw < Tinlet){
00409 Tw = gl_random_uniform(RNGSTATE,tank_setpoint - thermostat_deadband, tank_setpoint + thermostat_deadband);
00410 }
00411
00412 if(current_model != FORTRAN){
00413 current_model = NONE;
00414 load_state = STABLE;
00415
00416
00417 Tset_curtail = tank_setpoint - thermostat_deadband/2 - 10;
00418
00419
00420
00421 h = height;
00422
00423
00424 if(h == 0){
00425
00426 Tlower = Tinlet;
00427 Tupper = Tinlet + TSTAT_PRECISION;
00428 } else {
00429 Tlower = Tinlet;
00430 }
00431 }
00432 Tcontrol = Tw;
00433
00434 switch(shape.type){
00435 case MT_UNKNOWN:
00436
00437 break;
00438 case MT_ANALOG:
00439 if(shape.params.analog.energy == 0.0){
00440 GL_THROW("waterheater does not support fixed energy shaping");
00441
00442
00443
00444
00445
00446
00447
00448 } else if (shape.params.analog.power == 0){
00449
00450
00451
00452
00453
00454
00455 water_demand = gl_get_loadshape_value(&shape) / 2.4449;
00456 } else {
00457 water_demand = gl_get_loadshape_value(&shape);
00458 }
00459 break;
00460 case MT_PULSED:
00461
00462
00463 if(shape.params.pulsed.pulsetype == MPT_TIME){
00464 ;
00465 } else if(shape.params.pulsed.pulsetype == MPT_POWER){
00466 ;
00467 water_demand = gl_get_loadshape_value(&shape) / 2.4449;
00468 }
00469 break;
00470 case MT_MODULATED:
00471 if(shape.params.modulated.pulsetype == MPT_TIME){
00472 GL_THROW("Amplitude modulated water usage is nonsensical for residential water heaters");
00473
00474
00475
00476
00477 } else if(shape.params.modulated.pulsetype == MPT_POWER){
00478
00479
00480 water_demand = gl_get_loadshape_value(&shape) / 2.4449;
00481 }
00482 break;
00483 case MT_QUEUED:
00484 if(shape.params.queued.pulsetype == MPT_TIME){
00485 ;
00486 } else if(shape.params.queued.pulsetype == MPT_POWER){
00487 ;
00488 water_demand = gl_get_loadshape_value(&shape) / 2.4449;
00489 }
00490 break;
00491 default:
00492 GL_THROW("waterheater load shape has an unknown state!");
00493 break;
00494 }
00495
00496 if(current_model == FORTRAN){
00497 if(simulation_time <= 0){
00498 GL_THROW("The simulation time for the fortran water heater model must be greater than 0.");
00499 }
00500 fwh_sim_time = gl_globalclock;
00501 ncomp = 0;
00502 nheat[0] = nheat[1] = 0;
00503 heat_up = 0;
00504 fwh_energy = 0.0;
00505
00506 if(tank_volume == 40){
00507 thermal_conductivity = 1.80;
00508 convective_coefficient = 0.0024;
00509 water_density = 1000.0;
00510 water_heat_capacity = 4181.3;
00511 h = 1.1;
00512 tank_diameter = 0.3568;
00513 sensor_position[0] = 0.92;
00514 sensor_position[1] = 0.4;
00515 heater_element_power[0] = 4200.0;
00516 heater_element_power[1] = 2000.0;
00517 heater_size[0] = heater_size[1] = 0.01;
00518 heater_element_position[0] = heater_element_position[1] = 0.2;
00519 tank_heat_loss_rate = 3.5;
00520 temp_set[0] = temp_set[1] = 51.37;
00521 thermostat_deadband = 0.75;
00522 inlet_water_flow_threshold = 0.0;
00523 compressor_power_capacity = 750.0;
00524 compressor_activation_temp_offset = 9.0;
00525 lowest_ambient_temperature_limit = 45.0;
00526 highest_ambient_temperature_limit = 109.0;
00527 lowest_water_temperature_limit = 58.0;
00528 activation_temperature_offset = 5.0;
00529 ambient_air_dry_bulb_temp = 67.5;
00530 ambient_air_wet_bulb_temp = 57.0;
00531 upper_element_activation_temp_offset = 18.0;
00532 upper_fraction = 0.83;
00533 lower_fraction = 0.20;
00534 coarse_tank_grid = 12;
00535 fine_tank_grid = 12;
00536 } else if(tank_volume == 80){
00537 if(operating_mode == 3){
00538 thermal_conductivity = 0.58;
00539 convective_coefficient = 0.0024;
00540 water_density = 1000;
00541 water_heat_capacity = 4181.3;
00542 h = 1.524;
00543 tank_diameter = 0.508;
00544 sensor_position[0] = 0.92;
00545 sensor_position[1] = 0.4;
00546 heater_element_power[0] = 3900.0;
00547 heater_element_power[1] = 3900.0;
00548 heater_size[0] = 0.01;
00549 heater_size[1] = 0.01;
00550 heater_element_position[0] = 1.143;
00551 heater_element_position[1] = 0.254;
00552 tank_heat_loss_rate = 3.35;
00553 temp_set[0] = temp_set[1] = 51.67;
00554 thermostat_deadband = 0.75;
00555 inlet_water_flow_threshold = 0.0;
00556 compressor_power_capacity = 750.0;
00557 compressor_activation_temp_offset = 9.0;
00558 lowest_ambient_temperature_limit = 45.0;
00559 highest_ambient_temperature_limit = 109.0;
00560 lowest_water_temperature_limit = 58.0;
00561 activation_temperature_offset = 5.0;
00562 ambient_air_dry_bulb_temp = 67.5;
00563 ambient_air_wet_bulb_temp = 57.0;
00564 upper_element_activation_temp_offset = 18.0;
00565 upper_fraction = 0.83;
00566 lower_fraction = 0.20;
00567 coarse_tank_grid = 12;
00568 fine_tank_grid = 12;
00569 } else {
00570 thermal_conductivity = 0.58;
00571 convective_coefficient = 0.0024;
00572 water_density = 1000;
00573 water_heat_capacity = 4181.3;
00574 h = 1.4732;
00575 tank_diameter = 0.5;
00576 sensor_position[0] = 1.2277;
00577 sensor_position[1] = 0.4911;
00578 heater_element_power[0] = 4200.0;
00579 heater_element_power[1] = 2000.0;
00580 heater_size[0] = 0.0106;
00581 heater_size[1] = 0.01;
00582 heater_element_position[0] = heater_element_position[1] = 0.2;
00583 tank_heat_loss_rate = 3.9;
00584 temp_set[0] = temp_set[1] = 51.67;
00585 thermostat_deadband = 2.25;
00586 inlet_water_flow_threshold = 0.0;
00587 compressor_power_capacity = 750.0;
00588 compressor_activation_temp_offset = 9.0;
00589 lowest_ambient_temperature_limit = 45.0;
00590 highest_ambient_temperature_limit = 109.0;
00591 lowest_water_temperature_limit = 58.0;
00592 activation_temperature_offset = 5.0;
00593 ambient_air_dry_bulb_temp = 67.5;
00594 ambient_air_wet_bulb_temp = 57.0;
00595 upper_element_activation_temp_offset = 18.0;
00596 upper_fraction = 0.83;
00597 lower_fraction = 0.20;
00598 coarse_tank_grid = 12;
00599 fine_tank_grid = 12;
00600 }
00601 for( int i = 0; i < coarse_tank_grid*fine_tank_grid; i++){
00602 init_tank_temp[i] = (Tw - 32.0) * (5.0 / 9.0);
00603 tank_water_temp[i] = init_tank_temp[i];
00604 }
00605 } else {
00606 GL_THROW("Invalide tank volume for the fortran water heater_model. Valid volumes are 40 or 80 gallons.");
00607 }
00608 }
00609 return residential_enduse::init(parent);
00610 }
00611
00612 int waterheater::isa(char *classname)
00613 {
00614 return (strcmp(classname,"waterheater")==0 || residential_enduse::isa(classname));
00615 }
00616
00617
00618 void waterheater::thermostat(TIMESTAMP t0, TIMESTAMP t1){
00619 Ton = tank_setpoint - thermostat_deadband/2;
00620 Toff = tank_setpoint + thermostat_deadband/2;
00621
00622 enumeration tank_status = tank_state();
00623 switch(tank_status){
00624 case FULL:
00625 if(Tw-TSTAT_PRECISION < Ton){
00626 heat_needed = TRUE;
00627 } else if (Tw+TSTAT_PRECISION > Toff){
00628 heat_needed = FALSE;
00629 } else {
00630 ;
00631 }
00632 break;
00633 case PARTIAL:
00634 if (heat_mode == HEAT_PUMP) {
00635 if(Tcontrol-TSTAT_PRECISION < Ton){
00636 heat_needed = TRUE;
00637 } else if (Tcontrol+TSTAT_PRECISION > Toff){
00638 heat_needed = FALSE;
00639 } else {
00640 ;
00641 }
00642 } else {
00643 heat_needed = TRUE;
00644 }
00645 break;
00646 case EMPTY:
00647 heat_needed = TRUE;
00648 break;
00649 default:
00650 GL_THROW("waterheater thermostat() detected that the water heater tank is in an unknown state");
00651 }
00652
00653 }
00654
00659 TIMESTAMP waterheater::presync(TIMESTAMP t0, TIMESTAMP t1){
00660
00661 double nHours = (gl_tohours(t1) - gl_tohours(t0))/TS_SECOND;
00662 OBJECT *my = OBJECTHDR(this);
00663
00664 DATETIME t_next;
00665 gl_localtime(t1,&t_next);
00666
00667 if (t_next.day > 7 ) {
00668 if (t_next.hour >= 8) {
00669 double temp = 2;
00670 }
00671 }
00672 if(current_model != FORTRAN){
00673
00674 update_T_and_or_h(nHours);
00675 }
00676
00677 if(Tw > 212.0){
00678
00679 gl_warning("waterheater:%i is boiling", my->id);
00680
00681
00682
00683
00684
00685 }
00686
00687
00688 switch(shape.type){
00689 case MT_UNKNOWN:
00690
00691 break;
00692 case MT_ANALOG:
00693 if(shape.params.analog.energy == 0.0){
00694 GL_THROW("waterheater does not support fixed energy shaping");
00695
00696
00697
00698
00699
00700
00701
00702 } else if (shape.params.analog.power == 0){
00703
00704
00705
00706
00707
00708
00709 water_demand = gl_get_loadshape_value(&shape) / 2.4449;
00710 } else {
00711 water_demand = gl_get_loadshape_value(&shape);
00712 }
00713 break;
00714 case MT_PULSED:
00715
00716
00717 if(shape.params.pulsed.pulsetype == MPT_TIME){
00718 ;
00719 } else if(shape.params.pulsed.pulsetype == MPT_POWER){
00720 ;
00721 water_demand = gl_get_loadshape_value(&shape) / 2.4449;
00722 }
00723 break;
00724 case MT_MODULATED:
00725 if(shape.params.modulated.pulsetype == MPT_TIME){
00726 GL_THROW("Amplitude modulated water usage is nonsensical for residential water heaters");
00727
00728
00729
00730
00731 } else if(shape.params.modulated.pulsetype == MPT_POWER){
00732
00733
00734 water_demand = gl_get_loadshape_value(&shape) / 2.4449;
00735 }
00736 break;
00737 case MT_QUEUED:
00738 if(shape.params.queued.pulsetype == MPT_TIME){
00739 ;
00740 } else if(shape.params.queued.pulsetype == MPT_POWER){
00741 ;
00742 water_demand = gl_get_loadshape_value(&shape) / 2.4449;
00743 }
00744 break;
00745 default:
00746 GL_THROW("waterheater load shape has an unknown state!");
00747 break;
00748 }
00749
00750 return TS_NEVER;
00751
00752 }
00753
00757 TIMESTAMP waterheater::sync(TIMESTAMP t0, TIMESTAMP t1)
00758 {
00759 double internal_gain = 0.0;
00760 double nHours = (gl_tohours(t1) - gl_tohours(t0))/TS_SECOND;
00761 double Tamb = get_Tambient(location);
00762 int i = 0;
00763
00764
00765 if(current_model != FORTRAN){
00766 if(re_override == OV_ON){
00767 heat_needed = TRUE;
00768 } else if(re_override == OV_OFF){
00769 heat_needed = FALSE;
00770 }
00771 }
00772
00773 if(Tw > 212.0 - thermostat_deadband){
00774 heat_needed = FALSE;
00775 is_waterheater_on = 0;
00776 }
00777
00778
00779 TIMESTAMP t2 = residential_enduse::sync(t0,t1);
00780
00781
00782
00783
00784 if(current_model != FORTRAN){
00785 set_time_to_transition();
00786 }
00787
00788 if (location == INSIDE){
00789 if(this->current_model == ONENODE){
00790 internal_gain = tank_UA * (Tw - get_Tambient(location));
00791
00792 if(heat_mode == HEAT_PUMP){
00793 internal_gain -= (actual_kW() * (HP_COP - 1) * BTUPHPKW);
00794 }
00795 } else if(this->current_model == TWONODE){
00796 internal_gain = tank_UA * (Tw - Tamb) * h / height;
00797 internal_gain += tank_UA * (Tlower - Tamb) * (1 - h / height);
00798
00799 if(heat_mode == HEAT_PUMP){
00800 internal_gain -= (actual_kW() * (HP_COP - 1) * BTUPHPKW);
00801 }
00802 } else {
00803 internal_gain = 0;
00804 }
00805 } else {
00806 internal_gain = 0;
00807 }
00808
00809 if(current_model == FORTRAN){
00810 if(t1 == fwh_sim_time){
00811 fwh_cop_current = fwh_cop;
00812 fwh_power_now = fwh_power/1000;
00813 if(operating_mode == 3){
00814 Tw = (tank_water_temp[143] * (9.0 / 5.0)) + 32.0;
00815 } else {
00816 Tw = (tank_water_temp[119] * (9.0 / 5.0)) + 32.0;
00817 }
00818 ambient_temp = (Tamb - 32.0) * (5.0 / 9.0);
00819 ambient_rh = *pRH/100;
00820 int dr_sig = (int)dr_signal;
00821 int op_mode = (int)operating_mode;
00822 double sim_time = simulation_time/3600.0;
00823 double t_in = (Tinlet - 32.0) * (5.0 / 9.0);
00824 for(i = 0; i < coarse_tank_grid*fine_tank_grid; i++){
00825 init_tank_temp[i] = tank_water_temp[i];
00826 }
00827 if(location == INSIDE){
00828 for(i = 0; i < coarse_tank_grid * fine_tank_grid; i++){
00829 internal_gain += (tank_heat_loss_rate * (((init_tank_temp[i] * (9.0 / 5.0)) + 32.0) - Tamb)) / (coarse_tank_grid*fine_tank_grid);
00830 }
00831 load.heatgain = internal_gain;
00832 }
00833
00834
00835
00836
00837
00838
00839
00840
00841
00842
00843
00844
00845
00846
00847
00848
00849
00850
00851
00852
00853
00854
00855
00856
00857
00858
00859
00860
00861
00862
00863
00864
00865
00866
00867
00868
00869
00870
00871
00872
00873
00874
00875 load.total = complex(fwh_energy/(1000*simulation_time), 0);
00876 fwh_energy = 0;
00877 fwh_sim_time = t1+ (TIMESTAMP)simulation_time;
00878 }
00879 }
00880 if(current_model != FORTRAN){
00881
00882 if (heat_needed == TRUE){
00883 load.total = (heat_mode == GASHEAT ? gas_fan_power : heating_element_capacity);
00884 is_waterheater_on = 1;
00885 } else {
00886 load.total = (heat_mode == GASHEAT ? gas_standby_power : 0.0);
00887 is_waterheater_on = 0;
00888 }
00889 }
00890
00891
00892 load.power = load.total * load.power_fraction;
00893 load.admittance = load.total * load.impedance_fraction;
00894 load.current = load.total * load.current_fraction;
00895 if(current_model != FORTRAN){
00896 load.heatgain = internal_gain;
00897 }
00898
00899 waterheater_actual_power = load.power + (load.current + load.admittance * load.voltage_factor )* load.voltage_factor;
00900 actual_load = waterheater_actual_power.Re();
00901
00902 if (actual_load != 0.0)
00903 {
00904 power_state = PS_ON;
00905 }
00906 else{
00907 power_state = PS_OFF;
00908 }
00909
00910
00911 prev_load = actual_load;
00912
00913
00914 if(current_model != FORTRAN){
00915 if(re_override == OV_NORMAL){
00916 if (time_to_transition >= (1.0/3600.0))
00917 {
00918 TIMESTAMP t_to_trans = (TIMESTAMP)(t1+time_to_transition*3600.0/TS_SECOND);
00919 return -(t_to_trans);
00920 }
00921
00922 else
00923 return TS_NEVER;
00924 } else {
00925 return TS_NEVER;
00926 }
00927 } else {
00928 if(fwh_sim_time < t2){
00929 return fwh_sim_time;
00930 } else {
00931 return t2;
00932 }
00933 }
00934 }
00935
00936 TIMESTAMP waterheater::postsync(TIMESTAMP t0, TIMESTAMP t1){
00937 return TS_NEVER;
00938 }
00939
00940 TIMESTAMP waterheater::commit(){
00941 Tw_old = Tw;
00942 Tupper_old = Tw;
00943 Tlower_old = Tlower;
00944 water_demand_old = water_demand;
00945 return TS_NEVER;
00946 }
00947
00950 enumeration waterheater::tank_state(void)
00951 {
00952 if ( h >= height-HEIGHT_PRECISION )
00953 return FULL;
00954 else if ( h <= HEIGHT_PRECISION)
00955 return EMPTY;
00956 else
00957 return PARTIAL;
00958 }
00959
00962 void waterheater::set_time_to_transition(void)
00963 {
00964
00965 set_current_model_and_load_state();
00966
00967 time_to_transition = -1;
00968
00969 switch (current_model) {
00970 case ONENODE:
00971 if (heat_needed == FALSE)
00972 time_to_transition = new_time_1node(Tw, Ton);
00973 else if (load_state == RECOVERING)
00974 time_to_transition = new_time_1node(Tw, Toff);
00975 else
00976 time_to_transition = -1;
00977 break;
00978
00979 case TWONODE:
00980 switch (load_state) {
00981 case STABLE:
00982 time_to_transition = -1;
00983 break;
00984 case DEPLETING:
00985 time_to_transition = new_time_2zone(h, 0);
00986 break;
00987 case RECOVERING:
00988 time_to_transition = new_time_2zone(h, height);
00989 break;
00990 }
00991 }
00992 return;
00993 }
00994
00999 enumeration waterheater::set_current_model_and_load_state(void)
01000 {
01001 double dhdt_now = dhdt(h);
01002 double dhdt_full = dhdt(height);
01003 double dhdt_empty = dhdt(0.0);
01004 current_model = NONE;
01005 load_state = STABLE;
01006
01007 enumeration tank_status = tank_state();
01008 current_tank_state = tank_status;
01009
01010 if (tank_status == FULL) {
01011 Tcontrol = Tw;
01012 } else if (tank_status == PARTIAL) {
01013 Tcontrol = (h*Tw + (height-h)*Tinlet) / height;
01014 } else {
01015 Tcontrol = Tw;
01016 }
01017
01018 switch(tank_status)
01019 {
01020 case EMPTY:
01021 if (dhdt_empty <= 0.0)
01022 {
01023
01024
01025
01026
01027
01028
01029
01030 current_model = ONENODE;
01031 load_state = DEPLETING;
01032 Tw = Tupper = Tinlet + HEIGHT_PRECISION;
01033 Tlower = Tinlet;
01034 h = height;
01035
01036
01037
01038
01039
01040 }
01041 else if (dhdt_full > 0)
01042 {
01043
01044
01045 heat_needed = TRUE;
01046 current_model = TWONODE;
01047 load_state = RECOVERING;
01048 }
01049 else
01050 load_state = STABLE;
01051 break;
01052
01053 case FULL:
01054
01055
01056 if (dhdt_full < 0)
01057 {
01058
01059
01060 bool cur_heat_needed = heat_needed;
01061 if (heat_mode == HEAT_PUMP) {
01062 if(Tcontrol-TSTAT_PRECISION < Ton){
01063 heat_needed = TRUE;
01064 } else if (Tcontrol+TSTAT_PRECISION > Toff){
01065 heat_needed = FALSE;
01066 } else {
01067 ;
01068 }
01069 } else {
01070
01071
01072 heat_needed = TRUE;
01073 }
01074
01075 double dhdt_full_temp = dhdt(height);
01076
01077 if (dhdt_full_temp < 0)
01078 {
01079 if (heat_mode == HEAT_PUMP) {
01080 if (heat_needed == TRUE) {
01081 current_model = TWONODE;
01082 load_state = DEPLETING;
01083 } else {
01084 current_model = ONENODE;
01085 load_state = DEPLETING;
01086 }
01087 } else {
01088 current_model = TWONODE;
01089 load_state = DEPLETING;
01090 }
01091 }
01092 else
01093 {
01094 current_model = ONENODE;
01095
01096 heat_needed = cur_heat_needed;
01097 load_state = heat_needed ? RECOVERING : DEPLETING;
01098 }
01099
01100 if (re_override == OV_OFF)
01101 {
01102 load_state = DEPLETING;
01103 heat_needed = FALSE;
01104 if (water_demand > 0 || h < (tank_height-HEIGHT_PRECISION))
01105 current_model = TWONODE;
01106 else
01107 current_model = ONENODE;
01108 }
01109 else if (dhdt_full_temp < 0)
01110 {
01111 current_model = TWONODE;
01112 load_state = DEPLETING;
01113 }
01114 else
01115 {
01116 current_model = ONENODE;
01117
01118 heat_needed = cur_heat_needed;
01119 load_state = heat_needed ? RECOVERING : DEPLETING;
01120 }
01121 }
01122 else if (dhdt_empty > 0)
01123 {
01124 current_model = ONENODE;
01125 load_state = RECOVERING;
01126
01127 if (re_override == OV_OFF)
01128 {
01129 heat_needed = FALSE;
01130 load_state = DEPLETING;
01131 }
01132 }
01133 else {
01134 load_state = STABLE;
01135
01136 if (re_override == OV_OFF)
01137 heat_needed = FALSE;
01138 }
01139 break;
01140
01141 case PARTIAL:
01142 if (heat_mode == HEAT_PUMP) {
01143 if(Tcontrol-TSTAT_PRECISION < Ton || heat_needed == TRUE){
01144 heat_needed = TRUE;
01145 current_model = TWONODE;
01146 } else if (Tcontrol+TSTAT_PRECISION > Toff){
01147 heat_needed = FALSE;
01148 current_model = ONENODE;
01149 } else {
01150 ;
01151 }
01152
01153 if (dhdt_now < 0 && (dhdt_now * dhdt_empty) >= 0)
01154 load_state = DEPLETING;
01155 else if (dhdt_now > 0 && (dhdt_now * dhdt_full) >= 0)
01156 load_state = RECOVERING;
01157 else
01158 {
01159
01160 current_model = ONENODE;
01161 load_state = STABLE;
01162 }
01163
01164 if (re_override == OV_OFF)
01165 {
01166 heat_needed = FALSE;
01167 if (water_demand > 0 || h < (tank_height-HEIGHT_PRECISION))
01168 current_model = TWONODE;
01169 else
01170 current_model = ONENODE;
01171 }
01172 } else {
01173
01174
01175 current_model = TWONODE;
01176
01177
01178 heat_needed = TRUE;
01179
01180 if (dhdt_now < 0 && (dhdt_now * dhdt_empty) >= 0)
01181 load_state = DEPLETING;
01182 else if (dhdt_now > 0 && (dhdt_now * dhdt_full) >= 0)
01183 load_state = RECOVERING;
01184 else
01185 {
01186
01187 current_model = NONE;
01188 load_state = STABLE;
01189 }
01190
01191 if (re_override == OV_OFF)
01192 {
01193 heat_needed = FALSE;
01194 if (water_demand > 0 || h < (tank_height-HEIGHT_PRECISION))
01195 current_model = TWONODE;
01196 else
01197 current_model = ONENODE;
01198 }
01199 }
01200 break;
01201 }
01202
01203 return load_state;
01204 }
01205
01206 void waterheater::update_T_and_or_h(double nHours)
01207 {
01208
01209
01210
01211
01212
01213
01214
01215
01216
01217
01218
01219
01220
01221 switch (current_model)
01222 {
01223 case ONENODE:
01224
01225
01226 SingleZone:
01227 Tw = new_temp_1node(Tw, nHours);
01228 Tw = Tw;
01229 Tlower = Tinlet;
01230 break;
01231
01232 case TWONODE:
01233
01234
01235 heat_needed = TRUE;
01236 switch (load_state)
01237 {
01238 case STABLE:
01239
01240 break;
01241 case DEPLETING:
01242
01243 case RECOVERING:
01244 try {
01245 h = new_h_2zone(h, nHours);
01246 } catch (WRONGMODEL m)
01247 {
01248 if (m==MODEL_NOT_2ZONE)
01249 {
01250 current_model = ONENODE;
01251 goto SingleZone;
01252 }
01253 else
01254 GL_THROW("unexpected exception in update_T_and_or_h(%+.1f hrs)", nHours);
01255 }
01256 break;
01257 }
01258
01259
01260 if (h < ROUNDOFF)
01261 {
01262
01263
01264
01265 double vol_over = tank_volume/GALPCF * h/height;
01266 double energy_over = vol_over * RHOWATER * Cp * ( Tw - Tlower);
01267 double Tnew = Tlower + energy_over/Cw;
01268 Tw = Tlower = Tnew;
01269 h = 0;
01270 }
01271 else if (h > height)
01272 {
01273
01274 double vol_over = tank_volume/GALPCF * (h-height)/height;
01275 double energy_over = vol_over * RHOWATER * Cp * ( Tw - Tlower);
01276 double Tnew = Tw + energy_over/Cw;
01277 Tw = Tw = Tnew;
01278 Tlower = Tinlet;
01279 h = height;
01280 }
01281 else
01282 {
01283
01284
01285
01286
01287 Tw = Tw;
01288 }
01289 break;
01290
01291 default:
01292 break;
01293 }
01294
01295 if (heat_needed == TRUE)
01296 power_state = PS_ON;
01297 else
01298 power_state = PS_OFF;
01299
01300 return;
01301 }
01302
01303
01304
01305
01306
01307
01308 double waterheater::dhdt(double h)
01309 {
01310 if ( Tw - Tlower < ROUNDOFF)
01311 return 0.0;
01312
01313
01314 const double mdot = water_demand * 60 * RHOWATER / GALPCF;
01315 const double c1 = RHOWATER * Cp * area * ( Tw - Tlower);
01316
01317
01318 if (c1 <= ROUNDOFF)
01319 return 0.0;
01320
01321 double cA;
01322 if (heat_mode == HEAT_PUMP) {
01323
01324 HP_COP = (1.04 + (1.21 - 1.04) * (get_Tambient(location) - 50) / (70 - 50)) * (5.259 - 0.0255 * Tw);
01325 cA = -mdot / (RHOWATER * area) + (actual_kW() * BTUPHPKW * HP_COP + tank_UA * (get_Tambient(location) - Tlower)) / c1;
01326 } else {
01327 cA = -mdot / (RHOWATER * area) + (actual_kW() * BTUPHPKW + tank_UA * (get_Tambient(location) - Tlower)) / c1;
01328 }
01329 const double cb = (tank_UA / height) * ( Tw - Tlower) / c1;
01330
01331
01332 return cA - cb*h;
01333 }
01334
01335 double waterheater::actual_kW(void)
01336 {
01337 OBJECT *obj = OBJECTHDR(this);
01338 static int trip_counter = 0;
01339
01340
01341 if (heat_needed && re_override != OV_OFF)
01342 {
01343 if(heat_mode == GASHEAT){
01344 return heating_element_capacity;
01345 }
01346
01347 if (pCircuit == NULL)
01348 {
01349 actual_voltage = nominal_voltage;
01350 }
01351 else
01352 {
01353 actual_voltage = (pCircuit->pV->get_complex()).Mag();
01354 }
01355
01356 if (actual_voltage > 2.0*nominal_voltage)
01357 {
01358 if (trip_counter++ > 10)
01359 gl_error("Water heater line voltage for waterheater:%d is too high, exceeds twice nominal voltage.",obj->id);
01360
01361
01362
01363
01364
01365 else
01366 return 0.0;
01367 }
01368 double test;
01369 if (heat_mode == ELECTRIC) {
01370 test = heating_element_capacity * (actual_voltage*actual_voltage) / (nominal_voltage*nominal_voltage);
01371 } else {
01372
01373
01374 heating_element_capacity = (1.09 + (1.17 - 1.09) * (get_Tambient(location) - 50) / (70 - 50)) * (0.379 + 0.00364 * Tw);
01375 test = heating_element_capacity;
01376 }
01377
01378 return test;
01379 }
01380 else
01381 return 0.0;
01382 }
01383
01384 inline double waterheater::new_time_1node(double T0, double T1)
01385 {
01386 const double mdot_Cp = Cp * water_demand * 60 * RHOWATER / GALPCF;
01387
01388 if (Cw <= ROUNDOFF)
01389 return -1.0;
01390
01391 double c1;
01392 if (heat_mode == HEAT_PUMP) {
01393
01394 HP_COP = (1.04 + (1.21 - 1.04) * (get_Tambient(location) - 50) / (70 - 50)) * (5.259 - 0.0255 * Tw);
01395 c1 = ((actual_kW()*BTUPHPKW*HP_COP + tank_UA * get_Tambient(location)) + mdot_Cp*Tinlet) / Cw;
01396 } else {
01397 c1 = ((actual_kW()*BTUPHPKW + tank_UA * get_Tambient(location)) + mdot_Cp*Tinlet) / Cw;
01398 }
01399 const double c2 = -(tank_UA + mdot_Cp) / Cw;
01400
01401 if (fabs(c1 + c2*T1) <= ROUNDOFF || fabs(c1 + c2*T0) <= ROUNDOFF || fabs(c2) <= ROUNDOFF)
01402 return -1.0;
01403
01404 const double new_time = (log(fabs(c1 + c2 * T1)) - log(fabs(c1 + c2 * T0))) / c2;
01405 return new_time;
01406 }
01407
01408 inline double waterheater::new_temp_1node(double T0, double delta_t)
01409 {
01410
01411 const double mdot_Cp = Cp * water_demand_old * 60 * RHOWATER / GALPCF;
01412
01413
01414 if (Cw <= ROUNDOFF || (tank_UA+mdot_Cp) <= ROUNDOFF)
01415 return T0;
01416
01417 const double c1 = (tank_UA + mdot_Cp) / Cw;
01418 double c2;
01419 if (heat_mode == HEAT_PUMP) {
01420
01421 HP_COP = (1.04 + (1.21 - 1.04) * (get_Tambient(location) - 50) / (70 - 50)) * (5.259 - 0.0255 * Tw);
01422 c2 = (actual_kW()*BTUPHPKW*HP_COP + mdot_Cp*Tinlet + tank_UA*get_Tambient(location)) / (tank_UA + mdot_Cp);
01423 } else {
01424 c2 = (actual_kW()*BTUPHPKW + mdot_Cp*Tinlet + tank_UA*get_Tambient(location)) / (tank_UA + mdot_Cp);
01425 }
01426
01427
01428 return c2 - (c2 - T0) * exp(-c1 * delta_t);
01429 }
01430
01431
01432 inline double waterheater::new_time_2zone(double h0, double h1)
01433 {
01434 const double c0 = RHOWATER * Cp * area * ( Tw - Tlower);
01435 double dhdt0, dhdt1;
01436
01437 if (fabs(c0) <= ROUNDOFF || height <= ROUNDOFF)
01438 return -1.0;
01439
01440 const double cb = (tank_UA / height) * ( Tw - Tlower) / c0;
01441
01442 if (fabs(cb) <= ROUNDOFF)
01443 return -1.0;
01444 dhdt1 = fabs(dhdt(h1));
01445 dhdt0 = fabs(dhdt(h0));
01446 double last_timestep = (log(dhdt1) - log(dhdt0)) / -cb;
01447 return last_timestep;
01448 }
01449
01450 inline double waterheater::new_h_2zone(double h0, double delta_t)
01451 {
01452 if (delta_t <= ROUNDOFF)
01453 return h0;
01454
01455
01456 const double mdot = water_demand_old * 60 * RHOWATER / GALPCF;
01457 const double c1 = RHOWATER * Cp * area * ( Tw - Tlower);
01458
01459
01460 if (fabs(c1) <= ROUNDOFF)
01461 return height;
01462
01463
01464
01465 double cA;
01466 if (heat_mode == HEAT_PUMP) {
01467
01468 HP_COP = (1.04 + (1.21 - 1.04) * (get_Tambient(location) - 50) / (70 - 50)) * (5.259 - 0.0255 * Tw);
01469 cA = -mdot / (RHOWATER * area) + (actual_kW()*BTUPHPKW*HP_COP + tank_UA * (get_Tambient(location) - Tlower)) / c1;
01470 } else {
01471 cA = -mdot / (RHOWATER * area) + (actual_kW()*BTUPHPKW + tank_UA * (get_Tambient(location) - Tlower)) / c1;
01472 }
01473
01474 const double cb = (tank_UA / height) * ( Tw - Tlower) / c1;
01475
01476 if (fabs(cb) <= ROUNDOFF)
01477 return height;
01478
01479 return ((exp(cb * delta_t) * (cA + cb * h0)) - cA) / cb;
01480 }
01481
01482 double waterheater::get_Tambient(enumeration loc)
01483 {
01484 double ratio;
01485 OBJECT *parent = OBJECTHDR(this)->parent;
01486
01487 switch (loc) {
01488 case GARAGE:
01489 ratio = 0.5;
01490 break;
01491 case INSIDE:
01492 default:
01493 ratio = 1.0;
01494 break;
01495 }
01496
01497
01498
01499
01500 return *pTair * ratio + *pTout *(1-ratio);
01501 }
01502
01503 void waterheater::wrong_model(WRONGMODEL msg)
01504 {
01505 char *errtxt[] = {"model is not one-zone","model is not two-zone"};
01506 OBJECT *obj = OBJECTHDR(this);
01507 gl_warning("%s (waterheater:%d): %s", obj->name?obj->name:"(anonymous object)", obj->id, errtxt[msg]);
01508 throw msg;
01509 }
01510
01512
01514
01515 EXPORT int create_waterheater(OBJECT **obj, OBJECT *parent)
01516 {
01517 try
01518 {
01519 *obj = gl_create_object(waterheater::oclass);
01520 if (*obj!=NULL)
01521 {
01522 waterheater *my = OBJECTDATA(*obj,waterheater);;
01523 gl_set_parent(*obj,parent);
01524 my->create();
01525 return 1;
01526 }
01527 else
01528 return 0;
01529 }
01530 CREATE_CATCHALL(waterheater);
01531 }
01532
01533 EXPORT int init_waterheater(OBJECT *obj)
01534 {
01535 try
01536 {
01537 waterheater *my = OBJECTDATA(obj,waterheater);
01538 return my->init(obj->parent);
01539 }
01540 INIT_CATCHALL(waterheater);
01541 }
01542
01543 EXPORT int isa_waterheater(OBJECT *obj, char *classname)
01544 {
01545 if(obj != 0 && classname != 0){
01546 return OBJECTDATA(obj,waterheater)->isa(classname);
01547 } else {
01548 return 0;
01549 }
01550 }
01551
01552
01553 EXPORT TIMESTAMP sync_waterheater(OBJECT *obj, TIMESTAMP t0, PASSCONFIG pass)
01554 {
01555 try {
01556 waterheater *my = OBJECTDATA(obj, waterheater);
01557 if (obj->clock <= ROUNDOFF)
01558 obj->clock = t0;
01559 TIMESTAMP t1 = TS_NEVER;
01560 switch (pass) {
01561 case PC_PRETOPDOWN:
01562 return my->presync(obj->clock, t0);
01563 case PC_BOTTOMUP:
01564 return my->sync(obj->clock, t0);
01565 case PC_POSTTOPDOWN:
01566 t1 = my->postsync(obj->clock, t0);
01567 obj->clock = t0;
01568 return t1;
01569 default:
01570 throw "invalid pass request";
01571 }
01572 return TS_INVALID;
01573 }
01574 SYNC_CATCHALL(waterheater);
01575 }
01576
01577 EXPORT TIMESTAMP commit_waterheater(OBJECT *obj, TIMESTAMP t1, TIMESTAMP t2)
01578 {
01579 waterheater *my = OBJECTDATA(obj,waterheater);
01580 return my->commit();
01581 }
01582
01583 EXPORT TIMESTAMP plc_waterheater(OBJECT *obj, TIMESTAMP t0)
01584 {
01585
01586 if (obj->clock <= ROUNDOFF)
01587 obj->clock = t0;
01588
01589 waterheater *my = OBJECTDATA(obj,waterheater);
01590 my->thermostat(obj->clock, t0);
01591
01592
01594 return TS_NEVER;
01595 }
01596