residential/house.cpp

Go to the documentation of this file.
00001 
00079 #include <stdlib.h>
00080 #include <stdio.h>
00081 #include <errno.h>
00082 #include <math.h>
00083 #include "solvers.h"
00084 #include "house.h"
00085 #include "lock.h"
00086 
00088 // house CLASS FUNCTIONS
00090 CLASS* house::oclass = NULL;
00091 house *house::defaults = NULL;
00092 
00096 house::house(MODULE *mod) 
00097 {
00098     // first time init
00099     if (oclass==NULL)
00100     {
00101         // register the class definition
00102         oclass = gl_register_class(mod,"house",PC_PRETOPDOWN|PC_BOTTOMUP);
00103         if (oclass==NULL)
00104             GL_THROW("unable to register object class implemented by %s",__FILE__);
00105 
00106         // publish the class properties
00107         if (gl_publish_variable(oclass,
00108             PT_double,"floor_area[sf]",PADDR(floor_area),
00109             PT_double,"gross_wall_area[sf]",PADDR(gross_wall_area),
00110             PT_double,"ceiling_height[ft]",PADDR(ceiling_height),
00111             PT_double,"aspect_ratio",PADDR(aspect_ratio),
00112             PT_double,"envelope_UA[Btu/degF]",PADDR(envelope_UA),
00113             PT_double,"window_wall_ratio",PADDR(window_wall_ratio),
00114             PT_double,"glazing_shgc",PADDR(glazing_shgc),
00115             PT_double,"airchange_per_hour",PADDR(airchange_per_hour),
00116             PT_double,"total_internal_gain[Btu/h]",PADDR(total_internal_gain),
00117             PT_double,"thermostat_deadband[degF]",PADDR(thermostat_deadband),
00118             PT_double,"heating_setpoint[degF]",PADDR(heating_setpoint),
00119             PT_double,"cooling_setpoint[degF]",PADDR(cooling_setpoint),
00120             PT_double, "design_heating_capacity[Btu.h/sf]",PADDR(design_heating_capacity),
00121             PT_double,"design_cooling_capacity[Btu.h/sf]",PADDR(design_cooling_capacity),
00122             PT_double,"heating_COP",PADDR(heating_COP),
00123             PT_double,"cooling_COP",PADDR(cooling_COP),
00124             PT_double,"air_temperature[degF]",PADDR(Tair),
00125             PT_complex,"power[kW]",PADDR(power_kw),
00126             NULL)<1) 
00127             GL_THROW("unable to publish properties in %s",__FILE__);
00128 
00129         // deafults set during class creation
00130         defaults = this;
00131 
00132         // initalize published variables.  The model definition can set any of this.
00133         floor_area = 0.0;
00134         ceiling_height = 0.0;
00135         envelope_UA = 0.0;
00136         airchange_per_hour = 0.0;
00137         thermostat_deadband = 0.0;
00138         heating_setpoint = 0.0;
00139         cooling_setpoint = 0.0;
00140         window_wall_ratio = 0.0;
00141         gross_wall_area = 0.0;
00142         glazing_shgc = 0.0;
00143         design_heating_capacity = 0.0;
00144         design_cooling_capacity = 0.0;
00145         heating_COP = 3.0;
00146         cooling_COP = 3.0;
00147         aspect_ratio = 1.0;
00148 
00149         // set defaults for panel/meter variables
00150         panel.circuits=NULL;
00151         panel.max_amps=200;
00152     }   
00153 }
00154 
00155 int house::create() 
00156 {
00157     // copy the defaults
00158     memcpy(this,defaults,sizeof(*this));
00159 
00160     return 1;
00161 }
00162 
00167 int house::init_climate()
00168 {
00169     OBJECT *hdr = OBJECTHDR(this);
00170 
00171     // link to climate data
00172     static FINDLIST *climates = NULL;
00173     int not_found = 0;
00174     if (climates==NULL && not_found==0) 
00175     {
00176         climates = gl_find_objects(FL_NEW,FT_CLASS,SAME,"climate",FT_END);
00177         if (climates==NULL)
00178         {
00179             not_found = 1;
00180             gl_warning("house: no climate data found, using static data");
00181 
00182             //default to mock data
00183             static double tout=59.0, rhout=0.75, solar[8] = {0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0};
00184             pTout = &tout;
00185             pRhout = &rhout;
00186             pSolar = &solar[0];
00187         }
00188         else if (climates->hit_count>1)
00189         {
00190             gl_warning("house: %d climates found, using first one defined", climates->hit_count);
00191         }
00192     }
00193     if (climates!=NULL)
00194     {
00195         if (climates->hit_count==0)
00196         {
00197             //default to mock data
00198             static double tout=59.0, rhout=0.75, solar[8] = {0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0};
00199             pTout = &tout;
00200             pRhout = &rhout;
00201             pSolar = &solar[0];
00202         }
00203         else //climate data was found
00204         {
00205             // force rank of object w.r.t climate
00206             OBJECT *obj = gl_find_next(climates,NULL);
00207             if (obj->rank<=hdr->rank)
00208                 gl_set_dependent(obj,hdr);
00209             pTout = (double*)GETADDR(obj,gl_get_property(obj,"temperature"));
00210             pRhout = (double*)GETADDR(obj,gl_get_property(obj,"humidity"));
00211             pSolar = (double*)GETADDR(obj,gl_get_property(obj,"solar_flux"));
00212         }
00213     }
00214     return 1;
00215 }
00216 
00221 int house::init(OBJECT *parent)
00222 {
00223     // construct circuit variable map to meter
00224     struct {
00225         complex **var;
00226         char *varname;
00227     } map[] = {
00228         // local object name,   meter object name
00229         {&pCircuit_V,           "line12_voltage"}, // assumes 23 and 31 follow immediately in memory
00230         {&pLine_I,              "line1_current"}, // assumes 2 and 3(N) follow immediately in memory
00232     };
00233 
00234     static complex default_line123_voltage[3], default_line1_current[3];
00235     int i;
00236 
00237     // find parent meter, if not defined, use a default meter (using static variable 'default_meter')
00238     if (parent!=NULL && strcmp(parent->oclass->name,"meter")==0)
00239     {
00240         // attach meter variables to each circuit
00241         for (i=0; i<sizeof(map)/sizeof(map[0]); i++)
00242             *(map[i].var) = get_complex(parent,map[i].varname);
00243     }
00244     else
00245     {
00246         OBJECT *obj = OBJECTHDR(this);
00247         gl_warning("house:%d %s", obj->id, parent==NULL?"has no parent meter defined":"parent is not a meter");
00248 
00249         // attach meter variables to each circuit in the default_meter
00250             *(map[0].var) = &default_line123_voltage[0];
00251             *(map[1].var) = &default_line1_current[0];
00252 
00253         // provide initial values for voltages
00254             default_line123_voltage[0] = 240.0;
00255             default_line123_voltage[1] = -120.0;
00256             default_line123_voltage[2] = -120.0;
00257 
00258     }
00259         // Set defaults for published variables nor provided by model definition
00260     if (floor_area <= ROUNDOFF)
00261         floor_area = gl_random_uniform(2500,300);       // house size (sf) by 100 ft incs;
00262 
00263     if (ceiling_height <= ROUNDOFF)
00264         ceiling_height = 8.0;
00265 
00266     if (envelope_UA <= ROUNDOFF)
00267         envelope_UA = gl_random_uniform(0.15,0.2)*floor_area;   // UA of house envelope [BTU/h.F]
00268 
00269     if (aspect_ratio <= ROUNDOFF)
00270         aspect_ratio = 1.0;
00271 
00272     if (gross_wall_area <= ROUNDOFF)
00273         gross_wall_area = 4.0 * 2.0 * (aspect_ratio + 1.0) * ceiling_height * sqrt(floor_area/aspect_ratio);
00274 
00275     if (airchange_per_hour <= ROUNDOFF)
00276         airchange_per_hour = gl_random_uniform(4,6);            // air changes per hour [cf/h]
00277 
00278     if (thermostat_deadband <= ROUNDOFF)
00279         thermostat_deadband = 2;                            // thermostat hysteresis [F]
00280 
00281     if (heating_setpoint <= ROUNDOFF)
00282         heating_setpoint = gl_random_uniform(68,72);    // heating setpoint [F]
00283 
00284     if (cooling_setpoint <= ROUNDOFF)
00285         cooling_setpoint = gl_random_uniform(75,80);    // cooling setpoint [F]
00286 
00287     if (window_wall_ratio <= ROUNDOFF)
00288         window_wall_ratio = 0.15;                       // assuming 15% window wall ratio
00289 
00290     if (glazing_shgc <= ROUNDOFF)
00291         glazing_shgc = 0.65;                                // assuming generic double glazing
00292 
00293     if (design_cooling_capacity <= ROUNDOFF)
00294         design_cooling_capacity = gl_random_uniform(18,24); // Btuh/sf
00295 
00296     if (design_heating_capacity <= ROUNDOFF)
00297         design_heating_capacity = gl_random_uniform(18,24); // Btuh/sf
00298     
00299     
00300     // initalize/set hvac model parameters
00301     if (COP_coeff <= ROUNDOFF)
00302         COP_coeff = gl_random_uniform(0.9,1.1); // coefficient of cops [scalar]
00303     
00304     if (Tair <= ROUNDOFF)
00305         Tair = gl_random_uniform(heating_setpoint+thermostat_deadband, cooling_setpoint-thermostat_deadband);   // air temperature [F]
00306     
00307     if (over_sizing_factor <= ROUNDOFF)
00308         over_sizing_factor = gl_random_uniform(0.98,1.3);
00309 
00310     heat_cool_mode = house::OFF;                            // heating/cooling mode {HEAT, COOL, OFF}
00311 
00312     air_density = 0.0735;           // density of air [lb/cf]
00313     air_heat_capacity = 0.2402; // heat capacity of air @ 80F [BTU/lb/F]
00314 
00315     house_content_thermal_mass = 10000.0;       // thermal mass of house [BTU/F]
00316     
00317     if (house_content_heat_transfer_coeff <= ROUNDOFF)
00318         house_content_heat_transfer_coeff = gl_random_uniform(0.5,1.0)*floor_area;  // heat transfer coefficient of house contents [BTU/hr.F]
00319 
00320     //house properties for HVAC
00321     volume = 8*floor_area;                                  // volume of air [cf]
00322     air_mass = air_density*volume;                          // mass of air [lb]
00323     air_thermal_mass = air_heat_capacity*air_mass;          // thermal mass of air [BTU/F]
00324     Tmaterials = Tair;                                      // material temperture [F]
00325     hvac_rated_power = 24*floor_area*over_sizing_factor;    // rated heating/cooling output [BTU/h]
00326 
00327     if (set_Eigen_values() == FALSE)
00328         return 0;
00329 
00330     OBJECT *hdr = OBJECTHDR(this);
00331     if (hdr->latitude < 24 || hdr->latitude > 48)
00332     {
00333         // for unknown latitude, warn the user and set it midway at 36
00334         gl_error("Latitude beyond the currently supported range 24 - 48 N, Simulations will continue assuming latitude 36N");
00335         hdr->latitude = 36.0;
00336     }
00337 
00338     // attach the house HVAC to the panel
00339     attach(OBJECTHDR(this),50, TRUE);
00340     
00341     // init done ok
00342     return 1;
00343 }
00344 
00349 CIRCUIT *house::attach(OBJECT *obj, 
00350                        double breaker_amps, 
00351                        int is220) 
00352 {
00353     // construct and id the new circuit
00354     CIRCUIT *c = new CIRCUIT;
00355     if (c==NULL)
00356     {
00357         gl_error("memory allocation failure");
00358         return 0;
00359         /* Note ~ this returns a null pointer, but iff the malloc fails.  If
00360          * that happens, we're already in SEGFAULT sort of bad territory. */
00361     }
00362     c->next = panel.circuits;
00363     c->id = panel.circuits ? panel.circuits->id+1 : 1;
00364 
00365     // set the breaker capacity
00366     c->max_amps = breaker_amps;
00367 
00368     // get address of load values (if any)
00369     c->pS = get_complex(obj,"power");
00370     
00371     // choose circuit
00372     if (is220) // 220V circuit is on x12
00373     {
00374         c->type = X12;
00375         c->id++; // use two circuits
00376     }
00377     else if (c->id&0x01) // odd circuit is on x13
00378         c->type = X13;
00379     else // even circuit is on x23
00380         c->type = X23;
00381 
00382     // attach to circuit list
00383     panel.circuits = c;
00384 
00385     // get voltage
00386     c->pV = &(pCircuit_V[(int)c->type]);
00387 
00388     // close breaker
00389     c->status = BRK_CLOSED;
00390 
00391     // set breaker lifetime (at average of 3.5 ops/year, 100 seems reasonable)
00392     // @todo get data on residential breaker lifetimes (residential, low priority)
00393     c->tripsleft = 100;
00394 
00395     return c;
00396 }
00397 
00401 TIMESTAMP house::thermostat(TIMESTAMP t0, TIMESTAMP t1)
00402 {
00403     const double TcoolOn = cooling_setpoint+thermostat_deadband;
00404     const double TcoolOff = cooling_setpoint-thermostat_deadband;
00405     const double TheatOn = heating_setpoint-thermostat_deadband;
00406     const double TheatOff = heating_setpoint+thermostat_deadband;
00407 
00408     // check for deadband overlap
00409     if (TcoolOff<TheatOff)
00410     {
00411         gl_error("house: thermostat setpoints deadbands overlap (TcoolOff=%.1f < TheatOff=%.1f)", TcoolOff,TheatOff);
00412         return TS_INVALID;
00413     }
00414 
00415     // change control mode if appropriate
00416     if (Tair<=TheatOn+0.01) // heating turns on
00417         heat_cool_mode = HEAT;
00418     else if (Tair>=TheatOff-0.01 && Tair<=TcoolOff+0.01) // heating/cooling turns off
00419         heat_cool_mode = OFF;
00420     else if (Tair>=TcoolOn-0.01) // cooling turns on
00421         heat_cool_mode = COOL;
00422 
00423     return TS_NEVER;
00424 }
00425 
00429 TIMESTAMP house::presync(TIMESTAMP t0, TIMESTAMP t1) 
00430 {
00431     // reinitialize the enduse internal gains
00432     lights_heat_energy = 0.0;
00433     range_heat_energy = 0.0;
00434     occupantload_heat_energy = 0.0;
00435     plugload_heat_energy = 0.0;
00436     microwave_heat_energy = 0.0;
00437     dishwasher_heat_energy = 0.0;
00438     clotheswasher_heat_energy = 0.0;
00439     waterheater_heat_energy = 0.0;
00440 
00441     // reset power_kw for the next sync
00442     power_kw = 0;
00443     return TS_NEVER;
00444 }
00445 
00450 TIMESTAMP house::sync(TIMESTAMP t0, TIMESTAMP t1)
00451 {
00452     TIMESTAMP sync_time = TS_NEVER;
00453 
00454     total_internal_gain =   (lights_heat_energy + range_heat_energy + occupantload_heat_energy +
00455                             plugload_heat_energy + microwave_heat_energy +
00456                             dishwasher_heat_energy + clotheswasher_heat_energy + waterheater_heat_energy) * BTUPHPW;
00457 
00458     TIMESTAMP hvac_sync = sync_hvac_load(t1, (gl_tohours(t1)- gl_tohours(t0))/TS_SECOND);
00459 
00460     if (sync_time > hvac_sync)
00461         sync_time = hvac_sync;
00462 
00463     OBJECT *obj = OBJECTHDR(this);
00464 
00465     // clear accumulators for panel currents
00466     complex I[3]; I[X12] = I[X23] = I[X13] = complex(0,0);
00467 
00468     // gather load power and compute current for each circuit
00469     CIRCUIT *c;
00470     for (c=panel.circuits; c!=NULL; c=c->next)
00471     {
00472         // get circuit type
00473         int n = (int)c->type;
00474         if (n<0 || n>2)
00475             GL_THROW("%s:%d circuit %d has an invalid circuit type (%d)", obj->oclass->name, obj->id, c->id, (int)c->type);
00476 
00477         // if breaker is open and reclose time has arrived
00478         if (c->status==BRK_OPEN && t1>=c->reclose)
00479         {
00480             c->status = BRK_CLOSED;
00481             c->reclose = TS_NEVER;
00482             sync_time = t1; // must immediately reevaluate devices affected
00483             gl_debug("house:%d panel breaker %d closed", obj->id, c->id);
00484         }
00485 
00486         // if breaker is closed
00487         if (c->status==BRK_CLOSED)
00488         {
00489             // compute circuit current
00490             if ((c->pV)->Mag() == 0)
00491             {
00492                 gl_debug("house:%d circuit voltage is zero", obj->id);
00493                 break;
00494             }
00495             
00496             complex current = ~((*(c->pS)) / *(c->pV)) * 1000; // S is in kW
00497 
00498             // check breaker
00499             if (current.Mag()>c->max_amps)
00500             {
00501                 // probability of breaker failure increases over time
00502                 if (c->tripsleft>0 && gl_random_bernoulli(1/(c->tripsleft--))==0)
00503                 {
00504                     // breaker opens
00505                     c->status = BRK_OPEN;
00506 
00507                     // average five minutes before reclosing, exponentially distributed
00508                     c->reclose = t1 + (TIMESTAMP)(gl_random_exponential(1/300.0)*TS_SECOND); 
00509                     gl_debug("house:%d panel breaker %d tripped", obj->id, c->id);
00510                 }
00511 
00512                 // breaker fails from too frequent operation
00513                 else
00514                 {
00515                     c->status = BRK_FAULT;
00516                     c->reclose = TS_NEVER;
00517                     gl_debug("house:%d panel breaker %d failed", obj->id, c->id);
00518                 }
00519 
00520                 // must immediately reevaluate everything
00521                 sync_time = t1; 
00522             }
00523 
00524             // add to panel current
00525             else
00526             {
00527                 I[n] += current;
00528                 c->reclose = TS_NEVER;
00529             }
00530         }
00531 
00532         // sync time
00533         if (sync_time > c->reclose)
00534             sync_time = c->reclose;
00535 
00536     }
00537 
00539 
00540     // compute line currents and post to meter
00541     if (obj->parent != NULL)
00542         LOCK_OBJECT(obj->parent);
00543 
00544     pLine_I[0] = I[X12]-I[X13];
00545     pLine_I[1] = I[X23]-I[X12];
00546     pLine_I[2] = I[X13]-I[X23];
00547 
00548     if (obj->parent != NULL)
00549         UNLOCK_OBJECT(obj->parent);
00550 
00551     return sync_time;
00552     
00553 }
00554 
00562 TIMESTAMP house::sync_hvac_load(TIMESTAMP t1, double nHours)
00563 {
00564     // compute hvac performance
00565     const double heating_cop_adj = (-0.0063*(*pTout)+1.5984);
00566     const double cooling_cop_adj = -(-0.0108*(*pTout)+2.0389);
00567     const double heating_capacity_adj = (-0.0063*(*pTout)+1.5984);
00568     const double cooling_capacity_adj = -(-0.0063*(*pTout)+1.5984);
00569 
00570     double t = 0.0;
00571 
00572     if (heat_cool_mode == HEAT)
00573     {
00574         hvac_rated_capacity = design_heating_capacity*floor_area*heating_capacity_adj;
00575         hvac_rated_power = hvac_rated_capacity/(heating_COP * heating_cop_adj);
00576     }
00577     else if (heat_cool_mode == COOL)
00578     {
00579         hvac_rated_capacity = design_cooling_capacity*floor_area*cooling_capacity_adj;
00580         hvac_rated_power = hvac_rated_capacity/(cooling_COP * cooling_cop_adj);
00581     }
00582     else
00583     {
00584         hvac_rated_capacity = 0.0;
00585         hvac_rated_power = 0.0;
00586     }
00587 
00588     power_kw = hvac_rated_power*KWPBTUPH;
00589     hvac_kWh_use = power_kw.Mag()*nHours;  // this updates the energy usage of the elapsed time since last synch
00590 
00591     DATETIME tv;
00592     gl_localtime(t1, &tv);
00593 
00594     Tsolar = get_Tsolar(tv.hour, tv.month, Tair, *pTout);
00595     solar_load = 0.0;
00596 
00597     for (int i = 1; i<9; i++)
00598     {
00599         solar_load += (gross_wall_area*window_wall_ratio/8.0) * glazing_shgc * pSolar[i];
00600     }
00601     double netHeatrate = hvac_rated_capacity + total_internal_gain + solar_load;
00602     double Q1 = M_inv11*Tair + M_inv12*Tmaterials;
00603     double Q2 = M_inv21*Tair + M_inv22*Tmaterials;
00604 
00605     if (nHours > ROUNDOFF)
00606     {
00607         double q1 = exp(s1*nHours)*(Q1 + BB11*Tsolar/s1 + BB12*netHeatrate/s1) - BB11*Tsolar/s1 
00608             - BB12*netHeatrate/s1;
00609         double q2 = exp(s2*nHours)*(Q2 - BB11*Tsolar/s2 - BB12*netHeatrate/s2) + BB11*Tsolar/s2 
00610             + BB12*netHeatrate/s2;
00611 
00612         Tair = q1*(s1-A22)/A21 + q2*(s2-A22)/A21;
00613         Tmaterials = q1 + q2;
00614     }
00615     else
00616         return TS_NEVER;
00617 
00618     // calculate constants for solving time "t" to reach Tevent
00619     const double W = (Q1 + (BB11*Tsolar)/s1 + BB12*netHeatrate/s1)*(s1-A22)/A21;
00620     const double X = (BB11*Tsolar/s1 + BB12*netHeatrate/s1)*(s1-A22)/A21;
00621     const double Y = (Q2 - (BB11*Tsolar)/s2 - BB12*netHeatrate/s2)*(s2-A22)/A21;
00622     const double Z = (BB11*Tsolar/s2 + BB12*netHeatrate/s2)*(s2-A22)/A21;
00623     // end new solution
00624 
00625     // determine next internal event temperature
00626     int n_solutions = 0;
00627     double Tevent;
00628     const double TcoolOn = cooling_setpoint+thermostat_deadband;
00629     const double TcoolOff = cooling_setpoint-thermostat_deadband;
00630     const double TheatOn = heating_setpoint-thermostat_deadband;
00631     const double TheatOff = heating_setpoint+thermostat_deadband;
00632 
00633     /* determine the temperature of the next event */
00634 #define TPREC 0.01
00635     if (hvac_rated_capacity < 0.0)
00636         Tevent = TcoolOff;
00637     else if (hvac_rated_capacity > 0.0)
00638         Tevent = TheatOff;
00639     else if (Tair <= TheatOn+TPREC)
00640         Tevent = TheatOn;
00641     else if (Tair >= TcoolOn-TPREC)
00642         Tevent = TcoolOn;
00643     else
00644         return TS_NEVER;
00645 
00646 #ifdef OLD_SOLVER
00647     if (nHours > TPREC)
00648         // int dual_decay_solve(double *ans, double prec, double start, double end, int f, double a, double n, double b, double m, double c)
00649         n_solutions = dual_decay_solve(&t,TPREC,0.0 ,nHours,W,s1,Y,s2,Z-X-Tevent);
00650 
00651     Tair = Tevent;
00652 
00653     if (n_solutions<0)
00654         gl_error("house: solver error");
00655     else if (n_solutions == 0)
00656         return TS_NEVER;
00657     else if (t == 0)
00658         t = 1.0/3600.0;  // one second
00659     return t1+(TIMESTAMP)(t*3600*TS_SECOND);
00660 #else
00661     t =  e2solve(W,s1,Y,s2,Z-X-Tevent);
00662     if (isfinite(t))
00663         return t1+(TIMESTAMP)(t*3600*TS_SECOND);
00664     else
00665         return TS_NEVER;
00666 #endif
00667         
00668 
00669 }
00670 
00671 int house::set_Eigen_values()
00672 {
00673     // The eigen values and constants are calculated once and stored as part of the object
00674     // based on new solution to house etp network // April 06, 2004
00675 
00676     if (envelope_UA <= ROUNDOFF || house_content_heat_transfer_coeff <= ROUNDOFF || 
00677         air_thermal_mass <= ROUNDOFF || house_content_thermal_mass <= ROUNDOFF)
00678     {
00679         gl_error("House thermal mass or UA invalid.  Eigen values not set.");
00680         return FALSE;
00681     }
00682 
00683     double ra = 1/envelope_UA;
00684     double rm = 1/house_content_heat_transfer_coeff;
00685 
00686     A11 = -1.0/(air_thermal_mass*rm) - 1.0/(ra*air_thermal_mass);
00687     A12 = 1.0/(rm*air_thermal_mass);
00688     A21 = 1.0/(rm*house_content_thermal_mass);
00689     A22 = -1.0/(rm*house_content_thermal_mass);
00690 
00691     B11 = 1.0/(air_thermal_mass*ra);
00692     B12 = 1.0/air_thermal_mass;
00693     B21 = 0.0;
00694     B22 = 0.0;
00695 
00696     /* calculate eigen values */
00697     s1 = (A11 + A22 + sqrt((A11 + A22)*(A11 + A22) - 4*(A11*A22 - A21*A12)))/2.0;
00698     s2 = (A11 + A22 - sqrt((A11 + A22)*(A11 + A22) - 4*(A11*A22 - A21*A12)))/2.0;
00699 
00700     /* calculate egien vectors */
00701     double M11 = (s1 - A22)/A21;
00702     double M12 = (s2 - A22)/A21;
00703     double M21 = 1.0;
00704     double M22 = 1.0;
00705 
00706     double demoninator = (s1 - A22)/A21 - (s2-A22)/A21;
00707     M_inv11 = 1.0/demoninator;
00708     M_inv12 = -((s2 - A22)/A21)/demoninator;
00709     M_inv21 = -1.0/demoninator;
00710     M_inv22 = ((s1-A22)/A21)/demoninator;
00711 
00712     BB11 = M_inv11*B11 + M_inv12*B21;
00713     BB21 = M_inv21*B11 + M_inv22*B21;
00714     BB12 = M_inv11*B12 + M_inv12*B22;
00715     BB22 = M_inv21*B12 + M_inv22*B22;
00716 
00717     return TRUE;
00718 }
00724 double house::get_Tsolar(int hour, int month, double Tair, double Tout)
00725 {
00726     // Wood frame wall CLTD values from ASHRAE 1989 (for sunlighted walls in the north latitude)
00727     static double CLTD[] = {4.25, 2.75, 1.63, 0.50, -0.50, 3.50, 11.25, 17.88, 22.50, 25.88, 27.88, 29.25, 31.63, 35.13, 38.50, 40.38, 36.88, 28.00, 19.00, 14.00, 11.13, 8.63, 6.25};
00728 
00729     static double LM[4][24] =   {   
00730         {-1.33, -1.44, -0.89, -1.00, -0.67, -0.44, -0.67, -1.00, -0.89, -1.44, -1.33, -1.22},  // latitude 24
00731         {-2.89, -1.89, -0.78, -0.44, -0.11, -0.11, -0.11, -0.44, -0.78, -1.89, -2.89, -4.67},  // latitude 32
00732         {-5.44, -3.22, -1.11, -0.11, 0.22, 0.67, 0.22, -0.11, -1.11, -3.22, -5.44, -6.33},  // latitude 40
00733         {-7.56, -5.11, -1.78, -0.11, 1.33, 2.00, 1.33, -0.11, -1.78, -5.11, -7.56} // latitude 48
00734     };
00735 
00736     static double ColorSurface = 0.75;
00737     static double DR = 15.0;
00738     double solarTemp = Tair;
00739     double LMnow = 0.0;
00740     int LMcol = month-1;
00741 
00742     OBJECT *hdr = OBJECTHDR(this);
00743 
00744     if (hdr->latitude <= 24.0)
00745         LMnow = LM[0][LMcol];
00746     else if (hdr->latitude <= 32.)
00747         LMnow = LM[0][LMcol] + ((LM[1][LMcol]-LM[0][LMcol])*(hdr->latitude-24.0)/12.0);
00748     else if (hdr->latitude <= 40.)
00749         LMnow = LM[1][LMcol] + ((LM[2][LMcol]-LM[1][LMcol])*(hdr->latitude-32.0)/12.0);
00750     else if (hdr->latitude <= 48.)
00751         LMnow = LM[2][LMcol] + ((LM[3][LMcol]-LM[2][LMcol])*(hdr->latitude-40.0)/12.0);
00752     else // if (hdr->latitude > 48.0)
00753         LMnow = LM[3][LMcol];
00754 
00755     solarTemp += (CLTD[hour] + LMnow)*ColorSurface + (78. - Tair) + ((*pTout) - DR/2. - 85.);
00756 
00757     return solarTemp;
00758 }
00759 
00760 
00761 
00762 complex *house::get_complex(OBJECT *obj, char *name)
00763 {
00764     PROPERTY *p = gl_get_property(obj,name);
00765     if (p==NULL || p->ptype!=PT_complex)
00766         return NULL;
00767     return (complex*)GETADDR(obj,p);
00768 }
00769 
00771 // IMPLEMENTATION OF CORE LINKAGE
00773 
00774 EXPORT int create_house(OBJECT **obj, OBJECT *parent)
00775 {
00776     *obj = gl_create_object(house::oclass,sizeof(house));
00777     if (*obj!=NULL)
00778     {
00779         house *my = OBJECTDATA(*obj,house);;
00780         gl_set_parent(*obj,parent);
00781         my->create();
00782         return 1;
00783     }
00784     return 0;
00785 }
00786 
00787 EXPORT int init_house(OBJECT *obj)
00788 {
00789     house *my = OBJECTDATA(obj,house);
00790     my->init_climate();
00791     return my->init(obj->parent);
00792 }
00793 
00794 EXPORT TIMESTAMP sync_house(OBJECT *obj, TIMESTAMP t0, PASSCONFIG pass)
00795 {
00796     house *my = OBJECTDATA(obj,house);
00797     TIMESTAMP t1 = TS_NEVER;
00798     if (obj->clock <= ROUNDOFF)
00799         obj->clock = t0;  //set the object clock if it has not been set yet
00800 
00801     try {
00802         switch (pass) 
00803         {
00804             case PC_PRETOPDOWN:
00805                 t1 = my->presync(obj->clock, t0);
00806                 break;
00807 
00808             case PC_BOTTOMUP:
00809                 t1 = my->sync(obj->clock, t0);
00810                 obj->clock = t0;
00811                 break;
00812 
00813             default:
00814                 gl_error("house::sync- invalid pass configuration");
00815                 t1 = TS_INVALID; // serious error in exec.c
00816         }
00817     } 
00818     catch (char *msg)
00819     {
00820         gl_error("house::sync exception caught: %s", msg);
00821         t1 = TS_INVALID;
00822     }
00823     catch (...)
00824     {
00825         gl_error("house::sync exception caught: no info");
00826         t1 = TS_INVALID;
00827     }
00828     return t1;
00829 }
00830 
00831 EXPORT TIMESTAMP plc_house(OBJECT *obj, TIMESTAMP t0)
00832 {
00833     // this will be disabled if a PLC object is attached to the waterheater
00834     if (obj->clock <= ROUNDOFF)
00835         obj->clock = t0;  //set the clock if it has not been set yet
00836 
00837     house *my = OBJECTDATA(obj,house);
00838     return my->thermostat(obj->clock, t0);
00839 }
00840 

GridLAB-DTM Version 1.0
An open-source project initiated by the US Department of Energy