EXPORT TIMESTAMP sync_controller(OBJECT *obj, TIMESTAMP t1, PASSCONFIG pass) { TIMESTAMP t2 = TS_NEVER; controller *my = OBJECTDATA(obj,controller); try { switch (pass) { case PC_PRETOPDOWN: t2 = my->presync(obj->clock,t1); //obj->clock = t1; break; case PC_BOTTOMUP: t2 = my->sync(obj->clock, t1); //obj->clock = t1; break; case PC_POSTTOPDOWN: t2 = my->postsync(obj->clock,t1); obj->clock = t1; break; default: gl_error("invalid pass request (%d)", pass); return TS_INVALID; break; } } catch (const char *msg) { char name[64]; gl_error("sync_controller(obj=%s): %s", gl_name(obj,name,sizeof(name)), msg); t2 = TS_INVALID; } return t2; }
/** Initialize water heater model properties - randomized defaults for all published variables **/ int virtual_battery::init(OBJECT *parent) { parent2=parent; first_time=0; first_time2=0; actual=0; temp_capacity=0; if(parent != NULL){ if((parent->flags & OF_INIT) != OF_INIT){ char objname[256]; gl_verbose("virtual_battery::init(): deferring initialization on %s", gl_name(parent, objname, 255)); return 2; // defer } } OBJECT *hdr = OBJECTHDR(this); hdr->flags |= OF_SKIPSAFE; ///////////////////////////////find auction object///////////////////////////////////// static FINDLIST *xt1=NULL; xt1=gl_find_objects(FL_NEW,FT_CLASS,SAME,"auction",FT_END); OBJECT *firstt1= gl_find_next(xt1,NULL); OBJECT *it1; for(it1=firstt1;it1!=NULL;it1=it1->next) { if(gl_object_isa(it1,"auction")) { auction_object=it1; } } ///////////////////////////////////////////////////////////////////////////////////////// /////////////////////////////find climate object /////////////////////////////////////// FINDLIST *climates = NULL; OBJECT *obj; climates = gl_find_objects(FL_NEW,FT_CLASS,SAME,"climate",FT_END); obj = gl_find_next(climates,NULL); if (gl_object_isa(obj,"climate")) { climate_object=obj; } /////////////////////////////////////////////////////////////////////////////////////// // check the load configuration before initializing the parent class return 1; }
int occupantload::init(OBJECT *parent) { if(parent != NULL){ if((parent->flags & OF_INIT) != OF_INIT){ char objname[256]; gl_verbose("occupantload::init(): deferring initialization on %s", gl_name(parent, objname, 255)); return 2; // defer } } if (number_of_occupants==0) number_of_occupants = 4; // defaulted to 4, but perhaps define it based on house size?? if (heatgain_per_person==0) heatgain_per_person = 400.0; // Based on DOE-2, includes latent and sensible heatgain OBJECT *hdr = OBJECTHDR(this); hdr->flags |= OF_SKIPSAFE; if (parent==NULL || (!gl_object_isa(parent,"house") && !gl_object_isa(parent,"house_e"))) { gl_error("occupantload must have a parent house"); /* TROUBLESHOOT The occupantload object, being an enduse for the house model, must have a parent house that it is connected to. Create a house object and set it as the parent of the offending occupantload object. */ return 0; } // pull parent attach_enduse and attach the enduseload FUNCTIONADDR attach = 0; load.end_obj = hdr; attach = (gl_get_function(parent, "attach_enduse")); if(attach == NULL){ gl_error("occupantload parent must publish attach_enduse()"); /* TROUBLESHOOT The occupantload object attempt to attach itself to its parent, which must implement the attach_enduse function. */ return 0; } // Needed to pass heat gain up to the house // "true" on 220 keeps the circuits "balanced" ((CIRCUIT *(*)(OBJECT *, ENDUSELOAD *, double, int))(*attach))(hdr->parent, &(this->load), 20, true); load.heatgain = number_of_occupants * occupancy_fraction * heatgain_per_person; if(shape.type != MT_UNKNOWN && shape.type != MT_ANALOG){ char outname[64]; if(hdr->name){ //sprintf(outname, "%s", hdr->name); } else { sprintf(outname, "occupancy_load:%i", hdr->id); } gl_warning("occupancy_load \'%s\' may not work properly with a non-analog load shape.", hdr->name ? hdr->name : outname); } return 1; }
EXPORT int init_stub_bidder(OBJECT *obj, OBJECT *parent) { try { if (obj!=NULL){ return OBJECTDATA(obj,stub_bidder)->init(parent); } } catch (const char *msg) { char name[64]; gl_error("init_stub_bidder(obj=%s): %s", gl_name(obj,name,sizeof(name)), msg); return 0; } return 1; }
int microwave::init(OBJECT *parent) { if(parent != NULL){ if((parent->flags & OF_INIT) != OF_INIT){ char objname[256]; gl_verbose("microwave::init(): deferring initialization on %s", gl_name(parent, objname, 255)); return 2; // defer } } OBJECT *hdr = OBJECTHDR(this); hdr->flags |= OF_SKIPSAFE; if (load.voltage_factor==0) load.voltage_factor = 1.0; if(shape.type == MT_UNKNOWN){ init_noshape(); gl_warning("This device, %s, is considered very experimental and has not been validated.", get_name()); // initial demand update_state(0.0); } else if(shape.type == MT_ANALOG){ if(1){ ; } } else if(shape.type == MT_PULSED){ if(1){ ; } } else if(shape.type == MT_MODULATED){ if(1){ ; } } else if(shape.type == MT_QUEUED){ gl_error("queued loadshapes not supported ~ will attempt to run as an unshaped load"); shape.type = MT_UNKNOWN; init_noshape(); // initial demand update_state(0.0); } else { gl_error("unrecognized loadshape"); return 0; } load.total = load.power = standby_power; // waiting this long to initialize the parent class is normal return residential_enduse::init(parent); }
int dishwasher::init(OBJECT *parent) { // @todo This class has serious problems and should be deleted and started from scratch. Fuller 9/27/2013. OBJECT *hdr = OBJECTHDR(this); if(parent != NULL){ if((parent->flags & OF_INIT) != OF_INIT){ char objname[256]; gl_verbose("dishwasher::init(): deferring initialization on %s", gl_name(parent, objname, 255)); return 2; // defer } } int rv = 0; // default properties if (motor_power==0) motor_power = gl_random_uniform(&hdr->rng_state,150,350); if (heat_fraction==0) heat_fraction = 0.2; if (is_240) { load.config = EUC_IS220; if (stall_voltage==0) stall_voltage = 0.6*240; } else if (stall_voltage==0) stall_voltage = 0.6*120; if (trip_delay==0) trip_delay = 10; if (reset_delay==0) reset_delay = 60; count_motor_only = 0; count_motor_only1 =0; count_motor_only_25 = 0; count_coil_only = 0; count_control_only =0; count_control_only1 =0; count_motor_only_68 =0; pulse_interval[0] = 8; pulse_interval[1] = 18; pulse_interval[2] = 21; pulse_interval[3] = 28; pulse_interval[4] = 38; pulse_interval[5] = 50; pulse_interval[6] = 65; pulse_interval[7] = 118; pulse_interval[8] = 150; pulse_interval[9] = 220; pulse_interval[10] = 320; pulse_interval[11] = 355; pulse_interval[12] = 460; pulse_interval[13] = 580; pulse_interval[14] = 615; pulse_interval[15] = 780; pulse_interval[16] = 800; pulse_interval[17] = 950; pulse_interval[18] = 1150; if (coil_power[0]==-1) coil_power[0] = 5800; coil_power[0] = 10; coil_power[1] = 580; coil_power[2] = 695; coil_power[3] = 950; motor_power = 250; enduse_queue = 1; queue_min = 0; queue_max = 2; control_check1 = false; control_check2 = false; control_check3 = false; control_check4 = false; control_check5 = false; control_check6 = false; control_check7 = false; control_check8 = false; control_check9 = false; control_check10 = false; control_check11 = false; control_check12 = false; motor_only_check1 = false; motor_only_check2 = false; motor_only_check3 = false; motor_only_check4 = false; motor_only_check5 = false; motor_only_check6 = false; motor_only_check7 = false; motor_only_check8 = false; motor_only_check9 = false; motor_coil_only_check1 = false; motor_coil_only_check2 = false; heateddry_check1 = false; heateddry_check2 = false; coil_only_check1 = false; coil_only_check2 = false; coil_only_check3 = false; //Heateddry_option_check = false; hdr->flags |= OF_SKIPSAFE; load.power_factor = 0.95; load.breaker_amps = 30; actual_dishwasher_demand = 0; energy_needed = 0; rv = residential_enduse::init(parent); if (rv==SUCCESS) update_state(0.0); /* schedule checks */ switch(shape.type){ case MT_UNKNOWN: gl_warning("This device, %s, is considered very experimental and has not been validated.", get_name()); break; case MT_ANALOG: if(shape.params.analog.energy == 0.0){ GL_THROW("dishwasher does not support fixed energy shaping"); } else if (shape.params.analog.power == 0){ daily_dishwasher_demand = gl_get_loadshape_value(&shape) / 2.4449; } else { daily_dishwasher_demand = gl_get_loadshape_value(&shape); /* unitless ~ drive gpm */ } break; case MT_PULSED: /* pulsed loadshapes "emit one or more pulses at random times s. t. the total energy is accumulated over the period of the loadshape". * pulsed loadshapes can either user time or kW values per pulse. */ if(shape.params.pulsed.pulsetype == MPT_TIME){ ; /* constant time pulse ~ consumes X gallons to drive heater for Y hours ~ but what's Vdot, what's t? */ } else if(shape.params.pulsed.pulsetype == MPT_POWER){ ; /* constant power pulse ~ dishwasher demand X kW, limited by C + Q * h ~ Vdot proportional to power/time */ daily_dishwasher_demand = gl_get_loadshape_value(&shape) / 2.4449; } break; case MT_MODULATED: if(shape.params.modulated.pulsetype == MPT_TIME){ GL_THROW("Amplitude modulated dishwasher usage is nonsensical for residential dishwashers"); /* TROUBLESHOOT Though it is possible to put a constant, low-level dishwasher demand, it is thoroughly counterintuitive to the normal usage of the dishwasher. */ } else if(shape.params.modulated.pulsetype == MPT_POWER){ /* frequency modulated */ /* fixed-amplitude, varying length pulses at regular intervals. */ daily_dishwasher_demand = gl_get_loadshape_value(&shape) / 2.4449; } break; case MT_QUEUED: if(shape.params.queued.pulsetype == MPT_TIME){ ; /* constant time pulse ~ consumes X gallons/minute to consume Y thermal energy */ } else if(shape.params.queued.pulsetype == MPT_POWER){ ; /* constant power pulse ~ dishwasher demand X kW, limited by C + Q * h */ daily_dishwasher_demand = gl_get_loadshape_value(&shape) / 2.4449; } break; default: GL_THROW("dishwasher load shape has an unknown state!"); break; } return residential_enduse::init(parent); //} // must run before update_state() so that pCircuit can be set // return rv; }
int ZIPload::init(OBJECT *parent) { ////////////////////////////////////SEARCH//////////////////////////////////// static FINDLIST *xt2=NULL; xt2=gl_find_objects(FL_NEW,FT_CLASS,SAME,"irrigation_controller",FT_END); OBJECT *firstt2= gl_find_next(xt2,NULL); OBJECT *it2; for(it2=firstt2;it2!=NULL;it2=it2->next) { if(gl_object_isa(it2,"irrigation_controller")) { if(OBJECTHDR(this)->id==it2->parent->id) { irrigation_contr_object=it2; // printf("%d %d",OBJECTHDR(this)->id,irrigation_contr_object->id); //system("pause"); } else { // irrigation_contr_object=NULL; } } } static FINDLIST *xt1=NULL; xt1=gl_find_objects(FL_NEW,FT_CLASS,SAME,"auction",FT_END); OBJECT *firstt1= gl_find_next(xt1,NULL); OBJECT *it1; for(it1=firstt1;it1!=NULL;it1=it1->next) { if(gl_object_isa(it1,"auction")) { auction_object=it1; } } ////////////////////////////////////////////////////////////////////////////// if(parent != NULL){ if((parent->flags & OF_INIT) != OF_INIT){ char objname[256]; gl_verbose("zipload::init(): deferring initialization on %s", gl_name(parent, objname, 255)); return 2; // defer } } OBJECT *hdr = OBJECTHDR(this); hdr->flags |= OF_SKIPSAFE; if (demand_response_mode == true) { gl_warning("zipload_init: The demand response zipload is an experimental model at this time."); /* TROUBLESHOOT The warning is pretty obvious. However, over time, we will be developing this model further. If you have any questions about it, please see the Matlab files found in ../design/dr_model/ or read the paper titled "On the Equilibrium Dynamics of Demand Response" submitted to HICSS 2011 or post your questions on the WIKI forum. */ // Initial error checks if (abs(eta) > 1) { GL_THROW("zipload_init: demand_rate (eta) must be between -1 and 1."); /* TROUBLESHOOT The demand rate is limited to values between -1 and 1 (inclusive). Please reset to an appropriate value. */ } if (phi < 0 || phi > 1) { GL_THROW("zipload_init: duty_cycle (phi) must be between 0 and 1."); /* TROUBLESHOOT The duty cycle is only explicitly used if ron and roff are not set. In normal operation, phi will be calculated from ron and roff as a function of time. However, if ron and roff are not set, the initial values for ron and roff are calculated from phi. Please set to a value between 1 and 0 (inclusive). */ } // Set up the buffers and perform some error checks if (L > 0) if (L < 70) drm.nbins = L; else { gl_warning("zipload_init: Using a value for thermostatic_control_range (L) greater than 50 may cause some instabilities."); /* TROUBLESHOOT This warning is shown only as a reminder. Large values of L (thermostatic_control_range) can cause instabilities for some combinations of ron and roff. If you receive inderminant numbers as part of the solution, try reducing L. */ } else { GL_THROW("zipload_init: thermostatic_control_range (L) must be greater than 0."); /* TROUBLESHOOT The thermostatic control range must be a positive integer value, since this is used to create the number of bins for the discrete solution. */ } drm.off = (double*)malloc(sizeof(double)*L); drm.on = (double*)malloc(sizeof(double)*L); if (drm.off==NULL || drm.on==NULL) { GL_THROW("zipload_init: memory allocation error. Please report this error."); /* TROUBLESHOOT If you receive this error, something went horribly wrong with a memory allocation. Please report this to TRAC and provide the glm file that caused it. */ } /* setup the initial population */ if (ron == -1 || roff == -1) { if (phi <= 0.5) { roff = phi/(1-phi); ron = 1; gl_warning("ron or roff was not set. Using phi to calculate. Step changes in demand rates as a function of time will be ignored."); /* TROUBLESHOOT Ideally, in the method currently being used, ron and roff (heating and cooling rates) should be used to calculate phi. If you receive this error, the phi is being used to calculate ron and roff initially. However, phi does not update ron and roff at each time step, so you will not be able to perform disturbances of demand. If you wish this, please use ron and roff as a function of time instead. This can also be caused by using a schedule or player transform to describe the ron or roff values - essentially during the initialization, the value is not described yet. There is no current fix for this, but can be "faked" by setting phi to the correct initial value and waiting a couple of timesteps for things to settle. */ } else { roff = 1; ron = (1-phi)/phi; gl_warning("ron or roff was not set. Using phi to calculate. Step changes in demand rates as a function of time will be ignored."); /* TROUBLESHOOT Ideally, in the method currently being used, ron and roff (heating and cooling rates) should be used to calculate phi. If you receive this error, the phi is being used to calculate ron and roff initially. However, phi does not update ron and roff at each time step, so you will not be able to perform disturbances of demand. If you wish this, please use ron and roff as a function of time instead. This can also be caused by using a schedule or player transform to describe the ron or roff values - essentially during the initialization, the value is not described yet. There is no current fix for this, but can be "faked" by setting phi to the correct initial value and waiting a couple of timesteps for things to settle. */ } } else phi = roff / (ron + roff); if (roff < 0 || ron < 0) { GL_THROW("zipload_init: rate_of_cooling and rate_of_heating must be greater than or equal to 0."); /* TROUBLESHOOT Rates of heating and cooling should be positive or zero values. These values describe how fast objects transition from a cooler to hotter temperature, or vice versa, and have been defined as positive values in this model. */ } non = noff = 0; double test_N = 0; for (x=0; x<L; x++) { /* exponential distribution */ if (eta != 0) { drm.on[x] = N * eta * (1-phi) * exp(eta*(L-0.5-x)/roff) / (exp(eta*L/roff)-1); drm.off[x] = drm.on[x] * ron/roff; test_N += drm.on[x] + drm.off[x]; //non += drm.on[x] = eta * (1-phi) * exp(eta*(L-x+0.5)/roff) / (exp(eta*L/roff)-1); //noff += drm.off[x] = drm.on[x]*ron/roff; } /* uniform distribution */ else { non += drm.on[x] = N * phi/L; noff += drm.off[x] = N * (1-phi)/L; printf("testsfsdfsdfs : : : ;%f %f",non,noff); } } /* check for valid population */ if (abs(test_N - N) != 0) { double extra = N - test_N; drm.off[0] += extra; } } if (duty_cycle > 1 || duty_cycle < 0) { if (duty_cycle != -1) { GL_THROW("Value of duty cycle is set to a value outside of 0-1."); /* TROUBLESHOOT By definition, a duty cycle must be between, and including, 0 and 1. Zero will turn the duty cycle function OFF. Please specify a duty_cycle value between 0 and 1. */ } } // We're using a duty cycle mode if (duty_cycle != -1) { if (period <= 0) { GL_THROW("Period is less than or equal to zero."); /* TROUBLESHOOT When using duty cycle mode, the period must be a positive value. */ } if (phase < 0 || phase > 1) { GL_THROW("Phase is not between zero and one."); /* TROUBLESHOOT When using duty cycle mode, the phase must be specified as a value between 0 and 1. This will set the initial phase as a percentage of the period. The "duty" will assume to be applied at the beginning of each period. Randomizing this input value will prevent syncing of objects. */ } } if (heatgain_only == true) { load.config = EUC_HEATLOAD; load.power_fraction = load.current_fraction = load.impedance_fraction = 0.0; } if (is_240) //See if the 220/240 flag needs to be set { load.config |= EUC_IS220; } load.breaker_amps = breaker_val; only_once=0; first_time=0; return residential_enduse::init(parent); }
// Initialize a distribution meter, return 1 on success int substation::init(OBJECT *parent) { OBJECT *hdr = OBJECTHDR(this); int i,n; //Base check higher so can be used below if(base_power <= 0){ gl_warning("substation:%i is using the default base power of 100 VA. This could cause instability on your system.", hdr->id); base_power = 100;//default gives a max power error of 1 VA. } //Check convergence-posting criterion if (power_convergence_value<=0.0) { gl_warning("power_convergence_value not set - defaulting to 0.01 base_power"); /* TROUBLESHOOT A value was not specified for the convergence criterion required before posting an answer up to pw_load. This value has defaulted to 1% of base_power. If a different threshold is desired, set it explicitly. */ power_convergence_value = 0.01*base_power; }//End convergence value check //Check to see if it has a parent (no sense to ISAs if it is empty) if (parent != NULL) { if (gl_object_isa(parent,"pw_load","network")) { //Make sure it is done, otherwise defer if((parent->flags & OF_INIT) != OF_INIT){ char objname[256]; gl_verbose("substation::init(): deferring initialization on %s", gl_name(parent, objname, 255)); return 2; // defer } //Map up the appropriate variables- error checks mostly inside fetch_complex(&pPositiveSequenceVoltage,"load_voltage",parent); fetch_complex(&pConstantPowerLoad,"load_power",parent); fetch_complex(&pConstantCurrentLoad,"load_current",parent); fetch_complex(&pConstantImpedanceLoad,"load_impedance",parent); fetch_double(&pTransNominalVoltage,"bus_nom_volt",parent); //Do a general check for nominal voltages - make sure they match if (fabs(*pTransNominalVoltage-nominal_voltage)>0.001) { gl_error("Nominal voltages of tranmission node (%.1f V) and substation (%.1f) do not match!",*pTransNominalVoltage,nominal_voltage); /* TROUBLESHOOT The nominal voltage of the transmission node in PowerWorld does not match that of the value inside GridLAB-D's substation's nominal_voltage. This could cause information mismatch and is therefore not allowed. Please set the substation to the same nominal voltage as the transmission node. Use transformers to step the voltage down to an appropriate distribution or sub-transmission level. */ return 0; //Fail } //Check our bustype - otherwise we may get overridden (NR-esque check) if (bustype != SWING) { gl_warning("substation attached to pw_load and not a SWING bus - forcing to SWING"); /* TROUBLESHOOT When a substation object is connected to PowerWorld via a pw_load object, the substation must be designated as a SWING bus. This designation will now be forced upon the bus. */ bustype = SWING; }//End bus check //Flag us as pw_load connected has_parent = 1; } else //Parent isn't a pw_load, so we just become a normal node - let it handle things { has_parent = 2; //Flag as "normal" - let node checks sort out if we are really valid or not } }//End parent else //Parent is null, see what mode we're in { //Check for sequence voltages - if not set, we're normal (let node checks handle if we're valid) if ((seq_mat[0] != 0.0) || (seq_mat[1] != 0.0) || (seq_mat[2] != 0.0)) { //See if we're a swing, if not, this is meaningless if (bustype != SWING) { gl_warning("substation is not a SWING bus, so answers may be unexpected"); /* TROUBLESHOOT A substation object appears set to accept sequence voltage values, but it is not a SWING bus. This may end up causing the voltages to be converted from sequence, but then overridden by the distribution powerflow solver. */ } //Explicitly set has_parent = 0; } else //Else, nothing set, we're a normal old node { has_parent = 2; //Normal node //Warn that nothing was found gl_warning("substation:%s is set up as a normal node, no sequence values will be calculated",hdr->name); /* TROUBLESHOOT A substation is currently behaving just like a normal powerflow node. If it was desired that it convert a schedule or player of sequence values, please initialize those values to non-zero along with the player attachment. */ } }//End no parent //Set up reference items if they are needed if (has_parent != 2) //Not a normal node { //New requirement to maintain positive sequence ability - three phases must be had, unless //we're just a normal node. Then, we don't care. if (!has_phase(PHASE_A|PHASE_B|PHASE_C)) { gl_error("substation needs to have all three phases!"); /* TROUBLESHOOT To meet the requirements for sequence voltage conversions, the substation node must have all three phases at the connection point. If only a single phase or subset of full three phase is needed, those must be set in the distribution network, typically after a delta-ground wye transformer. */ return 0; } }//End not a normal node //set the reference phase number to shift the phase voltages appropriatly with the positive sequence voltage if(reference_phase == R_PHASE_A){ reference_number.SetPolar(1,0); } else if(reference_phase == R_PHASE_B){ reference_number.SetPolar(1,2*PI/3); } else if(reference_phase == R_PHASE_C){ reference_number.SetPolar(1,-2*PI/3); } //create the sequence to phase transformation matrix for(i=0; i<3; i++){ for(n=0; n<3; n++){ if((i==1 && n==1) || (i==2 && n==2)){ transformation_matrix[i][n].SetPolar(1,-2*PI/3); } else if((i==2 && n==1) || (i==1 && n==2)){ transformation_matrix[i][n].SetPolar(1,2*PI/3); } else { transformation_matrix[i][n].SetPolar(1,0); } } } return node::init(parent); }
/** Checks for climate object and maps the climate variables to the house object variables. Currently Tout, RHout and solar_service flux data from TMY files are used. If no climate object is linked, then Tout will be set to 59 degF, RHout is set to 75% and solar_service flux will be set to zero for all orientations. **/ int solar_service::init_climate() { OBJECT *hdr = OBJECTHDR(this); OBJECT *obj = NULL; // link to climate data FINDLIST *climates = NULL; if (solar_service_model_tilt != PLAYERVAL) { if (weather!=NULL) { if(!gl_object_isa(weather, "climate")){ // strcmp failure gl_error("weather property refers to a(n) \"%s\" object and not a climate object", weather->oclass->name); /* TROUBLESHOOT While attempting to map a climate property, the solar_service array encountered an object that is not a climate object. Please check to make sure a proper climate object is present, and/or specified. If the bug persists, please submit your code and a bug report via the trac website. */ return 0; } obj = weather; } else //No weather specified, search { climates = gl_find_objects(FL_NEW,FT_CLASS,SAME,"climate",FT_END); if (climates==NULL) { //Ensure weather is set to NULL - catch below weather = NULL; } else if (climates->hit_count==0) { //Ensure weather is set to NULL - catch below weather = NULL; } else //climate data must have been found { if (climates->hit_count>1) { gl_warning("solar_servicepanel: %d climates found, using first one defined", climates->hit_count); /* TROUBLESHOOT More than one climate object was found, so only the first one will be used by the solar_service array object */ } gl_verbose("solar_service init: climate data was found!"); // force rank of object w.r.t climate obj = gl_find_next(climates,NULL); weather = obj; } } //Make sure it actually found one if (weather == NULL) { //Replicate above warning gl_warning("solar_servicepanel: no climate data found, using static data"); /* TROUBLESHOOT No climate object was found and player mode was not enabled, so the solar_service array object is utilizing default values for all relevant weather variables. */ //default to mock data static double tout=59.0, rhout=0.75, solar_service=92.902, wsout=0.0, albdefault=0.2; pTout = &tout; pRhout = &rhout; psolar_serviceD = &solar_service; //Default all solar_service values to normal "optimal" 1000 W/m^2 psolar_serviceH = &solar_service; psolar_serviceG = &solar_service; pAlbedo = &albdefault; pWindSpeed = &wsout; if (orientation_type==FIXED_AXIS) { GL_THROW("FIXED_AXIS requires a climate file!"); /* TROUBLESHOOT The FIXED_AXIS model for the PV array requires climate data to properly function. Please specify such data, or consider using a different tilt model. */ } } else if (!gl_object_isa(weather,"climate")) //Semi redundant for "weather" { GL_THROW("weather object is not a climate object!"); /* TROUBLESHOOT The object specified for the weather property is not a climate object and will not work with the solar_service object. Please specify a valid climate object, or let the solar_service object automatically connect. */ } else //Must be a proper object { if((obj->flags & OF_INIT) != OF_INIT){ char objname[256]; gl_verbose("solar_service::init(): deferring initialization on %s", gl_name(obj, objname, 255)); return 2; // defer } if (obj->rank<=hdr->rank) gl_set_dependent(obj,hdr); pTout = (double*)GETADDR(obj,gl_get_property(obj,"temperature")); // pRhout = (double*)GETADDR(obj,gl_get_property(obj,"humidity")); <---- Not used anywhere yet psolar_serviceD = (double*)GETADDR(obj,gl_get_property(obj,"solar_service_direct")); psolar_serviceH = (double*)GETADDR(obj,gl_get_property(obj,"solar_service_diffuse")); psolar_serviceG = (double*)GETADDR(obj,gl_get_property(obj,"solar_service_global")); pAlbedo = (double*)GETADDR(obj,gl_get_property(obj,"ground_reflectivity")); pWindSpeed = (double*)GETADDR(obj,gl_get_property(obj,"wind_speed")); //Should probably check these if (pTout==NULL) { GL_THROW("Failed to map outside temperature"); /* TROUBLESHOOT The solar_service PV array failed to map the outside air temperature. Ensure this is properly specified in your climate data and try again. */ } //No need to error check - doesn't exist in any formulations yet //if (pRhout==NULL) //{ // GL_THROW("Failed to map outside relative humidity"); // /* TROUBLESHOOT // The solar_service PV array failed to map the outside relative humidity. Ensure this is // properly specified in your climate data and try again. // */ //} if (psolar_serviceD==NULL) { GL_THROW("Failed to map direct normal solar_service radiation"); /* TROUBLESHOOT The solar_service PV array failed to map the solar_service direct normal radiation. Ensure this is properly specified in your climate data and try again. */ } if (psolar_serviceH==NULL) { GL_THROW("Failed to map diffuse horizontal solar_service radiation"); /* TROUBLESHOOT The solar_service PV array failed to map the solar_service diffuse horizontal radiation. Ensure this is properly specified in your climate data and try again. */ } if (psolar_serviceG==NULL) { GL_THROW("Failed to map global horizontal solar_service radiation"); /* TROUBLESHOOT The solar_service PV array failed to map the solar_service global horizontal radiation. Ensure this is properly specified in your climate data and try again. */ } if (pAlbedo==NULL) { GL_THROW("Failed to map albedo/ground reflectance"); /* TROUBLESHOOT The solar_service PV array failed to map the ground reflectance. Ensure this is properly specified in your climate data and try again. */ } if (pWindSpeed==NULL) { GL_THROW("Failed to map wind speed"); /* TROUBLESHOOT The solar_service PV array failed to map the wind speed. Ensure this is properly specified in your climate data and try again. */ } //If climate data was found, check other related variables if (fix_angle_lat==true) { if (obj->latitude < 0) //Southern hemisphere { //Get the latitude from the climate file tilt_angle = -obj->latitude; } else //Northern { //Get the latitude from the climate file tilt_angle = obj->latitude; } } //Check the tilt angle for absurdity if (tilt_angle < 0) { GL_THROW("Invalid tilt_angle - tilt must be between 0 and 90 degrees"); /* TROUBLESHOOT A negative tilt angle was specified. This implies the array is under the ground and will not receive any meaningful solar_service irradiation. Please correct the tilt angle and try again. */ } else if (tilt_angle > 90.0) { GL_THROW("Invalid tilt angle - values above 90 degrees are unsupported!"); /* TROUBLESHOOT An tilt angle over 90 degrees (straight up and down) was specified. Beyond this angle, the tilt algorithm does not function properly. Please specific the tilt angle between 0 and 90 degrees and try again. */ } //Check the solar_service method if (orientation_type == FIXED_AXIS) { //See which function we want to use if (solar_service_model_tilt==LIUJORDAN) { //Map up the "classic" function calc_solar_service_radiation = (FUNCTIONADDR)(gl_get_function(obj,"calculate_solar_service_radiation_shading_degrees")); } else if (solar_service_model_tilt==SOLPOS) //Use the solpos/Perez tilt model { //Map up the "classic" function calc_solar_service_radiation = (FUNCTIONADDR)(gl_get_function(obj,"calc_solpos_radiation_shading_degrees")); } //Make sure it was found if (calc_solar_service_radiation == NULL) { GL_THROW("Unable to map solar_service radiation function on %s in %s",obj->name,hdr->name); /* TROUBLESHOOT While attempting to initialize the photovoltaic array mapping of the solar_service radiation function. Please try again. If the bug persists, please submit your GLM and a bug report via the trac website. */ } //Check azimuth for absurdity as well if ((orientation_azimuth<0.0) || (orientation_azimuth > 360.0)) { GL_THROW("orientation_azimuth must be a value representing a valid cardinal direction of 0 to 360 degrees!"); /* TROUBLESHOOT The orientation_azimuth property is expected values on the cardinal points degree system. For this convention, 0 or 360 is north, 90 is east, 180 is south, and 270 is west. Please specify a direction within the 0 to 360 degree bound and try again. */ } //Map up our azimuth now too, if needed - Liu & Jordan model assumes 0 = equator facing if (solar_service_model_tilt == LIUJORDAN) { if (obj->latitude>0.0) //North - "south" is equatorial facing { orientation_azimuth_corrected = 180.0 - orientation_azimuth; } else if (obj->latitude<0.0) //South - "north" is equatorial facing { gl_warning("solar_service:%s - Default solar_service position model is not recommended for southern hemisphere!",hdr->name); /* TROUBLESHOOT The Liu-Jordan (default) solar_service position and tilt model was built around the northern hemisphere. As such, operating in the southern hemisphere does not provide completely accurate results. They are close, but tilted surfaces are not properly accounted for. It is recommended that the solar_service_TILT_MODEL SOLPOS be used for southern hemisphere operations. */ if ((orientation_azimuth >= 0.0) && (orientation_azimuth <= 180.0)) { orientation_azimuth_corrected = orientation_azimuth; //East positive } else if (orientation_azimuth == 360.0) //Special case for those who like 360 as North { orientation_azimuth_corrected = 0.0; } else //Must be west { orientation_azimuth_corrected = orientation_azimuth - 360.0; } } else //Equator - erm.... { GL_THROW("Exact equator location of array detected - unknown how to handle orientation"); /* TROUBLESHOOT The solar_service orientation algorithm implemented inside GridLAB-D does not understand how to orient itself for an array exactly on the equator. Shift it north or south a little bit to get valid results. */ } } else //Right now only SOLPOS, which is "correct" - if another is implemented, may need another case orientation_azimuth_corrected = orientation_azimuth; } //Defaulted else for now - don't do anything }//End valid weather - mapping check } else //Player mode, just drop a message { gl_warning("solar_service object:%s is in player mode - be sure to specify relevant values",hdr->name); /* TROUBLESHOOT The solar_service array object is in player mode. It will not take values from climate files or objects. Be sure to specify the Insolation, ambient_temperature, and wind_speed values as necessary. It also will not incorporate any tilt functionality, since the Insolation value is expected to already include this adjustment. */ } return 1; }
/** Initialize oven model properties - randomized defaults for all published variables **/ int range::init(OBJECT *parent) { // @todo This class has serious problems and should be deleted and started from scratch. Fuller 9/27/2013. if(parent != NULL){ if((parent->flags & OF_INIT) != OF_INIT){ char objname[256]; gl_verbose("range::init(): deferring initialization on %s", gl_name(parent, objname, 255)); return 2; // defer } } OBJECT *hdr = OBJECTHDR(this); hdr->flags |= OF_SKIPSAFE; static double sTair = 74; static double sTout = 68; if (heat_fraction==0) heat_fraction = 0.2; if(parent){ pTair = gl_get_double_by_name(parent, "air_temperature"); pTout = gl_get_double_by_name(parent, "outdoor_temperature"); } if(pTair == 0){ pTair = &sTair; gl_warning("range parent lacks \'air_temperature\' property, using default"); } if(pTout == 0){ pTout = &sTout; gl_warning("range parent lacks \'outside_temperature\' property, using default"); } /* sanity checks */ /* initialize oven volume */ if(oven_volume <= 0.0){ // oven_volume = 5*floor((1.0/5.0)*gl_random_uniform(0.90, 1.10) * 50.0 * (pHouse->get_floor_area() /2000.0)); // [gal] if (oven_volume > 100.0) oven_volume = 100.0; else if (oven_volume < 20.0) oven_volume = 20.0; } if (oven_setpoint<90 || oven_setpoint>160){ GL_THROW("This model is experimental and not validated: oven thermostat is set to %f and is outside the bounds of 90 to 160 degrees Fahrenheit (32.2 - 71.1 Celsius).", oven_setpoint); /* TROUBLESHOOT TODO. */ } /* initialize oven deadband */ if (thermostat_deadband>10 || thermostat_deadband < 0.0) GL_THROW("oven deadband of %f is outside accepted bounds of 0 to 10 degrees (5.6 degC).", thermostat_deadband); // initial range UA if (oven_UA <= 0.0) GL_THROW("Range UA value is negative."); // Set heating element capacity if not provided by the user if (heating_element_capacity <= 0.0) { if (oven_volume >= 50) heating_element_capacity = 4.500; else { double randVal = gl_random_uniform(&hdr->rng_state,0,1); if (randVal < 0.33) heating_element_capacity = 3.200; else if (randVal < 0.67) heating_element_capacity = 3.500; else heating_element_capacity = 4.500; } } // Other initial conditions if(Tw < Tinlet){ // uninit'ed temperature Tw = gl_random_uniform(&hdr->rng_state,oven_setpoint - thermostat_deadband, oven_setpoint + thermostat_deadband); } current_model = NONE; load_state = STABLE; // initial demand Tset_curtail = oven_setpoint - thermostat_deadband/2 - 10; // Allow T to drop only 10 degrees below lower cut-in T... // Setup derived characteristics... area = (pi * pow(oven_diameter,2))/4; height = oven_volume/GALPCF / area; Cw = oven_volume/GALPCF * food_density * specificheat_food; // [Btu/F] h = height; // initial food temperature if(h == 0){ // discharged Tlower = Tinlet; Tupper = Tinlet + TSTAT_PRECISION; } else { Tlower = Tinlet; } /* schedule checks */ switch(shape.type){ case MT_UNKNOWN: /* normal, undriven behavior. */ gl_warning("This device, %s, is considered very experimental and has not been validated.", get_name()); break; case MT_ANALOG: if(shape.params.analog.energy == 0.0){ GL_THROW("range does not support fixed energy shaping"); /* TROUBLESHOOT Though it is possible to drive the demand of a oven, it is not possible to shape its power or energy draw. Its heater is either on or off, not in between. Change the load shape to not specify the power or energy and try again. */ } else if (shape.params.analog.power == 0){ oven_demand = gl_get_loadshape_value(&shape) / 2.4449; } else { oven_demand = gl_get_loadshape_value(&shape); /* unitless ~ drive gpm */ } break; case MT_PULSED: /* pulsed loadshapes "emit one or more pulses at random times s. t. the total energy is accumulated over the period of the loadshape". * pulsed loadshapes can either user time or kW values per pulse. */ if(shape.params.pulsed.pulsetype == MPT_TIME){ ; /* constant time pulse ~ consumes X gallons to drive heater for Y hours ~ but what's Vdot, what's t? */ } else if(shape.params.pulsed.pulsetype == MPT_POWER){ ; /* constant power pulse ~ oven demand X kW, limited by C + Q * h ~ Vdot proportional to power/time */ oven_demand = gl_get_loadshape_value(&shape) / 2.4449; } break; case MT_MODULATED: if(shape.params.modulated.pulsetype == MPT_TIME){ GL_THROW("Amplitude modulated oven usage is nonsensical for residential ovens"); /* TROUBLESHOOT Though it is possible to put a constant, it is thoroughly counterintuitive to the normal usage of the range. */ } else if(shape.params.modulated.pulsetype == MPT_POWER){ /* frequency modulated */ /* fixed-amplitude, varying length pulses at regular intervals. */ oven_demand = gl_get_loadshape_value(&shape) / 2.4449; } break; case MT_QUEUED: if(shape.params.queued.pulsetype == MPT_TIME){ ; /* constant time pulse ~ consumes X gallons/minute to consume Y thermal energy */ } else if(shape.params.queued.pulsetype == MPT_POWER){ ; /* constant power pulse ~ oven demand X kW, limited by C + Q * h */ oven_demand = gl_get_loadshape_value(&shape) / 2.4449; } break; default: GL_THROW("range load shape has an unknown state!"); break; } return residential_enduse::init(parent); }
/** @return 0 on failure, 1 on success **/ int group_recorder::read_line(){ size_t index = 0, offset = 0, unit_len = 0; quickobjlist *curr = 0; char *swap_ptr = 0; char buffer[128]; char objname[128]; if(TS_OPEN != tape_status){ // could be ERROR or CLOSED return 0; } // pre-calculate buffer needs if(line_size <= 0 || line_buffer == 0){ size_t prop_size; // in v2.3, there's no measure of the property's string representation size. // this value *is* present in 3.0. prop_size = 48; line_size = (prop_size + 1) * obj_count + 1; line_buffer = (char *)malloc(line_size); if(0 == line_buffer){ return 0; } memset(line_buffer, 0, line_size); if(-1 == write_interval){ // 'on change', will need second buffer prev_line_buffer = (char *)malloc(line_size); if(0 == prev_line_buffer){ gl_error("group_recorder::read_line(): malloc failure"); /* TROUBLESHOOT Memory allocation failure. */ return 0; } memset(prev_line_buffer, 0, line_size); } } // if we need the previous buffer to compare against, swap the buffers if(-1 == write_interval){ swap_ptr = prev_line_buffer; prev_line_buffer = line_buffer; line_buffer = swap_ptr; } memset(line_buffer, 0, line_size); for(curr = obj_list; curr != 0; curr = curr->next){ // GETADDR is a macro defined in object.h if(curr->prop->ptype == PT_complex && complex_part != NONE){ double part_value = 0.0; complex *cptr = 0; // get value as a complex cptr = gl_get_complex(curr->obj, curr->prop); if(0 == cptr){ gl_error("group_recorder::read_line(): unable to get complex property '%s' from object '%s'", curr->prop->name, gl_name(curr->obj, objname, 127)); /* TROUBLESHOOT Could not read a complex property as a complex value. */ return 0; } // switch on part switch(complex_part){ case NONE: // didn't we test != NONE just a few lines ago? gl_error("group_recorder::read_line(): inconsistant complex_part states!"); return 0; case REAL: part_value = cptr->Re(); break; case IMAG: part_value = cptr->Im(); break; case MAG: part_value = cptr->Mag(); break; case ANG: part_value = cptr->Arg() * 180/PI; break; case ANG_RAD: part_value = cptr->Arg(); break; } sprintf(buffer, "%f", part_value); offset = strlen(buffer); } else { offset = gl_get_value(curr->obj, GETADDR(curr->obj, curr->prop), buffer, 127, prop_ptr); if(0 == offset){ gl_error("group_recorder::read_line(): unable to get value for '%s' in object '%s'", curr->prop->name, curr->obj->name); /* TROUBLESHOOT An error occured while reading the specified property in one of the objects. */ return 0; } } // check line_buffer space unit_len = ((print_units && 0 != curr->prop && 0 != curr->prop->unit) ? 1 + strlen(curr->prop->unit->name) : 0); if( unit_len + (index + offset + 1) > line_size ){ gl_error("group_recorder::read_line(): potential buffer overflow from a too-short automatically sized output value buffer"); /* TROUBLESHOOT A potential buffer overflow was caught, most likely due to incorrect property representation assumptions for buffer allocation. */ return 0; } // write to line_buffer // * lead with a comma on all entries, assume leading timestamp will NOT print a comma if(print_units && 0 != curr->prop && 0 != curr->prop->unit){ if(0 >= sprintf(line_buffer+index, ",%s %s", buffer, curr->prop->unit->name)){return 0;} } else { if(0 >= sprintf(line_buffer+index, ",%s", buffer)){return 0;} } index += (offset + 1) + unit_len; // add the comma } // assume write_line will add newline character return 1; }
//EXPORT for object-level call (as opposed to module-level) EXPORT SIMULATIONMODE update_double_assert(OBJECT *obj, TIMESTAMP t0, unsigned int64 delta_time, unsigned long dt, unsigned int iteration_count_val) { char buff[64]; char dateformat[8]=""; char error_output_buff[1024]; char datebuff[64]; double_assert *da = OBJECTDATA(obj,double_assert); DATETIME delta_dt_val; double del_clock; TIMESTAMP del_clock_int; int del_microseconds; double *x; if(da->get_once() == da->ONCE_TRUE){ da->set_once_value(da->get_value()); da->set_once(da->ONCE_DONE); } else if (da->get_once() == da->ONCE_DONE){ if(da->get_once_value() == da->get_value()){ gl_verbose("Assert skipped with ONCE logic"); return SM_EVENT; } else { da->set_once_value(da->get_value()); } } // get the within range double range = 0.0; if ( da->get_within_mode() == da->IN_RATIO ) { range = da->get_value() * da->get_within(); //if ( range<0.001 ) //minimum bounds removed since many deltamode items are small //{ // minimum bounds // range = 0.001; //} } else if ( da->get_within_mode()== da->IN_ABS ) { range = da->get_within(); } //Iteration checker - assert only valid on the first timestep if (iteration_count_val == 0) { //Skip first timestep of any delta iteration -- nature of delta means it really isn't checking the right one if (delta_time>=dt) { //Get value x = (double*)gl_get_double_by_name(obj->parent,da->get_target()); if (x==NULL) { gl_error("Specified target %s for %s is not valid.",da->get_target(),gl_name(obj->parent,buff,64)); /* TROUBLESHOOT Check to make sure the target you are specifying is a published variable for the object that you are pointing to. Refer to the documentation of the command flag --modhelp, or check the wiki page to determine which variables can be published within the object you are pointing to with the assert function. */ return SM_ERROR; } else if (da->get_status() == da->ASSERT_TRUE) { double m = fabs(*x-da->get_value()); if (_isnan(m) || m>range) { //Calculate time if (delta_time>=dt) //After first iteration del_clock = (double)t0 + (double)(delta_time-dt)/(double)DT_SECOND; else //First second different, don't back out del_clock = (double)t0 + (double)(delta_time)/(double)DT_SECOND; del_clock_int = (TIMESTAMP)del_clock; /* Whole seconds - update from global clock because we could be in delta for over 1 second */ del_microseconds = (int)((del_clock-(int)(del_clock))*1000000+0.5); /* microseconds roll-over - biased upward (by 0.5) */ //Convert out gl_localtime(del_clock_int,&delta_dt_val); //Determine output format gl_global_getvar("dateformat",dateformat,sizeof(dateformat)); //Output date appropriately if ( strcmp(dateformat,"ISO")==0) sprintf(datebuff,"ERROR [%04d-%02d-%02d %02d:%02d:%02d.%.06d %s] : ",delta_dt_val.year,delta_dt_val.month,delta_dt_val.day,delta_dt_val.hour,delta_dt_val.minute,delta_dt_val.second,del_microseconds,delta_dt_val.tz); else if ( strcmp(dateformat,"US")==0) sprintf(datebuff,"ERROR [%02d-%02d-%04d %02d:%02d:%02d.%.06d %s] : ",delta_dt_val.month,delta_dt_val.day,delta_dt_val.year,delta_dt_val.hour,delta_dt_val.minute,delta_dt_val.second,del_microseconds,delta_dt_val.tz); else if ( strcmp(dateformat,"EURO")==0) sprintf(datebuff,"ERROR [%02d-%02d-%04d %02d:%02d:%02d.%.06d %s] : ",delta_dt_val.day,delta_dt_val.month,delta_dt_val.year,delta_dt_val.hour,delta_dt_val.minute,delta_dt_val.second,del_microseconds,delta_dt_val.tz); else sprintf(datebuff,"ERROR .09f : ",del_clock); //Actual error part sprintf(error_output_buff,"Assert failed on %s - %s (%g) not within %f of given value %g",gl_name(obj->parent, buff, 64),da->get_target(), *x, da->get_within(), da->get_value()); //Send it out gl_output("%s%s",datebuff,error_output_buff); return SM_ERROR; } gl_verbose("Assert passed on %s", gl_name(obj->parent, buff, 64)); return SM_EVENT; } else if (da->get_status() == da->ASSERT_FALSE) { double m = fabs(*x-da->get_value()); if (_isnan(m) || m<range) { //Calculate time if (delta_time>=dt) //After first iteration del_clock = (double)t0 + (double)(delta_time-dt)/(double)DT_SECOND; else //First second different, don't back out del_clock = (double)t0 + (double)(delta_time)/(double)DT_SECOND; del_clock_int = (TIMESTAMP)del_clock; /* Whole seconds - update from global clock because we could be in delta for over 1 second */ del_microseconds = (int)((del_clock-(int)(del_clock))*1000000+0.5); /* microseconds roll-over - biased upward (by 0.5) */ //Convert out gl_localtime(del_clock_int,&delta_dt_val); //Determine output format gl_global_getvar("dateformat",dateformat,sizeof(dateformat)); //Output date appropriately if ( strcmp(dateformat,"ISO")==0) sprintf(datebuff,"ERROR [%04d-%02d-%02d %02d:%02d:%02d.%.06d %s] : ",delta_dt_val.year,delta_dt_val.month,delta_dt_val.day,delta_dt_val.hour,delta_dt_val.minute,delta_dt_val.second,del_microseconds,delta_dt_val.tz); else if ( strcmp(dateformat,"US")==0) sprintf(datebuff,"ERROR [%02d-%02d-%04d %02d:%02d:%02d.%.06d %s] : ",delta_dt_val.month,delta_dt_val.day,delta_dt_val.year,delta_dt_val.hour,delta_dt_val.minute,delta_dt_val.second,del_microseconds,delta_dt_val.tz); else if ( strcmp(dateformat,"EURO")==0) sprintf(datebuff,"ERROR [%02d-%02d-%04d %02d:%02d:%02d.%.06d %s] : ",delta_dt_val.day,delta_dt_val.month,delta_dt_val.year,delta_dt_val.hour,delta_dt_val.minute,delta_dt_val.second,del_microseconds,delta_dt_val.tz); else sprintf(datebuff,"ERROR .09f : ",del_clock); //Actual error part sprintf(error_output_buff,"Assert failed on %s - %s (%g) not within %f of given value %g",gl_name(obj->parent, buff, 64),da->get_target(), *x, da->get_within(), da->get_value()); //Send it out gl_output("%s%s",datebuff,error_output_buff); return SM_ERROR; } gl_verbose("Assert passed on %s", gl_name(obj->parent, buff, 64)); return SM_EVENT; } else { gl_verbose("Assert test is not being run on %s", gl_name(obj->parent, buff, 64)); return SM_EVENT; } } else //First pass, just proceed return SM_EVENT; } else //Iteration, so don't care return SM_EVENT; }
int thermal_storage::init(OBJECT *parent) { if(parent != NULL){ if((parent->flags & OF_INIT) != OF_INIT){ char objname[256]; gl_verbose("thermal_storage::init(): deferring initialization on %s", gl_name(parent, objname, 255)); return 2; // defer } } OBJECT *hdr = OBJECTHDR(this); hdr->flags |= OF_SKIPSAFE; double *design_cooling_capacity; //Make sure the parent is a house if (!(gl_object_isa(parent,"house","residential"))) { GL_THROW("thermal_storage:%s must be parented to a house!",hdr->name); /* TROUBLESHOOT The thermal_storage model is only valid for house objects. Please parent it appropriately. */ } //Pull a house link, we'll use it for addresses house_e *house_lnk = OBJECTDATA(parent,house_e); //Link the variables to the parent values (house values) design_cooling_capacity = &house_lnk->design_cooling_capacity; outside_temperature = &house_lnk->outside_temperature; thermal_storage_available = &house_lnk->thermal_storage_present; thermal_storage_active = &house_lnk->thermal_storage_inuse; //Check the cooling capacity if (*design_cooling_capacity == NULL) { gl_warning("\'design_cooling_capacity\' not specified in parent ~ default to 5 ton or 60,000 Btu/hr"); /* TROUBLESHOOT The thermal_storage did not reference a parent object that publishes design_cooling_capacity, so 5 ton was assumed. Confirm or change the parent reference and try again. */ discharge_rate = 5 * 12000; //Btu/hr, is set to 5 ton when not defined water_capacity = 1.7413; //m^3, is set to the same as a 5 ton unit } else { discharge_rate = *design_cooling_capacity; water_capacity = 1.7413 * (discharge_rate / (5 * 12000)); } surface_area = 6 * pow(water_capacity, 0.6667); //suface area of a cube calculated from volume if (total_capacity == 0) total_capacity = (30 / 5) * discharge_rate; //Btu if (state_of_charge < 0 && stored_capacity < 0) //Btu { stored_capacity = total_capacity; state_of_charge = 100; } else if (state_of_charge < 0 && stored_capacity >= 0) { state_of_charge = stored_capacity / total_capacity; } else if (state_of_charge >= 0 && stored_capacity < 0) { stored_capacity = (state_of_charge / 100) * total_capacity; } else if (state_of_charge >= 0 && stored_capacity >= 0) { stored_capacity = (state_of_charge / 100) * total_capacity; gl_warning("stored_capacity and SOC are both defined, SOC being used for initial energy state"); /* TROUBLESHOOT During the initialization of the system, a value was specified for both the stored_capacity and SOC (state of charge). The thermal energy storage object gives precedence to the SOC variable, so the initial stored_capacity will be the SOC percentage of the total_capacity. */ } if (recharge_power == 0) recharge_power = (3.360 * discharge_rate) / (5 * 12000); //kW if (discharge_power == 0) discharge_power = (0.300 * discharge_rate) / (5 * 12000); //kW if (recharge_power_factor == 0) recharge_power_factor = 0.97; //same as used for HVAC compressor in house_e if (discharge_power_factor == 0) discharge_power_factor = 1; //assume ideal pump if (k < 0) k = 0; //assume no thermal conductivity k = k * 0.00052667; //convert k from W/m/K to BTU/sec/m/degF //Determine how to read the scheduling information - charging if (recharge_schedule_type==INTERNAL) { //See if someone else has already created such a schedule recharge_schedule_vals = gl_schedule_find(thermal_default_schedule_list[1].schedule_name); //If not found, create if (recharge_schedule_vals == NULL) { //Populate schedules - charging recharge_schedule_vals = gl_schedule_create(thermal_default_schedule_list[1].schedule_name,thermal_default_schedule_list[1].schedule_definition); //Make sure it worked if (recharge_schedule_vals==NULL) { GL_THROW("Failure to create default charging schedule"); /* TROUBLESHOOT While attempting to create the default charging schedule in the thermal_storage object, an error occurred. Please try again. If the error persists, please submit your code and a bug report via the track website. */ } } gl_verbose("thermal_storage charging defaulting to internal schedule"); /* TROUBLESHOOT recharge_schedule_type was not set to EXTERNAL, so the internal schedule definition will be used for the recharging schedule. */ //Assign to the schedule value recharge_time_ptr = &recharge_schedule_vals->value; } else { //Assign the to published property recharge_time_ptr = &recharge_time; } //Determine how to read the scheduling information - discharging if (discharge_schedule_type==INTERNAL) { //See if someone else has already created such a schedule discharge_schedule_vals = gl_schedule_find(thermal_default_schedule_list[0].schedule_name); //If not found, create if (discharge_schedule_vals == NULL) { //Populate schedules - discharging discharge_schedule_vals = gl_schedule_create(thermal_default_schedule_list[0].schedule_name,thermal_default_schedule_list[0].schedule_definition); //Make sure it worked if (discharge_schedule_vals==NULL) { GL_THROW("Failure to create default discharging schedule"); /* TROUBLESHOOT While attempting to create the default discharging schedule in the thermal_storage object, an error occurred. Please try again. If the error persists, please submit your code and a bug report via the track website. */ } } gl_verbose("thermal_storage discharging defaulting to internal schedule"); /* TROUBLESHOOT discharge_schedule_type was not set to EXTERNAL, so the internal schedule definition will be used for the discharging availability schedule. */ //Assign to the schedule value discharge_time_ptr = &discharge_schedule_vals->value; } else { //Assigned to the published property discharge_time_ptr = &discharge_time; } // waiting this long to initialize the parent class is normal return residential_enduse::init(parent); }
int windturb_dg::init(OBJECT *parent) { OBJECT *obj = OBJECTHDR(this); double ZB, SB, EB; complex tst, tst2, tst3, tst4; switch (Turbine_Model) { case GENERIC_IND_LARGE: case GENERIC_SYNCH_LARGE: //Creates generic 1.5 MW wind turbine. blade_diam = 82.5; turbine_height = 90; q = 3; //number of gearbox stages Rated_VA = 1635000; Max_P = 1500000; Max_Q = 650000; Rated_V = 600; pf = 0.95; CP_Data = GENERAL_LARGE; cut_in_ws = 4; //lowest wind speed cut_out_ws = 25; //highest wind speed Cp_max = 0.302; //rotor specifications for power curve ws_maxcp = 7; Cp_rated = Cp_max-.05; ws_rated = 12.5; if (Turbine_Model == GENERIC_IND_LARGE) { Gen_type = INDUCTION; Rst = 0.12; Xst = 0.17; Rr = 0.12; Xr = 0.15; Rc = 999999; Xm = 9.0; } else if (Turbine_Model == GENERIC_SYNCH_LARGE) { Gen_type = SYNCHRONOUS; Rs = 0.05; Xs = 0.200; Rg = 0.000; Xg = 0.000; } break; case GENERIC_IND_MID: case GENERIC_SYNCH_MID: //Creates generic 100kW wind turbine, northwind 100 blade_diam = 23.2; //in m turbine_height = 30; //in m q = 0; //number of gearbox stages, no gear box Rated_VA = 156604; Max_P = 150000; Max_Q = 45000; Rated_V = 480; pf = 0.9; ///lag and lead of 0.9 CP_Data = GENERAL_MID; cut_in_ws = 3.5; //lowest wind speed in m/s cut_out_ws = 25; //highest wind speed in m/s Cp_max = 0.302; //rotor specifications for power curve ws_maxcp = 7; Cp_rated = Cp_max-.05; ws_rated = 14.5; // in m/s if (Turbine_Model == GENERIC_IND_MID) { // need to check the machine parameters Gen_type = INDUCTION; Rst = 0.12; Xst = 0.17; Rr = 0.12; Xr = 0.15; Rc = 999999; Xm = 9.0; } else if (Turbine_Model == GENERIC_SYNCH_MID) { Gen_type = SYNCHRONOUS; Rs = 0.05; Xs = 0.200; Rg = 0.000; Xg = 0.000; } break; case GENERIC_IND_SMALL: case GENERIC_SYNCH_SMALL: //Creates generic 5 kW wind turbine, Fortis Montana 5 kW wind turbine blade_diam = 5; // in m turbine_height = 16; //in m q = 0; //number of gearbox stages, no gear box Rated_VA = 6315; // calculate from P & Q Max_P = 5800; Max_Q = 2500; Rated_V = 600; pf = 0.95; CP_Data = GENERAL_SMALL; cut_in_ws = 2.5; //lowest wind speed cut_out_ws = 25; //highest wind speed Cp_max = 0.302; //rotor specifications for power curve ws_maxcp = 7; // | Cp_rated = Cp_max-.05; // | ws_rated = 17; // | if (Turbine_Model == GENERIC_IND_SMALL) { Gen_type = INDUCTION; Rst = 0.12; Xst = 0.17; Rr = 0.12; Xr = 0.15; Rc = 999999; Xm = 9.0; } else if (Turbine_Model == GENERIC_SYNCH_SMALL) { Gen_type = SYNCHRONOUS; Rs = 0.05; Xs = 0.200; Rg = 0.000; Xg = 0.000; } break; case VESTAS_V82: //Include manufacturer's data - cases can be added to call other wind turbines turbine_height = 78; blade_diam = 82; Rated_VA = 1808000; Rated_V = 600; Max_P = 1650000; Max_Q = 740000; pf = 0.91; //Can range between 0.65-1.00 depending on controllers and Pout. CP_Data = MANUF_TABLE; cut_in_ws = 3.5; cut_out_ws = 20; q = 2; Gen_type = SYNCHRONOUS; //V82 actually uses a DFIG, but will use synch representation for now Rs = 0.025; //Estimated values for synch representation. Xs = 0.200; Rg = 0.000; Xg = 0.000; break; case GE_25MW: turbine_height = 100; blade_diam = 100; Rated_VA = 2727000; Rated_V = 690; Max_P = 2500000; Max_Q = 1090000; pf = 0.95; //ranges between -0.9 -> 0.9; q = 3; CP_Data = GENERAL_LARGE; cut_in_ws = 3.5; cut_out_ws = 25; Cp_max = 0.28; Cp_rated = 0.275; ws_maxcp = 8.2; ws_rated = 12.5; Gen_type = SYNCHRONOUS; Rs = 0.035; Xs = 0.200; Rg = 0.000; Xg = 0.000; break; case BERGEY_10kW: turbine_height = 24; blade_diam = 7; Rated_VA = 10000; Rated_V = 360; Max_P = 15000; Max_Q = 4000; pf = 0.95; //ranges between -0.9 -> 0.9; q = 0; CP_Data = GENERAL_SMALL; cut_in_ws = 2; cut_out_ws = 20; Cp_max = 0.28; Cp_rated = 0.275; ws_maxcp = 8.2; ws_rated = 12.5; Gen_type = SYNCHRONOUS; Rs = 0.05; Xs = 0.200; Rg = 0.000; Xg = 0.000; break; case USER_DEFINED: CP_Data = USER_SPECIFY; Gen_type = USER_TYPE; Rs = 0.2; Xs = 0.2; Rg = 0.1; Xg = 0; if (turbine_height <=0) GL_THROW ("turbine height cannot have a negative or zero value."); /* TROUBLESHOOT Turbine height must be specified as a value greater than or equal to zero. */ if (blade_diam <=0) GL_THROW ("blade diameter cannot have a negative or zero value."); /* TROUBLESHOOT Blade diameter must be specified as a value greater than or equal to zero. */ if (cut_in_ws <=0) GL_THROW ("cut in wind speed cannot have a negative or zero value."); /* TROUBLESHOOT Cut in wind speed must be specified as a value greater than or equal to zero. */ if (cut_out_ws <=0) GL_THROW ("cut out wind speed cannot have a negative or zero value."); /* TROUBLESHOOT Cut out wind speed must be specified as a value greater than or equal to zero. */ if (ws_rated <=0) GL_THROW ("rated wind speed cannot have a negative or zero value."); /* TROUBLESHOOT Rated wind speed must be specified as a value greater than or equal to zero. */ if (ws_maxcp <=0) GL_THROW ("max cp cannot have a negative or zero value."); /* TROUBLESHOOT Maximum coefficient of performance must be specified as a value greater than or equal to zero. */ break; default: GL_THROW("Unknown turbine model was specified"); /* TROUBLESHOOT An unknown wind turbine model was selected. Please select a Turbine_Model from the available list. */ } // construct circuit variable map to meter -- copied from 'House' module struct { complex **var; char *varname; } map[] = { // local object name, meter object name {&pCircuit_V, "voltage_A"}, // assumes 2 and 3 follow immediately in memory {&pLine_I, "current_A"}, // assumes 2 and 3(N) follow immediately in memory /// @todo use triplex property mapping instead of assuming memory order for meter variables (residential, low priority) (ticket #139) }; static complex default_line123_voltage[3], default_line1_current[3]; int i; //Map phases set *phaseInfo; PROPERTY *tempProp; tempProp = gl_get_property(parent,"phases"); if ((tempProp==NULL || tempProp->ptype!=PT_set)) { GL_THROW("Unable to map phases property - ensure the parent is a powerflow:meter"); /* TROUBLESHOOT While attempting to map the phases property from the parent object, an error was encountered. Please check and make sure your parent object is a meter inside the powerflow module and try again. If the error persists, please submit your code and a bug report via the Trac website. */ } else phaseInfo = (set*)GETADDR(parent,tempProp); int temp_phases=0; // Currently only supports 3-phase connection, so check number of phases of parent if ((*phaseInfo & PHASE_A) == PHASE_A) temp_phases += 1; if ((*phaseInfo & PHASE_B) == PHASE_B) temp_phases += 1; if ((*phaseInfo & PHASE_C) == PHASE_C) temp_phases += 1; if (temp_phases < 3) GL_THROW("The wind turbine model currently only supports a 3-phase connection, please check meter connection"); /* TROUBLESHOOT Currently the wind turbine model only supports 3-phase connnections. Please attach to 3-phase meter. */ // find parent meter, if not defined, use a default meter (using static variable 'default_meter') if (parent!=NULL) { if((parent->flags & OF_INIT) != OF_INIT){ char objname[256]; gl_verbose("windturb_dg::init(): deferring initialization on %s", gl_name(parent, objname, 255)); return 2; // defer } if (gl_object_isa(parent,"meter","powerflow")) //Attach to meter { /* NR_mode = get_bool(parent,"NR_mode"); //Check NR_mode, just to be consistent if (NR_mode == NULL) { GL_THROW("Wind turbine failed to map NR_mode property"); */ /* TROUBLESHOOT While attempting to map up the NR_mode property, an error was encountered. Please try again. If the error persists, please submit your code and a bug report via the trac website. */ /* } */ //Map the voltages double *parNominalVoltage; tempProp = gl_get_property(parent,"nominal_voltage"); if ((tempProp==NULL || tempProp->ptype!=PT_double)) { GL_THROW("Unable to map nominal_voltage property - ensure the parent is a powerflow:meter"); /* TROUBLESHOOT While attempting to map the nominal_voltage property from the parent object, an error was encountered. Please check and make sure your parent object is a meter inside the powerflow module and try again. If the error persists, please submit your code and a bug report via the Trac website. */ } else parNominalVoltage = (double*)GETADDR(parent,tempProp); // check nominal voltage against rated voltage if ( fabs(1 - (*parNominalVoltage * sqrt(3.0) / Rated_V) ) > 0.1 ) gl_warning("windturb_dg (id:%d, name:%s): Rated generator voltage (LL: %.1f) and nominal voltage (LL: %.1f) of meter parent are different by greater than 10 percent. Odd behavior may occur.",obj->id,obj->name,Rated_V,*parNominalVoltage * sqrt(3.0)); /* TROUBLESHOOT Currently, the model allows you to attach the turbine to a voltage that is quite different than the rated terminal voltage of the generator. However, this may cause odd behavior, as the solved powerflow voltage is used to calculate the generator induced voltages and conversion from mechanical power. It is recommended that the nominal voltages of the parent meter be within ~10% of the rated voltage. */ // attach meter variables to each circuit for (i=0; i<sizeof(map)/sizeof(map[0]); i++) { *(map[i].var) = get_complex(parent,map[i].varname); if (*(map[i].var) == NULL) { GL_THROW("Unable to map variable %s",map[i].varname); /* TROUBLESHOOT The variable name was not found when mapping it */ } } } else if (gl_object_isa(parent,"triplex_meter","powerflow")) { GL_THROW("The wind turbine model does currently support direct connection to single phase or triplex meters. Connect through a rectifier-inverter combination."); /* TROUBLESHOOT This model does not currently support connection to a triplex system. Please connect to a 3-phase meter. */ //Map voltage pCircuit_V = get_complex(parent,"voltage_1"); //Make sure it worked if (pCircuit_V == NULL) GL_THROW("Unable to map triplex_meter voltage"); //Map current pLine_I = get_complex(parent,"current_1"); //Make sure it worked if (pLine_I == NULL) GL_THROW("Unable to map triplex_meter current"); //NR_mode = get_bool(parent,"NR_mode"); } else if (gl_object_isa(parent,"rectifier","generators")) { //Map the voltages double *parNominalVoltage; tempProp = gl_get_property(parent,"V_Rated"); if ((tempProp==NULL || tempProp->ptype!=PT_double)) { GL_THROW("Unable to map V_Rated property - ensure the parent is a powerflow:meter"); /* TROUBLESHOOT While attempting to map the nominal_voltage property from the parent object, an error was encountered. Please check and make sure your parent object is a meter inside the powerflow module and try again. If the error persists, please submit your code and a bug report via the Trac website. */ } else parNominalVoltage = (double*)GETADDR(parent,tempProp); // check nominal voltage against rated voltage if ( fabs(1 - (*parNominalVoltage / Rated_V) ) > 0.1 ) gl_warning("windturb_dg (id:%d, name:%s): Rated generator voltage (LL: %.1f) and nominal voltage (LL: %.1f) of meter parent are different by greater than 10 percent. Odd behavior may occur.",obj->id,obj->name,Rated_V,*parNominalVoltage * sqrt(3.0)); /* TROUBLESHOOT Currently, the model allows you to attach the turbine to a voltage that is quite different than the rated terminal voltage of the generator. However, this may cause odd behavior, as the solved powerflow voltage is used to calculate the generator induced voltages and conversion from mechanical power. It is recommended that the nominal voltages of the parent meter be within ~10% of the rated voltage. */ // attach meter variables to each circuit for (i=0; i<sizeof(map)/sizeof(map[0]); i++) { if ((*(map[i].var) = get_complex(parent,map[i].varname))==NULL) { GL_THROW("%s (%s:%d) does not implement rectifier variable %s for %s (windturb_dg:%d)", /* TROUBLESHOOT The rectifier requires that the inverter contains certain published properties in order to properly connect. If you encounter this error, please report it to the developers, along with the version of GridLAB-D that raised this error. */ parent->name?parent->name:"unnamed object", parent->oclass->name, parent->id, map[i].varname, obj->name?obj->name:"unnamed", obj->id); } } } else { GL_THROW("windturb_dg (id:%d): Invalid parent object",obj->id); /* TROUBLESHOOT The wind turbine object must be attached a 3-phase meter object. Please check parent of object. */ } } else { gl_warning("windturb_dg:%d %s", obj->id, parent==NULL?"has no parent meter defined":"parent is not a meter"); // attach meter variables to each circuit in the default_meter *(map[0].var) = &default_line123_voltage[0]; *(map[1].var) = &default_line1_current[0]; // provide initial values for voltages default_line123_voltage[0] = complex(Rated_V/sqrt(3.0),0); default_line123_voltage[1] = complex(Rated_V/sqrt(3.0)*cos(2*PI/3),Rated_V/sqrt(3.0)*sin(2*PI/3)); default_line123_voltage[2] = complex(Rated_V/sqrt(3.0)*cos(-2*PI/3),Rated_V/sqrt(3.0)*sin(-2*PI/3)); NR_mode = &default_NR_mode; } if (Gen_status==OFFLINE) { gl_warning("init_windturb_dg (id:%d,name:%s): Generator is out of service!", obj->id,obj->name); } if (Gen_type == SYNCHRONOUS || Gen_type == INDUCTION) { if (Gen_mode == CONSTANTE) { gl_warning("init_windturb_dg (id:%d,name:%s): Synchronous and induction generators in constant voltage mode has not been fully tested and my not work properly.", obj->id,obj->name); } } if (Rated_VA!=0.0) SB = Rated_VA/3; if (Rated_V!=0.0) EB = Rated_V/sqrt(3.0); if (SB!=0.0) ZB = EB*EB/SB; else GL_THROW("Generator power capacity not specified!"); /* TROUBLESHOOT Rated_VA of generator must be specified so that per unit values can be calculated */ if (Gen_type == INDUCTION) { complex Zrotor(Rr,Xr); complex Zmag = complex(Rc*Xm*Xm/(Rc*Rc + Xm*Xm),Rc*Rc*Xm/(Rc*Rc + Xm*Xm)); complex Zstator(Rst,Xst); //Induction machine two-port matrix. IndTPMat[0][0] = (Zmag + Zstator)/Zmag; IndTPMat[0][1] = Zrotor + Zstator + Zrotor*Zstator/Zmag; IndTPMat[1][0] = complex(1,0) / Zmag; IndTPMat[1][1] = (Zmag + Zrotor) / Zmag; } else if (Gen_type == SYNCHRONOUS) { double Real_Rs = Rs * ZB; double Real_Xs = Xs * ZB; double Real_Rg = Rg * ZB; double Real_Xg = Xg * ZB; tst = complex(Real_Rg,Real_Xg); tst2 = complex(Real_Rs,Real_Xs); AMx[0][0] = tst2 + tst; //Impedance matrix AMx[1][1] = tst2 + tst; AMx[2][2] = tst2 + tst; AMx[0][1] = AMx[0][2] = AMx[1][0] = AMx[1][2] = AMx[2][0] = AMx[2][1] = tst; tst3 = (complex(1,0) + complex(2,0)*tst/tst2)/(tst2 + complex(3,0)*tst); tst4 = (-tst/tst2)/(tst2 + tst); invAMx[0][0] = tst3; //Admittance matrix (inverse of Impedance matrix) invAMx[1][1] = tst3; invAMx[2][2] = tst3; invAMx[0][1] = AMx[0][2] = AMx[1][0] = AMx[1][2] = AMx[2][0] = AMx[2][1] = tst4; } else GL_THROW("Unknown generator type specified"); /* TROUBLESHOOT Shouldn't have been able to specify an unknown generator type. Please report this error to GridLAB-D support. */ init_climate(); return 1; }
int climate::init(OBJECT *parent) { char *dot = 0; OBJECT *obj=OBJECTHDR(this); double meter_to_feet = 1.0; double tz_num_offset; reader_type = RT_NONE; // ignore "" files ~ manual climate control is a feature if (strcmp(tmyfile,"")==0) return 1; // open access to the TMY file char *found_file; found_file = gl_findfile(tmyfile, NULL, FF_READ); if (found_file == NULL) // TODO: get proper values for solar { gl_error("weather file '%s' access failed", tmyfile); return 0; } //dot = strchr(tmyfile, '.'); //while(strchr(dot+1, '.')){ /* init time, doesn't have to be fast -MH */ // dot = strchr(dot, '.'); //} if(strstr(tmyfile, ".tmy2") || strstr(tmyfile,".tmy")){ reader_type = RT_TMY2; } else if(strstr(tmyfile, ".csv")){ reader_type = RT_CSV; } else { gl_warning("climate: unrecognized filetype, assuming TMY2"); } if(reader_type == RT_CSV){ // may or may not have an object, // have not called open() int rv = 0; if(reader == NULL){ csv_reader *creader = new csv_reader(); reader_hndl = creader; rv = creader->open(found_file); // creader->get_data(t0, &temperature, &humidity, &solar_direct, &solar_diffuse, &wind_speed, &rainfall, &snowdepth); } else { csv_reader *my = OBJECTDATA(reader,csv_reader); reader_hndl = my; rv = my->open(my->filename); // my->get_data(t0, &temperature, &humidity, &solar_direct, &solar_diffuse, &wind_speed, &rainfall, &snowdepth); //Pull timezone information tz_num_offset = my->tz_numval; tz_offset_val = tz_num_offset; //Copy latitude and longitude information from CSV reader obj->latitude = reader->latitude; obj->longitude = reader->longitude; //CSV Reader validity check if (fabs(obj->latitude) > 90) { gl_error("climate:%s - Latitude is outside +/-90!",obj->name); //Defined below return 0; } if (fabs(obj->longitude) > 180) { gl_error("climate:%s - Longitude is outside +/-180!",obj->name); //Defined below return 0; } //Generic warning about southern hemisphere and Duffie-Beckman usage if (obj->latitude<0) { gl_warning("climate:%s - Southern hemisphere solar position model may have issues",obj->name); /* TROUBLESHOOT The default solar position model was built around a northern hemisphere assumption. As such, it doesn't always produce completely accurate results for southern hemisphere locations. Calculated insolation values are approximately correct, but may show discrepancies against measured data. If this climate is associated with a solar object, use the SOLAR_TILT_MODEL SOLPOS to ensure proper results (this warning will still pop up). */ } //Set the timezone offset - stolen from TMY code below tz_meridian = 15 * tz_num_offset;//std_meridians[-file.tz_offset-5]; } return rv; } // implicit if(reader_type == RT_TMY2) ~ do the following if( file.open(found_file) < 3 ){ gl_error("climate::init() -- weather file header improperly formed"); return 0; } // begin parsing the TMY file int line=0; tmy = (TMYDATA*)malloc(sizeof(TMYDATA)*8760); if (tmy==NULL) { gl_error("TMY buffer allocation failed"); return 0; } int month, day, hour;//, year; double dnr,dhr,ghr,wspeed,precip,snowdepth,pressure,extra_dni; //char cty[50]; //char st[3]; int lat_deg,lat_min,long_deg,long_min; /* The city/state data isn't used anywhere. -mhauer */ //file.header_info(cty,st,&lat_deg,&lat_min,&long_deg,&long_min); file.header_info(NULL,NULL,&lat_deg,&lat_min,&long_deg,&long_min); //Handle hemispheres if (lat_deg<0) obj->latitude = (double)lat_deg - (((double)lat_min) / 60); else obj->latitude = (double)lat_deg + (((double)lat_min) / 60); if (long_deg<0) obj->longitude = (double)long_deg - (((double)long_min) / 60); else obj->longitude = (double)long_deg + (((double)long_min) / 60); //Generic check for TMY files if (fabs(obj->latitude) > 90) { gl_error("climate:%s - Latitude is outside +/-90!",obj->name); /* TROUBLESHOOT The value read from the weather data indicates a latitude of greater than 90 or less than -90 degrees. This is not a valid value. Please specify the latitude in this range, with positive values representing the northern hemisphere and negative values representing the southern hemisphere. */ return 0; } if (fabs(obj->longitude) > 180) { gl_error("climate:%s - Longitude is outside +/-180!",obj->name); /* TROUBLESHOOT The value read from the weather data indicates a longitude of greater than 180 or less than -180 degrees. This is not a valid value. Please specify the longitude in this range, with positive values representing the eastern hemisphere and negative values representing the western hemisphere. */ return 0; } //Generic warning about southern hemisphere and Duffie-Beckman usage if (obj->latitude<0) { gl_warning("climate:%s - Southern hemisphere solar position model may have issues",obj->name); //Defined above } if(0 == gl_convert("m", "ft", &meter_to_feet)){ gl_error("climate::init unable to gl_convert() 'm' to 'ft'!"); return 0; } file.elevation *= meter_to_feet; tz_meridian = 15 * file.tz_offset;//std_meridians[-file.tz_offset-5]; tz_offset_val = file.tz_offset; while (line<8760 && file.next()) { file.read_data(&dnr,&dhr,&ghr,&temperature,&humidity,&month,&day,&hour,&wspeed,&precip,&snowdepth,&pressure,&extra_dni); int doy = sa->day_of_yr(month,day); int hoy = (doy - 1) * 24 + (hour-1); if (hoy>=0 && hoy<8760){ // pre-conversion of solar data from W/m^2 to W/sf if(0 == gl_convert("W/m^2", "W/sf", &(dnr))){ gl_error("climate::init unable to gl_convert() 'W/m^2' to 'W/sf'!"); return 0; } if(0 == gl_convert("W/m^2", "W/sf", &(dhr))){ gl_error("climate::init unable to gl_convert() 'W/m^2' to 'W/sf'!"); return 0; } if(0 == gl_convert("W/m^2", "W/sf", &(ghr))){ gl_error("climate::init unable to gl_convert() 'W/m^2' to 'W/sf'!"); return 0; } if(0 == gl_convert("W/m^2", "W/sf", &(extra_dni))){ gl_error("climate::init unable to gl_convert() 'W/m^2' to 'W/sf'!"); return 0; } if(0 == gl_convert("mps", "mph", &(wspeed))){ gl_error("climate::init unable to gl_convert() 'm/s' to 'miles/h'!"); return 0; } tmy[hoy].temp_raw = temperature; tmy[hoy].temp = temperature; // post-conversion of copy of temperature from C to F if(0 == gl_convert("degC", "degF", &(tmy[hoy].temp))){ gl_error("climate::init unable to gl_convert() 'degC' to 'degF'!"); return 0; } tmy[hoy].windspeed=wspeed; tmy[hoy].rh = humidity; tmy[hoy].dnr = dnr; tmy[hoy].dhr = dhr; tmy[hoy].ghr = ghr; tmy[hoy].rainfall = precip; tmy[hoy].snowdepth = snowdepth; tmy[hoy].solar_raw = dnr; tmy[hoy].direct_normal_extra = extra_dni; tmy[hoy].pressure = pressure; // calculate the solar radiation - hour on here may need a -1 application (hour-1) - unsure how TMYs really code things double sol_time = sa->solar_time((double)hour,doy,RAD(tz_meridian),RAD(obj->longitude)); double sol_rad = 0.0; tmy[hoy].solar_elevation = sa->altitude(doy, RAD(obj->latitude), sol_time); tmy[hoy].solar_azimuth = sa->azimuth(doy, RAD(obj->latitude), sol_time); for(COMPASS_PTS c_point = CP_H; c_point < CP_LAST;c_point=COMPASS_PTS(c_point+1)){ if(c_point == CP_H) sol_rad = file.calc_solar(CP_E,doy,RAD(obj->latitude),sol_time,dnr,dhr,ghr,ground_reflectivity,0.0);//(double)dnr * cos_incident + dhr; else sol_rad = file.calc_solar(c_point,doy,RAD(obj->latitude),sol_time,dnr,dhr,ghr,ground_reflectivity);//(double)dnr * cos_incident + dhr; /* TMY2 solar radiation data is in Watt-hours per square meter. */ tmy[hoy].solar[c_point] = sol_rad; /* track records */ if (sol_rad>record.solar || record.solar==0) record.solar = sol_rad; if (tmy[hoy].temp>record.high || record.high==0) { record.high = tmy[hoy].temp; record.high_day = doy; } if (tmy[hoy].temp<record.low || record.low==0) { record.low = tmy[hoy].temp; record.low_day = doy; } } } else gl_error("%s(%d): day %d, hour %d is out of allowed range 0-8759 hours", tmyfile,line,day,hour); line++; } file.close(); /* initialize climate to starttime */ presync(gl_globalclock); /* enable forecasting if specified */ #if 0 if ( strcmp(forecast,"")!=0 && gl_forecast_create(obj,"")==NULL ) { char buf[1024]; gl_error("%s: forecast '%s' is not valid", gl_name(obj,buf,sizeof(buf))?buf:"(object?)", forecast); return 0; } else if (obj->forecast) { /* initialize the forecast data entity */ FORECAST *fc = obj->forecast; fc->propref = gl_find_property(obj->oclass,"temperature"); gl_forecast_save(fc,obj->clock,3600,0,NULL); obj->flags |= OF_FORECAST; } #endif return 1; }
int refrigerator::init(OBJECT *parent) { if(parent != NULL){ if((parent->flags & OF_INIT) != OF_INIT){ char objname[256]; gl_verbose("refrigerator::init(): deferring initialization on %s", gl_name(parent, objname, 255)); return 2; // defer } } OBJECT *hdr = OBJECTHDR(this); hdr->flags |= OF_SKIPSAFE; // defaults for unset values */ if (size==0) size = gl_random_uniform(&hdr->rng_state,20,40); // cf if (thermostat_deadband==0) thermostat_deadband = gl_random_uniform(&hdr->rng_state,2,3); if (Tset==0) Tset = gl_random_uniform(&hdr->rng_state,35,39); if (UA == 0) UA = 0.6; if (UAr==0) UAr = UA+size/40*gl_random_uniform(&hdr->rng_state,0.9,1.1); if (UAf==0) UAf = gl_random_uniform(&hdr->rng_state,0.9,1.1); if (COPcoef==0) COPcoef = gl_random_uniform(&hdr->rng_state,0.9,1.1); if (Tout==0) Tout = 59.0; if (load.power_factor==0) load.power_factor = 0.95; pTout = (double*)gl_get_addr(parent, "air_temperature"); if (pTout==NULL) { static double default_air_temperature = 72; gl_warning("%s (%s:%d) parent object lacks air temperature, using %0f degF instead", hdr->name, hdr->oclass->name, hdr->id, default_air_temperature); pTout = &default_air_temperature; } /* derived values */ Tair = gl_random_uniform(&hdr->rng_state,Tset-thermostat_deadband/2, Tset+thermostat_deadband/2); // size is used to couple Cw and Qrated Cf = size/10.0 * RHOWATER * CWATER; // cf * lb/cf * BTU/lb/degF = BTU / degF rated_capacity = BTUPHPW * size*10; // BTU/h ... 10 BTU.h / cf (34W/cf, so ~700 for a full-sized refrigerator) start_time = 0; if(compressor_off_normal_energy==0) compressor_off_normal_energy=15*45*60; //watt-secs if(compressor_off_normal_power==0) compressor_off_normal_power=15; //watt if(long_compressor_cycle_energy==0) long_compressor_cycle_energy=120*100*60; //watt-secs if(long_compressor_cycle_power==0) long_compressor_cycle_power=120; //watt if(compressor_on_normal_energy==0) compressor_on_normal_energy=120*35*60; //watt-secs if(compressor_on_normal_power==0) compressor_on_normal_power=120; //watt if(defrost_energy==0) defrost_energy=40*550*60; //watt-secs if(defrost_power==0) defrost_power=550; //watt if(icemaking_energy==0) icemaking_energy=300*60; //watt-secs if(icemaking_power==0) icemaking_power=300; //watt if(ice_making_probability==0) ice_making_probability=0.02; //watt if(DO_Thershold==0) DO_Thershold=24; if(long_compressor_cycle_threshold==0) long_compressor_cycle_threshold=0.05; if(FF_Door_Openings==0) FF_Door_Openings=0; if(door_opening_power==0) door_opening_power=16; if(delay_defrost_time==0) delay_defrost_time=28800; if(defrost_criterion==0) defrost_criterion=DC_TIMED; refrigerator_power = 0; return_time = 0; no_of_defrost = 0; total_compressor_time = 0; if(door_open_time==0) door_open_time=7; long_compressor_cycle_due=false; door_energy_calc = false; ice_making_time = new double[1,2,3]; icemaker_running = false; check_defrost = false; switch(state){ case RS_DEFROST: if(energy_needed==0) energy_needed = defrost_energy; cycle_time = ceil((energy_needed - energy_used)/defrost_power); break; case RS_COMPRESSSOR_OFF_NORMAL: if(energy_needed==0) energy_needed = compressor_off_normal_energy; cycle_time = ceil((energy_needed - energy_used)/compressor_off_normal_power); break; case RS_COMPRESSSOR_ON_NORMAL: if(energy_needed==0) energy_needed = compressor_on_normal_energy; cycle_time = ceil((energy_needed - energy_used)/compressor_on_normal_power); break; } run_defrost = false; if (is_240) { load.config = EUC_IS220; } load.total = Qr * KWPBTUPH; return residential_enduse::init(parent); }
/** Initialize water heater model properties - randomized defaults for all published variables **/ int waterheater::init(OBJECT *parent) { OBJECT *hdr = OBJECTHDR(this); if(parent != NULL){ if((parent->flags & OF_INIT) != OF_INIT){ char objname[256]; gl_verbose("waterheater::init(): deferring initialization on %s", gl_name(parent, objname, 255)); return 2; // defer } } hdr->flags |= OF_SKIPSAFE; static double sTair = 74; static double sTout = 68; if(parent){ pTair = gl_get_double_by_name(parent, "air_temperature"); pTout = gl_get_double_by_name(parent, "outdoor_temperature"); } if(pTair == 0){ pTair = &sTair; gl_warning("waterheater parent lacks \'air_temperature\' property, using default"); } if(pTout == 0){ pTout = &sTout; gl_warning("waterheater parent lacks \'outside_temperature\' property, using default"); } /* sanity checks */ /* initialize water tank volume */ if(tank_volume <= 0.0){ // tank_volume = 5*floor((1.0/5.0)*gl_random_uniform(0.90, 1.10) * 50.0 * (pHouse->get_floor_area() /2000.0)); // [gal] if (tank_volume > 100.0) tank_volume = 100.0; else if (tank_volume < 20.0) tank_volume = 20.0; } else { if (tank_volume > 100.0 || tank_volume < 20.0){ gl_error("watertank volume of %f outside the volume bounds of 20 to 100 gallons.", tank_volume); /* TROUBLESHOOT All waterheaters must be set between 40 and 100 gallons. Most waterheaters are assumed to be 50 gallon tanks. */ } } if (tank_setpoint<90 || tank_setpoint>160) 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); /* TROUBLESHOOT All waterheaters must be set between 90 degF and 160 degF. /* initialize water tank deadband */ if (thermostat_deadband>10 || thermostat_deadband < 0.0) GL_THROW("watertank deadband of %f is outside accepted bounds of 0 to 10 degrees (5.6 degC).", thermostat_deadband); // initial tank UA if (tank_UA <= 0.0) GL_THROW("Tank UA value is negative."); // Set heating element capacity if not provided by the user if (heating_element_capacity <= 0.0) { if (tank_volume >= 50) heating_element_capacity = 4.500; else { // Smaller tanks can be either 3200, 3500, or 4500... double randVal = gl_random_uniform(RNGSTATE,0,1); if (randVal < 0.33) heating_element_capacity = 3.200; else if (randVal < 0.67) heating_element_capacity = 3.500; else heating_element_capacity = 4.500; } } // set gas electric loads, if not provided by the user if(0 > gas_fan_power){ gas_fan_power = heating_element_capacity * 0.01; } if(0 > gas_standby_power){ gas_standby_power = 0.0; // some units consume 3-5W } // Other initial conditions if(Tw < Tinlet){ // uninit'ed temperature Tw = gl_random_uniform(RNGSTATE,tank_setpoint - thermostat_deadband, tank_setpoint + thermostat_deadband); } current_model = NONE; load_state = STABLE; // initial demand Tset_curtail = tank_setpoint - thermostat_deadband/2 - 10; // Allow T to drop only 10 degrees below lower cut-in T... // Setup derived characteristics... area = (pi * pow(tank_diameter,2))/4; height = tank_volume/GALPCF / area; Cw = tank_volume/GALPCF * RHOWATER * Cp; // [Btu/F] h = height; // initial water temperature if(h == 0){ // discharged Tlower = Tinlet; Tupper = Tinlet + TSTAT_PRECISION; } else { Tlower = Tinlet; } /* schedule checks */ switch(shape.type){ case MT_UNKNOWN: /* normal, undriven behavior. */ break; case MT_ANALOG: if(shape.params.analog.energy == 0.0){ GL_THROW("waterheater does not support fixed energy shaping"); /* TROUBLESHOOT Though it is possible to drive the water demand of a water heater, it is not possible to shape its power or energy draw. Its heater is either on or off, not in between. Change the load shape to not specify the power or energy and try again. */ } else if (shape.params.analog.power == 0){ /* power-driven ~ cheat with W/degF*gpm */ // double heat_per_gallon = RHOWATER * // lb/cf // CFPGAL * // lb/gal // CWATER * // BTU/degF / gal // KWBTUPH / // kW/gal // 1000.0; // W/gal water_demand = gl_get_loadshape_value(&shape) / 2.4449; } else { water_demand = gl_get_loadshape_value(&shape); /* unitless ~ drive gpm */ } break; case MT_PULSED: /* pulsed loadshapes "emit one or more pulses at random times s. t. the total energy is accumulated over the period of the loadshape". * pulsed loadshapes can either user time or kW values per pulse. */ if(shape.params.pulsed.pulsetype == MPT_TIME){ ; /* constant time pulse ~ consumes X gallons to drive heater for Y hours ~ but what's Vdot, what's t? */ } else if(shape.params.pulsed.pulsetype == MPT_POWER){ ; /* constant power pulse ~ draws water to consume X kW, limited by C + Q * h ~ Vdot proportional to power/time */ water_demand = gl_get_loadshape_value(&shape) / 2.4449; } break; case MT_MODULATED: if(shape.params.modulated.pulsetype == MPT_TIME){ GL_THROW("Amplitude modulated water usage is nonsensical for residential water heaters"); /* TROUBLESHOOT Though it is possible to put a constant, low-level water draw on a water heater, it is thoroughly counterintuitive to the normal usage of the waterheater. */ } else if(shape.params.modulated.pulsetype == MPT_POWER){ /* frequency modulated */ /* fixed-amplitude, varying length pulses at regular intervals. */ water_demand = gl_get_loadshape_value(&shape) / 2.4449; } break; case MT_QUEUED: if(shape.params.queued.pulsetype == MPT_TIME){ ; /* constant time pulse ~ consumes X gallons/minute to consume Y thermal energy */ } else if(shape.params.queued.pulsetype == MPT_POWER){ ; /* constant power pulse ~ draws water to consume X kW, limited by C + Q * h */ water_demand = gl_get_loadshape_value(&shape) / 2.4449; } break; default: GL_THROW("waterheater load shape has an unknown state!"); break; } return residential_enduse::init(parent); }
/** initialization process **/ int irrigation_controller::init(OBJECT *parent){ OBJECT *hdr = OBJECTHDR(this); char tname[32]; parent2=parent; insync=0; initial_zipload_power=gl_get_double_by_name(parent,"base_power"); char *namestr = (hdr->name ? hdr->name : tname); sprintf(tname, "irrigation_controller:%i", hdr->id); first=0; cheat(); if(parent == NULL){ gl_error("%s: irrigation_controller has no parent, therefore nothing to control", namestr); return 0; } if(pMarket == NULL){ gl_error("%s: irrigation_controller has no market, therefore no price signals", namestr); return 0; } if(gl_object_isa(pMarket, "auction")){ gl_set_dependent(hdr, pMarket); market = OBJECTDATA(pMarket, auction); } else { gl_error("irrigation_controllers only work when attached to an 'auction' object"); return 0; } if(dPeriod == 0.0){ if((pMarket->flags & OF_INIT) != OF_INIT){ char objname[256]; gl_verbose("irrigation_controller::init(): deferring initialization on %s", gl_name(pMarket, objname, 255)); return 2; // defer } period = market->period; } else { period = (TIMESTAMP)floor(dPeriod + 0.5); } if(bid_delay < 0){ bid_delay = -bid_delay; } if(bid_delay > period){ gl_warning("Bid delay is greater than the irrigation_controller period. Resetting bid delay to 0."); bid_delay = 0; } if(target[0] == 0){ GL_THROW("irrigation_controller: %i, target property not specified", hdr->id); } if(setpoint[0] == 0 && control_mode == CN_RAMP){ GL_THROW("irrigation_controller: %i, setpoint property not specified", hdr->id);; } if(demand[0] == 0 && control_mode == CN_RAMP){ GL_THROW("irrigation_controller: %i, demand property not specified", hdr->id); } if(deadband[0] == 0 && use_predictive_bidding == TRUE && control_mode == CN_RAMP){ GL_THROW("irrigation_controller: %i, deadband property not specified", hdr->id); } if(total[0] == 0){ GL_THROW("irrigation_controller: %i, total property not specified", hdr->id); } if(load[0] == 0){ GL_THROW("irrigation_controller: %i, load property not specified", hdr->id); } fetch(&pMonitor, target, parent); // auto tha einai to soil hmidit tha to pairnei apo to soil_SENSOR if(control_mode == CN_RAMP){ fetch(&pSetpoint, setpoint, parent); fetch(&pDemand, demand, parent); fetch(&pTotal, total, parent); fetch(&pLoad, load, parent); if(use_predictive_bidding == TRUE){ fetch(&pDeadband, deadband.get_string(), parent); } } fetch(&pAvg, avg_target.get_string(), pMarket); fetch(&pStd, std_target.get_string(), pMarket); if(dir == 0){ double high = ramp_high * range_high; double low = ramp_low * range_low; //printf("high:%f, low:%f, rh:%f, rl:%f,gh:%f,gl:%f\n\n\n\n",high,low,ramp_high,ramp_low,range_high,range_low); if(high > low){ dir = 1; } else if(high < low){ dir = -1; } else if((high == low) && (fabs(ramp_high) > 0.001 || fabs(ramp_low) > 0.001)){ dir = 0; if(ramp_high > 0){ direction = 1; } else { direction = -1; } gl_warning("%s: irrigation_controller has no price ramp", namestr); /* occurs given no price variation, or no control width (use a normal thermostat?) */ } if(ramp_low * ramp_high < 0){ gl_warning("%s: irrigation_controller price curve is not injective and may behave strangely"); /* TROUBLESHOOT The price curve 'changes directions' at the setpoint, which may create odd conditions in a number of circumstances. */ } } if(setpoint0==0) setpoint0 = -1; // key to check first thing // double period = market->period; // next_run = gl_globalclock + (TIMESTAMP)(period - fmod(gl_globalclock+period,period)); next_run = gl_globalclock;// + (market->period - gl_globalclock%market->period); init_time = gl_globalclock; time_off = TS_NEVER; if(sliding_time_delay < 0 ) dtime_delay = 21600; // default sliding_time_delay of 6 hours else dtime_delay = (int64)sliding_time_delay; if(state[0] != 0){ // grab state pointer pState = gl_get_enum_by_name(parent, state); last_pState = 0; if(pState == 0){ gl_error("state property name \'%s\' is not published by parent class", state); return 0; } } // get override, if set if(re_override[0] != 0){ pOverride = gl_get_enum_by_name(parent, re_override); } if((pOverride == 0) && (use_override == OU_ON)){ gl_error("use_override is ON but no valid override property name is given"); return 0; } if(control_mode == CN_RAMP){ if(slider_setting < -0.001){ gl_warning("slider_setting is negative, reseting to 0.0"); slider_setting = 0.0; } if(slider_setting > 1.0){ gl_warning("slider_setting is greater than 1.0, reseting to 1.0"); slider_setting = 1.0; } } last_p = market->init_price; /////////////////search for virtual_battery/////////////////////// /* static FINDLIST *xt1=NULL; xt1=gl_find_objects(FL_NEW,FT_CLASS,SAME,"virtual_battery",FT_END); OBJECT *firstt1= gl_find_next(xt1,NULL); OBJECT *it1; for(it1=firstt1;it1!=NULL;it1=it1->next) { if(gl_object_isa(it1,"virtual_battery")) { virtual_battery_object=it1; } else { // virtual_battery_object=NULL; } } */ ////////////////////////////////////////////////////////////////// return 1; }
EXPORT int check() { /* check each link to make sure it has a node at either end */ FINDLIST *list = gl_find_objects(FL_NEW,FT_MODULE,SAME,"powerflow",NULL); OBJECT *obj=NULL; int *nodemap, /* nodemap marks where nodes are */ *linkmap, /* linkmap counts the number of links to/from a given node */ *tomap; /* counts the number of references to any given node */ int errcount = 0; int objct = 0; int queuef = 0, queueb = 0, queuect = 0; int islandct = 0; int i, j; GLOBALVAR *gvroot = NULL; PFLIST anchor, *tlist = NULL; link **linklist = NULL, **linkqueue = NULL; objct = gl_get_object_count(); anchor.ptr = NULL; anchor.next = NULL; nodemap = (int *)malloc((size_t)(objct*sizeof(int))); linkmap = (int *)malloc((size_t)(objct*sizeof(int))); tomap = (int *)malloc((size_t)(objct*sizeof(int))); linkqueue = (link **)malloc((size_t)(objct*sizeof(link *))); linklist = (link **)malloc((size_t)(objct*sizeof(link *))); memset(nodemap, 0, objct*sizeof(int)); memset(linkmap, 0, objct*sizeof(int)); memset(tomap, 0, objct*sizeof(int)); memset(linkqueue, 0, objct*sizeof(link *)); memset(linklist, 0, objct*sizeof(link *)); /* per-object checks */ /* check from/to info on links */ while (obj=gl_find_next(list,obj)) { if (gl_object_isa(obj,"node")) { /* add to node map */ nodemap[obj->id]+=1; /* if no parent, then add to anchor list */ if(obj->parent == NULL){ tlist = (PFLIST *)malloc(sizeof(PFLIST)); tlist->ptr = obj; tlist->next = anchor.next; anchor.next = tlist; tlist = NULL; } } else if (gl_object_isa(obj,"link")) { link *pLink = OBJECTDATA(obj,link); OBJECT *from = pLink->from; OBJECT *to = pLink->to; node *tNode = OBJECTDATA(to, node); node *fNode = OBJECTDATA(from, node); /* count 'to' reference */ tomap[to->id]++; /* check link connections */ if (from==NULL){ gl_error("link %s (%s:%d) from object is not specified", pLink->get_name(), pLink->oclass->name, pLink->get_id()); ++errcount; } else if (!gl_object_isa(from,"node")){ gl_error("link %s (%s:%d) from object is not a node", pLink->get_name(), pLink->oclass->name, pLink->get_id()); ++errcount; } else { /* is a "from" and it isa(node) */ linkmap[from->id]++; /* mark that this node has a link from it */ } if (to==NULL){ gl_error("link %s (%s:%d) to object is not specified", pLink->get_name(), pLink->oclass->name, pLink->get_id()); ++errcount; } else if (!gl_object_isa(to,"node")){ gl_error("link %s (%s:%d) to object is not a node", pLink->get_name(), pLink->oclass->name, pLink->get_id()); ++errcount; } else { /* is a "to" and it isa(node) */ linkmap[to->id]++; /* mark that this node has links to it */ } /* add link to heap? */ if((from != NULL) && (to != NULL) && (linkmap[from->id] > 0) && (linkmap[to->id] > 0)){ linklist[queuect] = pLink; queuect++; } // check phases /* this isn't cooperating with me. -MH */ /* if(tNode->get_phases(PHASE_A) == fNode->get_phases(PHASE_A)){ gl_error("link:%i: to, from nodes have mismatched A phase (%i vs %i)", obj->id, tNode->get_phases(PHASE_A), fNode->get_phases(PHASE_A)); ++errcount; } if(tNode->get_phases(PHASE_B) == fNode->get_phases(PHASE_B)){ gl_error("link:%i: to, from nodes have mismatched B phase (%i vs %i)", obj->id, tNode->get_phases(PHASE_B), fNode->get_phases(PHASE_B)); ++errcount; } if(tNode->get_phases(PHASE_C) == fNode->get_phases(PHASE_C)){ gl_error("link:%i: to, from nodes have mismatched C phase (%i vs %i)", obj->id, tNode->get_phases(PHASE_C), fNode->get_phases(PHASE_C)); ++errcount; } if(tNode->get_phases(PHASE_D) == fNode->get_phases(PHASE_D)){ gl_error("link:%i: to, from nodes have mismatched D phase (%i vs %i)", obj->id, tNode->get_phases(PHASE_D), fNode->get_phases(PHASE_D)); ++errcount; } if(tNode->get_phases(PHASE_N) == fNode->get_phases(PHASE_N)){ gl_error("link:%i: to, from nodes have mismatched N phase (%i vs %i)", obj->id, tNode->get_phases(PHASE_N), fNode->get_phases(PHASE_N)); ++errcount; }*/ } } for(i = 0; i < objct; ++i){ /* locate unlinked nodes */ if(nodemap[i] != 0){ if(linkmap[i] * nodemap[i] > 0){ /* there is a node at [i] and links to it*/ ; } else if(linkmap[i] == 1){ /* either a feeder or an endpoint */ ; } else { /* unattached node */ gl_error("node:%i: node with no links to or from it", i); nodemap[i] *= -1; /* mark as unlinked */ ++errcount; } } } for(i = 0; i < objct; ++i){ /* mark by islands*/ if(nodemap[i] > 0){ /* has linked node */ linkmap[i] = i; /* island until proven otherwise */ } else { linkmap[i] = -1; /* just making sure... */ } } queueb = 0; for(i = 0; i < queuect; ++i){ if(linklist[i] != NULL){ /* consume the next item */ linkqueue[queueb] = linklist[i]; linklist[i] = NULL; queueb++; } while(queuef < queueb){ /* expand this island */ linkmap[linkqueue[queuef]->to->id] = linkmap[linkqueue[queuef]->from->id]; /* capture the adjacent nodes */ for(j = 0; j < queuect; ++j){ if(linklist[j] != NULL){ if(linklist[j]->from->id == linkqueue[queuef]->to->id){ linkqueue[queueb] = linklist[j]; linklist[j] = NULL; ++queueb; } } } ++queuef; } /* we've consumed one island, grab another */ } for(i = 0; i < objct; ++i){ if(nodemap[i] != 0){ gl_testmsg("node:%i on island %i", i, linkmap[i]); if(linkmap[i] == i){ ++islandct; } } if(tomap[i] > 1){ FINDLIST *cow = gl_find_objects(FL_NEW,FT_ID,SAME,i,NULL); OBJECT *moo = gl_find_next(cow, NULL); char grass[64]; gl_output("object #%i, \'%s\', has more than one link feeding to it (this will diverge)", i, gl_name(moo, grass, 64)); } } gl_output("Found %i islands", islandct); tlist = anchor.next; while(tlist != NULL){ PFLIST *tptr = tlist; tlist = tptr->next; free(tptr); } /* An extra something to check link directionality, * if the root node has been defined on the command line. * -d3p988 */ gvroot = gl_global_find("powerflow::rootnode"); if(gvroot != NULL){ PFLIST *front=NULL, *back=NULL, *del=NULL; /* node queue */ OBJECT *_node = gl_get_object((char *)gvroot->prop->addr); OBJECT *_link = NULL; int *rankmap = (int *)malloc((size_t)(objct*sizeof(int))); int bct = 0; if(_node == NULL){ gl_error("powerflow check(): Unable to do directionality check, root node name not found."); } else { gl_testmsg("Powerflow Check ~ Backward Links:"); } for(int i = 0; i < objct; ++i){ rankmap[i] = objct; } rankmap[_node->id] = 0; front = (PFLIST *)malloc(sizeof(PFLIST)); front->next = NULL; front->ptr = _node; back = front; while(front != NULL){ // find all links from the node for(OBJECT *now=gl_find_next(list, NULL); now != NULL; now = gl_find_next(list, now)){ link *l; if(!gl_object_isa(now, "link")) continue; l = OBJECTDATA(now, link); if((l->from != front->ptr) && (l->to != front->ptr)){ continue; } else if(rankmap[l->from->id]<objct && rankmap[l->to->id]<objct){ continue; } else if(rankmap[l->from->id] < rankmap[l->to->id]){ /* mark */ rankmap[l->to->id] = rankmap[l->from->id]+1; } else if(rankmap[l->from->id] > rankmap[l->to->id]){ /* swap & mark */ OBJECT *t = l->from; gl_testmsg("reversed link: %s goes from %s to %s", now->name, l->from->name, l->to->name); l->from = l->to; l->to = t; rankmap[l->to->id] = rankmap[l->from->id]+1;; } // enqueue the "to" node back->next = (PFLIST *)malloc(sizeof(PFLIST)); back->next->next = NULL; //back->next->ptr = l->to; back = back->next; back->ptr = l->to; } del = front; front = front->next; free(del); } } free(nodemap); free(linkmap); free(linklist); free(linkqueue); return 0; }
int clotheswasher::init(OBJECT *parent) { OBJECT *hdr = OBJECTHDR(this); if(parent != NULL){ if((parent->flags & OF_INIT) != OF_INIT){ char objname[256]; gl_verbose("clotheswasher::init(): deferring initialization on %s", gl_name(parent, objname, 255)); return 2; // defer } } hdr->flags |= OF_SKIPSAFE; // default properties if (shape.params.analog.power==0) shape.params.analog.power = gl_random_uniform(&hdr->rng_state,0.100,0.750); // clotheswasher size [W] if (load.heatgain_fraction==0) load.heatgain_fraction = 0.5; if (load.power_factor==0) load.power_factor = 0.95; if(shape.params.analog.power < 0.1){ gl_error("clotheswasher motor is undersized, using 500W motor"); shape.params.analog.power = 0.5; } int res = residential_enduse::init(parent); Is_on = 0; if(NORMAL_PREWASH_ENERGY == 0) NORMAL_PREWASH_ENERGY = 12*20*60; if(NORMAL_WASH_ENERGY == 0) NORMAL_WASH_ENERGY = 4*40*60; if(NORMAL_SMALLWASH_ENERGY == 0) NORMAL_SMALLWASH_ENERGY = 2*25*60; if(NORMAL_SPIN_LOW_ENERGY == 0) NORMAL_SPIN_LOW_ENERGY = 2*60*60; if(NORMAL_SPIN_MEDIUM_ENERGY == 0) NORMAL_SPIN_MEDIUM_ENERGY = 2*150*60; if(NORMAL_SPIN_HIGH_ENERGY == 0) NORMAL_SPIN_HIGH_ENERGY = 2*220*60; if(NORMAL_PREWASH_POWER == 0) NORMAL_PREWASH_POWER = 20; if(NORMAL_WASH_POWER == 0) NORMAL_WASH_POWER = 40; if(NORMAL_SMALLWASH_POWER == 0) NORMAL_SMALLWASH_POWER = 25; if(NORMAL_SPIN_LOW_POWER == 0) NORMAL_SPIN_LOW_POWER = 60; if(NORMAL_SPIN_MEDIUM_POWER == 0) NORMAL_SPIN_MEDIUM_POWER = 150; if(NORMAL_SPIN_HIGH_POWER == 0) NORMAL_SPIN_HIGH_POWER = 220; if(PERMPRESS_PREWASH_ENERGY == 0) PERMPRESS_PREWASH_ENERGY = 12*20*60; if(PERMPRESS_WASH_ENERGY == 0) PERMPRESS_WASH_ENERGY = 4*40*60; if(PERMPRESS_SMALLWASH_ENERGY == 0) PERMPRESS_SMALLWASH_ENERGY = 2*25*60; if(PERMPRESS_SPIN_LOW_ENERGY == 0) PERMPRESS_SPIN_LOW_ENERGY = 2*60*60; if(PERMPRESS_SPIN_MEDIUM_ENERGY == 0) PERMPRESS_SPIN_MEDIUM_ENERGY = 2*150*60; if(PERMPRESS_SPIN_HIGH_ENERGY == 0) PERMPRESS_SPIN_HIGH_ENERGY = 2*220*60; if(PERMPRESS_PREWASH_POWER == 0) PERMPRESS_PREWASH_POWER = 20; if(PERMPRESS_WASH_POWER == 0) PERMPRESS_WASH_POWER = 40; if(PERMPRESS_SMALLWASH_POWER == 0) PERMPRESS_SMALLWASH_POWER = 25; if(PERMPRESS_SPIN_LOW_POWER == 0) PERMPRESS_SPIN_LOW_POWER = 60; if(PERMPRESS_SPIN_MEDIUM_POWER == 0) PERMPRESS_SPIN_MEDIUM_POWER = 150; if(PERMPRESS_SPIN_HIGH_POWER == 0) PERMPRESS_SPIN_HIGH_POWER = 220; if(GENTLE_PREWASH_ENERGY == 0) GENTLE_PREWASH_ENERGY = 12*20*60; if(GENTLE_WASH_ENERGY == 0) GENTLE_WASH_ENERGY = 4*40*60; if(GENTLE_SMALLWASH_ENERGY == 0) GENTLE_SMALLWASH_ENERGY = 2*25*60; if(GENTLE_SPIN_LOW_ENERGY == 0) GENTLE_SPIN_LOW_ENERGY = 2*60*60; if(GENTLE_SPIN_MEDIUM_ENERGY == 0) GENTLE_SPIN_MEDIUM_ENERGY = 2*150*60; if(GENTLE_SPIN_HIGH_ENERGY == 0) GENTLE_SPIN_HIGH_ENERGY = 2*220*60; if(GENTLE_PREWASH_POWER == 0) GENTLE_PREWASH_POWER = 20; if(GENTLE_WASH_POWER == 0) GENTLE_WASH_POWER = 40; if(GENTLE_SMALLWASH_POWER == 0) GENTLE_SMALLWASH_POWER = 25; if(GENTLE_SPIN_LOW_POWER == 0) GENTLE_SPIN_LOW_POWER = 60; if(GENTLE_SPIN_MEDIUM_POWER == 0) GENTLE_SPIN_MEDIUM_POWER = 150; if(GENTLE_SPIN_HIGH_POWER == 0) GENTLE_SPIN_HIGH_POWER = 220; if(normal_perc == 0) normal_perc = 0.5; if(perm_press_perc == 0) perm_press_perc = 0.8; return res; }