00001
00021 #include <stdlib.h>
00022 #include <stdio.h>
00023 #include <errno.h>
00024 #include <math.h>
00025
00026 #include "house_a.h"
00027 #include "clotheswasher.h"
00028
00030
00032 CLASS* clotheswasher::oclass = NULL;
00033 CLASS* clotheswasher::pclass = NULL;
00034 clotheswasher *clotheswasher::defaults = NULL;
00035
00036 clotheswasher::clotheswasher(MODULE *module) : residential_enduse(module)
00037 {
00038
00039 if (oclass==NULL)
00040 {
00041
00042 oclass = gl_register_class(module,"clotheswasher",sizeof(clotheswasher),PC_PRETOPDOWN|PC_BOTTOMUP);
00043 pclass = residential_enduse::oclass;
00044 if (oclass==NULL)
00045 GL_THROW("unable to register object class implemented by %s",__FILE__);
00046
00047
00048 if (gl_publish_variable(oclass,
00049 PT_INHERIT, "residential_enduse",
00050 PT_double,"motor_power[kW]",PADDR(shape.params.analog.power),
00051 PT_double,"circuit_split",PADDR(circuit_split),
00052 PT_double,"queue[unit]",PADDR(enduse_queue), PT_DESCRIPTION, "the total laundry accumulated",
00053 PT_double,"demand[unit/day]",PADDR(enduse_demand), PT_DESCRIPTION, "the amount of laundry accumulating daily",
00054 PT_complex,"energy_meter[kWh]",PADDR(load.energy),
00055 PT_double,"stall_voltage[V]", PADDR(stall_voltage),
00056 PT_double,"start_voltage[V]", PADDR(start_voltage),
00057 PT_complex,"stall_impedance[Ohm]", PADDR(stall_impedance),
00058 PT_double,"trip_delay[s]", PADDR(trip_delay),
00059 PT_double,"reset_delay[s]", PADDR(reset_delay),
00060 PT_enumeration,"state", PADDR(state),
00061 PT_KEYWORD,"STOPPED",STOPPED,
00062 PT_KEYWORD,"RUNNING",RUNNING,
00063 PT_KEYWORD,"STALLED",STALLED,
00064 PT_KEYWORD,"TRIPPED",TRIPPED,
00065 NULL)<1)
00066 GL_THROW("unable to publish properties in %s",__FILE__);
00067 }
00068 }
00069
00070 clotheswasher::~clotheswasher()
00071 {
00072 }
00073
00074 int clotheswasher::create()
00075 {
00076 int res = residential_enduse::create();
00077
00078
00079 load.name = oclass->name;
00080
00081 load.power = load.admittance = load.current = load.total = complex(0,0,J);
00082 load.voltage_factor = 1.0;
00083 load.power_factor = 0.95;
00084 load.power_fraction = 1.0;
00085
00086 gl_warning("explicit %s model is experimental", OBJECTHDR(this)->oclass->name);
00087
00088 return res;
00089 }
00090
00091 int clotheswasher::init(OBJECT *parent)
00092 {
00093
00094 if (shape.params.analog.power==0) shape.params.analog.power = gl_random_uniform(0.100,0.750);
00095 if (load.heatgain_fraction==0) load.heatgain_fraction = 0.5;
00096 if (load.power_factor==0) load.power_factor = 0.95;
00097 if (stall_voltage==0) stall_voltage = 0.7*120;
00098 if (trip_delay==0) trip_delay = 10;
00099 if (reset_delay==0) reset_delay = 60;
00100
00101 OBJECT *hdr = OBJECTHDR(this);
00102 hdr->flags |= OF_SKIPSAFE;
00103
00104 if(shape.params.analog.power < 0.1){
00105 gl_error("clotheswasher motor is undersized, using 500W motor");
00106 shape.params.analog.power = 0.5;
00107 }
00108
00109 int res = residential_enduse::init(parent);
00110
00111 if (res==SUCCESS && shape.type==MT_UNKNOWN)
00112 update_state(0);
00113
00114 return res;
00115 }
00116
00117 int clotheswasher::isa(char *classname)
00118 {
00119 return (strcmp(classname,"clotheswasher")==0 || residential_enduse::isa(classname));
00120 }
00121
00122 TIMESTAMP clotheswasher::presync(TIMESTAMP t0, TIMESTAMP t1){
00123 return TS_NEVER;
00124 }
00125
00126 TIMESTAMP clotheswasher::sync(TIMESTAMP t0, TIMESTAMP t1)
00127 {
00128
00129 double dt = 0.0;
00130 TIMESTAMP t2 = residential_enduse::sync(t0, t1);
00131
00132 if (pCircuit!=NULL)
00133 load.voltage_factor = pCircuit->pV->Mag() / 120;
00134
00135 switch(shape.type){
00136 case MT_UNKNOWN:
00137 dt = gl_toseconds(t0>0?t1-t0:0);
00138
00139 dt = update_state(dt);
00140 break;
00141 case MT_ANALOG:
00142 break;
00143 case MT_PULSED:
00144 break;
00145 case MT_MODULATED:
00146 break;
00147 default:
00148 GL_THROW("clotheswasher load shape has an unknown state!");
00149 break;
00150 }
00151
00152 gl_enduse_sync(&(residential_enduse::load),t1);
00153
00154 if(dt>0){
00155 if(t2 > (TIMESTAMP)(dt*TS_SECOND + t0)){
00156 t2 = (TIMESTAMP)(dt*TS_SECOND + t0);
00157 }
00158 }
00159 return -t2;
00160 }
00161
00162 double clotheswasher::update_state(double dt)
00163 {
00164
00165 switch(state) {
00166 case STOPPED:
00167 if (enduse_queue>1)
00168 {
00169 state = RUNNING;
00170 enduse_queue--;
00171 cycle_time = cycle_duration>0 ? cycle_duration : gl_random_uniform(20,40)*60;
00172 state_time = 0;
00173 }
00174 break;
00175 case RUNNING:
00176 if (cycle_time<=0)
00177 {
00178 state = STOPPED;
00179 cycle_time = state_time = 0;
00180 }
00181 else if (pCircuit->pV->Mag()<stall_voltage)
00182 {
00183 state = STALLED;
00184 state_time = 0;
00185 }
00186 break;
00187 case STALLED:
00188 if (pCircuit->pV->Mag()>start_voltage)
00189 {
00190 state = RUNNING;
00191 state_time = cycle_time;
00192 }
00193 else if (state_time>trip_delay)
00194 {
00195 state = TRIPPED;
00196 state_time = 0;
00197 }
00198 break;
00199 case TRIPPED:
00200 if (state_time>reset_delay)
00201 {
00202 if (pCircuit->pV->Mag()>start_voltage)
00203 state = RUNNING;
00204 else
00205 state = STALLED;
00206 state_time = 0;
00207 }
00208 break;
00209 }
00210
00211
00212 state_time += dt;
00213
00214
00215 enduse_queue += enduse_demand * dt/3600/24;
00216
00217
00218 switch(state) {
00219 case STOPPED:
00220
00221
00222 load.power = load.current = load.admittance = complex(0,0,J);
00223
00224
00225 dt = (enduse_demand<=0) ? -1 : dt = 3600/enduse_demand;
00226
00227 break;
00228
00229 case RUNNING:
00230
00231
00232 cycle_time -= dt;
00233
00234
00235 load.power.SetPowerFactor(shape.params.analog.power, load.power_factor);
00236 load.current = load.admittance = complex(0,0,J);
00237
00238
00239 dt = cycle_time;
00240
00241 break;
00242 case STALLED:
00243
00244
00245 load.power = load.current = complex(0,0,J);
00246 load.admittance = complex(1)/stall_impedance;
00247
00248
00249 dt = trip_delay;
00250
00251 break;
00252 case TRIPPED:
00253
00254
00255 load.power = load.current = load.admittance = complex(0,0,J);
00256
00257
00258 dt = reset_delay;
00259
00260 break;
00261 default:
00262 throw "unexpected motor state";
00263
00264
00265
00266
00267 break;
00268 }
00269
00270
00271 load.total = load.power + ~(load.current + load.admittance**pCircuit->pV)**pCircuit->pV/1000;
00272
00273
00274 load.heatgain = load.total.Mag() * load.heatgain_fraction * BTUPHPKW;
00275
00276 return dt;
00277 }
00278
00280
00282
00283 EXPORT int create_clotheswasher(OBJECT **obj, OBJECT *parent)
00284 {
00285 *obj = gl_create_object(clotheswasher::oclass);
00286 if (*obj!=NULL)
00287 {
00288 clotheswasher *my = OBJECTDATA(*obj,clotheswasher);
00289 gl_set_parent(*obj,parent);
00290 my->create();
00291 return 1;
00292 }
00293 return 0;
00294 }
00295
00296 EXPORT int init_clotheswasher(OBJECT *obj)
00297 {
00298 clotheswasher *my = OBJECTDATA(obj,clotheswasher);
00299 return my->init(obj->parent);
00300 }
00301
00302 EXPORT int isa_clotheswasher(OBJECT *obj, char *classname)
00303 {
00304 if(obj != 0 && classname != 0){
00305 return OBJECTDATA(obj,clotheswasher)->isa(classname);
00306 } else {
00307 return 0;
00308 }
00309 }
00310
00311 EXPORT TIMESTAMP sync_clotheswasher(OBJECT *obj, TIMESTAMP t0, PASSCONFIG pass)
00312 {
00313 clotheswasher *my = OBJECTDATA(obj, clotheswasher);
00314 if (obj->clock <= ROUNDOFF)
00315 obj->clock = t0;
00316 try {
00317 TIMESTAMP t1 = TS_NEVER;
00318 switch (pass) {
00319 case PC_PRETOPDOWN:
00320 return my->presync(obj->clock, t0);
00321 case PC_BOTTOMUP:
00322 t1 = my->sync(obj->clock, t0);
00323 obj->clock = t0;
00324 return t1;
00325 default:
00326 throw "invalid pass request";
00327 }
00328 }
00329 catch (int m)
00330 {
00331 gl_error("%s (clotheswasher:%d) model zone exception (code %d) not caught", obj->name?obj->name:"(anonymous waterheater)", obj->id, m);
00332 }
00333 catch (char *msg)
00334 {
00335 gl_error("%s (clotheswasher:%d) %s", obj->name?obj->name:"(anonymous clotheswasher)", obj->id, msg);
00336 }
00337 return TS_INVALID;}
00338