00001
00052 #include <stdlib.h>
00053 #include <stdio.h>
00054 #include <errno.h>
00055 #include <math.h>
00056
00057 #include "freezer.h"
00058
00060
00062 CLASS* freezer::oclass = NULL;
00063 CLASS* freezer::pclass = NULL;
00064
00065
00066
00067 freezer::freezer(MODULE *module) : residential_enduse(module)
00068 {
00069
00070 if (oclass == NULL)
00071 {
00072 oclass = gl_register_class(module,"freezer",sizeof(freezer),PC_PRETOPDOWN | PC_BOTTOMUP|PC_AUTOLOCK);
00073 if (oclass==NULL)
00074 throw "unable to register class freezer";
00075 else
00076 oclass->trl = TRL_DEMONSTRATED;
00077
00078
00079
00080 if (gl_publish_variable(oclass,
00081 PT_INHERIT, "residential_enduse",
00082 PT_double, "size[cf]", PADDR(size),
00083 PT_double, "rated_capacity[Btu/h]", PADDR(rated_capacity),
00084 PT_double,"temperature[degF]",PADDR(Tair),
00085 PT_double,"setpoint[degF]",PADDR(Tset),
00086 PT_double,"deadband[degF]",PADDR(thermostat_deadband),
00087 PT_timestamp,"next_time",PADDR(last_time),
00088 PT_double,"output",PADDR(Qr),
00089 PT_double,"event_temp",PADDR(Tevent),
00090 PT_double,"UA[Btu/degF*h]",PADDR(UA),
00091 PT_enumeration,"state",PADDR(motor_state),
00092 PT_KEYWORD,"OFF",(enumeration)S_OFF,
00093 PT_KEYWORD,"ON",(enumeration)S_ON,
00094 NULL) < 1)
00095 GL_THROW("unable to publish properties in %s", __FILE__);
00096 }
00097 }
00098
00099 freezer::~freezer()
00100 {
00101 }
00102
00103 int freezer::create()
00104 {
00105 int res = residential_enduse::create();
00106
00107
00108 load.name = oclass->name;
00109
00110 gl_warning("explicit %s model is experimental", OBJECTHDR(this)->oclass->name);
00111
00112 return res;
00113 }
00114
00115 int freezer::init(OBJECT *parent)
00116 {
00117 gl_warning("This device, %s, is considered very experimental and has not been validated.", get_name());
00118
00119 if(parent != NULL){
00120 if((parent->flags & OF_INIT) != OF_INIT){
00121 char objname[256];
00122 gl_verbose("freezer::init(): deferring initialization on %s", gl_name(parent, objname, 255));
00123 return 2;
00124 }
00125 }
00126
00127 if (size==0) size = gl_random_uniform(RNGSTATE,20,40);
00128 if (thermostat_deadband==0) thermostat_deadband = gl_random_uniform(RNGSTATE,2,3);
00129 if (Tset==0) Tset = gl_random_uniform(RNGSTATE,10,20);
00130 if (UA == 0) UA = 0.3;
00131 if (UAr==0) UAr = UA+size/40*gl_random_uniform(RNGSTATE,0.9,1.1);
00132 if (UAf==0) UAf = gl_random_uniform(RNGSTATE,0.9,1.1);
00133 if (COPcoef==0) COPcoef = gl_random_uniform(RNGSTATE,0.9,1.1);
00134 if (Tout==0) Tout = 59.0;
00135 if (power_factor==0) power_factor = 0.95;
00136
00137 OBJECT *hdr = OBJECTHDR(this);
00138 hdr->flags |= OF_SKIPSAFE;
00139
00140
00141 pTempProp = gl_get_property(parent, "air_temperature");
00142 if(pTempProp == NULL){
00143 GL_THROW("Parent house of freezer lacks property \'air_temperature\'");
00144 }
00145
00146
00147 Tair = gl_random_uniform(RNGSTATE,Tset-thermostat_deadband/2, Tset+thermostat_deadband/2);
00148
00149
00150
00151 Cf = size/10.0 * RHOWATER * CWATER;
00152
00153 rated_capacity = BTUPHPW * size*10;
00154
00155
00156 if (gl_random_bernoulli(RNGSTATE,0.04)){
00157 Qr = rated_capacity;
00158 } else {
00159 Qr = 0;
00160 }
00161
00162
00163 load.total = Qr * KWPBTUPH;
00164
00165 return residential_enduse::init(parent);
00166 }
00167
00168 int freezer::isa(char *classname)
00169 {
00170 return (strcmp(classname,"freezer")==0 || residential_enduse::isa(classname));
00171 }
00172
00173 TIMESTAMP freezer::presync(TIMESTAMP t0, TIMESTAMP t1){
00174 OBJECT *hdr = OBJECTHDR(this);
00175 double *pTout = 0, t = 0.0, dt = 0.0;
00176 double nHours = (gl_tohours(t1)- gl_tohours(t0))/TS_SECOND;
00177
00178 pTout = gl_get_double(hdr->parent, pTempProp);
00179 if(pTout == NULL){
00180 GL_THROW("Parent house of freezer lacks property \'air_temperature\' at sync time?");
00181 }
00182 Tout = *pTout;
00183
00184 if(nHours > 0 && t0 > 0){
00185 const double COP = COPcoef*((-3.5/45)*(Tout-70)+4.5);
00186
00187 if(t1 == next_time){
00188
00189 load.heatgain = -((Tair - Tout) * exp(-(UAr+UAf)/Cf) + Tout - Tair) * Cf * nHours + Qr * nHours * COP;
00190 Tair = Tevent;
00191 } else {
00192
00193 const double C1 = Cf/(UAr+UAf);
00194 const double C2 = Tout - Qr/UAr;
00195 load.heatgain = -((Tair - Tout) * exp(-(UAr+UAf)/Cf) + Tout - Tair) * Cf * nHours + Qr * nHours * COP;
00196 Tair = (Tair-C2)*exp(-nHours/C1)+C2;
00197 }
00198 if (Tair < 0 || Tair > 32){
00199 gl_warning("freezer air temperature out of control");
00200
00201 }
00202 last_time = t1;
00203 }
00204
00205 return TS_NEVER;
00206 }
00207
00208
00209
00210
00211 void freezer::thermostat(TIMESTAMP t0, TIMESTAMP t1){
00212 const double Ton = Tset+thermostat_deadband / 2;
00213 const double Toff = Tset-thermostat_deadband / 2;
00214
00215
00216 if(motor_state == S_OFF){
00217
00218 if(Tair >= Ton){
00219 motor_state = S_ON;
00220 Tevent = Toff;
00221 } else {
00222 Tevent = Ton;
00223 }
00224 } else if(motor_state == S_ON){
00225
00226 if(Tair <= Toff){
00227 motor_state = S_OFF;
00228 Tevent = Ton;
00229 } else {
00230 Tevent = Toff;
00231 }
00232 }
00233 }
00234
00235 TIMESTAMP freezer::sync(TIMESTAMP t0, TIMESTAMP t1)
00236 {
00237 double nHours = (gl_tohours(t1)- gl_tohours(t0))/TS_SECOND;
00238 double t = 0.0, dt = 0.0;
00239 double temp_voltage_magnitude;
00240
00241 const double COP = COPcoef*((-3.5/45)*(Tout-70)+4.5);
00242
00243
00244 load.energy += load.total * nHours;
00245
00246
00247 if(motor_state == S_ON){
00248 Qr = rated_capacity;
00249 } else if(motor_state == S_OFF){
00250 Qr = 0;
00251 } else{
00252 throw "freezer motor state is ambiguous";
00253 }
00254
00255 load.total = Qr * KWPBTUPH * COP;
00256
00257
00258 temp_voltage_magnitude = (pCircuit->pV->get_complex()).Mag();
00259
00260 if(temp_voltage_magnitude < (default_line_voltage * 0.6) ){
00261 gl_verbose("freezer motor has stalled");
00262 motor_state = S_OFF;
00263 Qr = 0;
00264 return TS_NEVER;
00265 }
00266
00267
00268 const double C1 = Cf/(UAr+UAf);
00269 const double C2 = Tout - Qr/UAr;
00270
00271
00272 dt = t = -log((Tevent - C2)/(Tair-C2))*C1;
00273
00274 if(t == 0){
00275 GL_THROW("freezer control logic error, dt = 0");
00276 } else if(t < 0){
00277 GL_THROW("freezer control logic error, dt < 0");
00278 }
00279
00280
00281 next_time = (TIMESTAMP)(t1 + (t > 0 ? t : -t) * (3600.0/TS_SECOND) + 1);
00282 return next_time > TS_NEVER ? TS_NEVER : -next_time;
00283 }
00284
00285 TIMESTAMP freezer::postsync(TIMESTAMP t0, TIMESTAMP t1){
00286 return TS_NEVER;
00287 }
00288
00289
00291
00293 EXPORT int create_freezer(OBJECT **obj, OBJECT *parent)
00294 {
00295 try
00296 {
00297 *obj = gl_create_object(freezer::oclass);
00298 if (*obj!=NULL)
00299 {
00300 freezer *my = OBJECTDATA(*obj,freezer);;
00301 gl_set_parent(*obj,parent);
00302 my->create();
00303 return 1;
00304 }
00305 else
00306 return 0;
00307 }
00308 CREATE_CATCHALL(freezer);
00309 }
00310
00311 EXPORT TIMESTAMP sync_freezer(OBJECT *obj, TIMESTAMP t0, PASSCONFIG pass)
00312 {
00313 try {
00314 freezer *my = OBJECTDATA(obj,freezer);
00315 TIMESTAMP t1 = TS_NEVER;
00316 switch (pass)
00317 {
00318 case PC_PRETOPDOWN:
00319 t1 = my->presync(obj->clock, t0);
00320 break;
00321
00322 case PC_BOTTOMUP:
00323 t1 = my->sync(obj->clock, t0);
00324 obj->clock = t0;
00325 break;
00326
00327 case PC_POSTTOPDOWN:
00328 t1 = my->postsync(obj->clock, t0);
00329 break;
00330
00331 default:
00332 gl_error("freezer::sync- invalid pass configuration");
00333 t1 = TS_INVALID;
00334 }
00335 return t1;
00336 }
00337 SYNC_CATCHALL(freezer);
00338 }
00339
00340 EXPORT int init_freezer(OBJECT *obj)
00341 {
00342 try
00343 {
00344 freezer *my = OBJECTDATA(obj,freezer);
00345 return my->init(obj->parent);
00346 }
00347 INIT_CATCHALL(freezer);
00348 }
00349
00350 EXPORT int isa_freezer(OBJECT *obj, char *classname)
00351 {
00352 if(obj != 0 && classname != 0){
00353 return OBJECTDATA(obj,freezer)->isa(classname);
00354 } else {
00355 return 0;
00356 }
00357 }
00358
00359
00360 EXPORT TIMESTAMP plc_freezer(OBJECT *obj, TIMESTAMP t0)
00361 {
00362
00363
00364 freezer *my = OBJECTDATA(obj,freezer);
00365 my->thermostat(obj->clock, t0);
00366
00367 return TS_NEVER;
00368 }
00369