//Perform post-event analysis (update computations, write event file if necessary) - no secondary count void metrics::event_ended(OBJECT *event_obj, OBJECT *fault_obj, OBJECT *faulting_obj, TIMESTAMP event_start_time, TIMESTAMP event_end_time, char *fault_type, char *impl_fault, int number_customers_int) { DATETIME start_dt, end_dt; TIMESTAMP outage_length; OBJECT *hdr = OBJECTHDR(this); int returnval; FILE *FPVal; //Determine the actual outage length (may be off due to iterations and such) outage_length = event_end_time - event_start_time; //Perform the calculation returnval = ((int (*)(OBJECT *, OBJECT *, int, int, TIMESTAMP, TIMESTAMP))(*compute_metrics))(hdr,module_metrics_obj,number_customers_int,CustomerCount,outage_length,metric_interval); //Make sure it worked if (returnval != 1) { GL_THROW("Metrics:%s failed to perform a post-event metric update",hdr->name); /* TROUBLESHOOT While attempting to provide a post-fault update of relevant metrics, the metrics object encountered an error. Please try your code again. If the error persists, please submit your code and a bug report using the trac website. */ } //Increment the counters metric_interval_event_count++; annual_interval_event_count++; //Now that that's done, let's see if we need to write a file output if (report_event_log == true) { //Convert the times gl_localtime(event_start_time,&start_dt); gl_localtime(event_end_time,&end_dt); //Open the file handle FPVal = fopen(report_file,"at"); //Print the name of the "safety" device? if (faulting_obj==NULL) { //Print the details out fprintf(FPVal,"%d,%d,%04d-%02d-%02d %02d:%02d:%02d,%04d-%02d-%02d %02d:%02d:%02d,%s,%s,%s,N/A,%s,%s,%d\n",annual_interval_event_count,metric_interval_event_count,start_dt.year,start_dt.month,start_dt.day,start_dt.hour,start_dt.minute,start_dt.second,end_dt.year,end_dt.month,end_dt.day,end_dt.hour,end_dt.minute,end_dt.second,fault_obj->oclass->name,fault_obj->name,event_obj->name,fault_type,impl_fault,number_customers_int); } else { //Print the details out fprintf(FPVal,"%d,%d,%04d-%02d-%02d %02d:%02d:%02d,%04d-%02d-%02d %02d:%02d:%02d,%s,%s,%s,%s,%s,%s,%d\n",annual_interval_event_count,metric_interval_event_count,start_dt.year,start_dt.month,start_dt.day,start_dt.hour,start_dt.minute,start_dt.second,end_dt.year,end_dt.month,end_dt.day,end_dt.hour,end_dt.minute,end_dt.second,fault_obj->oclass->name,fault_obj->name,event_obj->name,faulting_obj->name,fault_type,impl_fault,number_customers_int); } //Close the file fclose(FPVal); } }
static TIMESTAMP collector_write(OBJECT *obj) { struct collector *my = OBJECTDATA(obj,struct collector); char ts[64]; if (my->format==0) { //time_t t = (time_t)(my->last.ts*TS_SECOND); //strftime(ts,sizeof(ts),timestamp_format, gmtime(&t)); DATETIME dt; gl_localtime(my->last.ts, &dt); gl_strtime(&dt, ts, sizeof(ts)); } else sprintf(ts,"%" FMT_INT64 "d", my->last.ts); if ((my->limit>0 && my->samples>my->limit) /* limit reached */ || write_collector(my,ts,my->last.value)==0) /* write failed */ { if (my->ops){ close_collector(my); } else { gl_error("collector_write: no TAPEOP structure when closing the tape"); } my->status = TS_DONE; } else my->samples++; return TS_NEVER; }
//sjin: add solar azimuth wrapper funcions EXPORT int64 calculate_solar_azimuth(OBJECT *obj, double lititude, double *value) { static SolarAngles sa; // just for the functions double std_time = 0.0; double solar_time = 0.0; short int doy = 0; DATETIME dt; climate *cli; if (obj == 0 || value == 0){ //throw "climate/calc_solar: null object pointer in arguement"; return 0; } cli = OBJECTDATA(obj, climate); if(gl_object_isa(obj, "climate", "climate") == 0){ //throw "climate/calc_solar: input object is not a climate object"; return 0; } gl_localtime(obj->clock, &dt); std_time = (double)(dt.hour) + ((double)dt.minute)/60.0 + (dt.is_dst ? -1:0); solar_time = sa.solar_time(std_time, doy, RAD(cli->tz_meridian), RAD(obj->longitude)); double hr_ang = -(15.0 * PI_OVER_180)*(solar_time-12.0); // morning +, afternoon - double decl = 0.409280*sin(2.0*PI*(284+doy)/365); double alpha = (90.0 * PI_OVER_180) - lititude + decl; *value = acos( (sin(decl)*cos(lititude) - cos(decl)*sin(lititude)*cos(hr_ang))/cos(alpha) ); return 1; }
/** @return 1 on successful write, 0 on unsuccessful write, error, or when not ready **/ int group_recorder::write_line(TIMESTAMP t1){ char time_str[64]; DATETIME dt; if(TS_OPEN != tape_status){ gl_error("group_recorder::write_line(): trying to write line when the tape is not open"); // could be ERROR or CLOSED, should not have happened return 0; } if(0 == rec_file){ gl_error("group_recorder::write_line(): no output file open and state is 'open'"); /* TROUBLESHOOT group_recorder claimed to be open and attempted to write to a file when a file had not successfully opened. */ tape_status = TS_ERROR; return 0; } // check that buffer needs were pre-calculated if(line_size <= 0 || line_buffer == 0){ gl_error("group_recorder::write_line(): output buffer not initialized (read_line() not called)"); /* TROUBLESHOOT read_line was not called before write_line, indicating an internal logic error. */ tape_status = TS_ERROR; return 0; } // write time_str // recorder.c uses multiple formats, in the sense of "formatted or not". This does not. if(0 == gl_localtime(t1, &dt)){ gl_error("group_recorder::write_line(): error when converting the sync time"); /* TROUBLESHOOT Unprintable timestamp. */ tape_status = TS_ERROR; return 0; } if(0 == gl_strtime(&dt, time_str, sizeof(time_str) ) ){ gl_error("group_recorder::write_line(): error when writing the sync time as a string"); /* TROUBLESHOOT Error printing the timestamp. */ tape_status = TS_ERROR; return 0; } // print line to file if(0 >= fprintf(rec_file, "%s%s\n", time_str, line_buffer)){ gl_error("group_recorder::write_line(): error when writing to the output file"); /* TROUBLESHOOT File I/O error. */ tape_status = TS_ERROR; return 0; } ++write_count; return 1; }
/* Sync is called when the clock needs to advance on the bottom-up pass */ TIMESTAMP office::presync(TIMESTAMP t0, TIMESTAMP t1) { DATETIME dt; /* reset the multizone heat transfer */ Qz = 0; /* update out_temp */ zone.current.out_temp = *(zone.current.pTemperature); /* get the occupancy mode from the schedule, if any */ if (t0>0) { int day;// = gl_getweekday(t0); int hour;// = gl_gethour(t0); gl_localtime(t0, &dt); day = dt.weekday; hour = dt.hour; if (zone.design.schedule[0]!='\0' ) zone.current.occupancy = IS_OCCUPIED(day,hour); } return TS_NEVER; }
double meter::process_bill(TIMESTAMP t1){ DATETIME dtime; gl_localtime(t1,&dtime); monthly_energy = measured_real_energy/1000 - previous_energy_total; monthly_bill = monthly_fee; switch(bill_mode){ case BM_NONE: break; case BM_UNIFORM: monthly_bill += monthly_energy * price; break; case BM_TIERED: if(monthly_energy < tier_energy[0]) monthly_bill += last_price * monthly_energy; else if(monthly_energy < tier_energy[1]) monthly_bill += last_price*tier_energy[0] + last_tier_price[0]*(monthly_energy - tier_energy[0]); else if(monthly_energy < tier_energy[2]) monthly_bill += last_price*tier_energy[0] + last_tier_price[0]*(tier_energy[1] - tier_energy[0]) + last_tier_price[1]*(monthly_energy - tier_energy[1]); else monthly_bill += last_price*tier_energy[0] + last_tier_price[0]*(tier_energy[1] - tier_energy[0]) + last_tier_price[1]*(tier_energy[2] - tier_energy[1]) + last_tier_price[2]*(monthly_energy - tier_energy[2]); break; case BM_HOURLY: monthly_bill += hourly_acc; break; case BM_TIERED_RTP: monthly_bill += hourly_acc; if(monthly_energy < tier_energy[0]) monthly_bill += last_price_base * monthly_energy; else if(monthly_energy < tier_energy[1]) monthly_bill += last_price_base*tier_energy[0] + last_tier_price[0]*(monthly_energy - tier_energy[0]); else if(monthly_energy < tier_energy[2]) monthly_bill += last_price_base*tier_energy[0] + last_tier_price[0]*(tier_energy[1] - tier_energy[0]) + last_tier_price[1]*(monthly_energy - tier_energy[1]); else monthly_bill += last_price_base*tier_energy[0] + last_tier_price[0]*(tier_energy[1] - tier_energy[0]) + last_tier_price[1]*(tier_energy[2] - tier_energy[1]) + last_tier_price[2]*(monthly_energy - tier_energy[2]); break; } if (dtime.day == bill_day && dtime.hour == 0 && dtime.month != last_bill_month) { previous_monthly_bill = monthly_bill; previous_monthly_energy = monthly_energy; previous_energy_total = measured_real_energy/1000; last_bill_month = dtime.month; hourly_acc = 0; } last_price = price; last_price_base = price_base; last_tier_price[0] = tier_price[0]; last_tier_price[1] = tier_price[1]; last_tier_price[2] = tier_price[2]; return monthly_bill; }
TIMESTAMP schedule::presync(TIMESTAMP t0, TIMESTAMP t1){ /* get localtime & run rules */ DATETIME dt; TIMESTAMP min = TS_NEVER; // earliest start of next state TIMESTAMP max = TS_NEVER; // earliest end of current state int res = 0; schedule_list *lptr = 0, *child = 0; // skip if t0 < next_ts ... we should know when this needs to change gl_localtime(t1, &dt); currval = default_value; for(lptr = sched_list; lptr != NULL; lptr = lptr->next){ // process lptr rule // traverse lptr children // track when current state ends OR when next state begins /* group_right & group_left should actually be used ... coming soon! */ for(child = lptr; child != NULL; child = child->group_right){ res = test_sched_dt(child->moy_start, child->moy_end, dt.month); if(res == 0){ continue; } if(child->dow_or_dom == schedule_list::wor_skip){ res = 1; /* short circuit, both are '*' */ } else if(child->dow_or_dom == schedule_list::wor_month){ res = test_sched_dt(child->dom_start, child->dom_end, dt.day); } else if(child->dow_or_dom == schedule_list::wor_week){ res = test_sched_dt(child->dow_start, child->dow_end, dt.weekday); } else if(child->dow_or_dom == schedule_list::wor_both){ res = test_sched_dt(child->dom_start, child->dom_end, dt.day) + test_sched_dt(child->dow_start, child->dow_end, dt.weekday); } else { gl_verbose("invalid day of week/day of month flag"); continue; } if(res == 0){ continue; } res = test_sched_dt(child->hod_start, child->hod_end, dt.hour); res += test_sched_dt(child->moh_start, child->moh_end, dt.minute > 59 ? 59 : dt.minute); if(res == 2){ currval = child->scale; break; /* found rule in the group that matches, on to the next group */ } } } return TS_NEVER; }
void climate::update_forecasts(TIMESTAMP t0) { static const int Nh = 72; /* number of hours in forecast */ static const int dt = 3600; /* number of seconds in forecast interval */ #if 0 OBJECT *my = OBJECTHDR(this); FORECAST *fc; for ( fc=my->forecast ; fc!=NULL ; fc=fc->next ) { /* don't update forecasts that are already current */ if ( t0/dt==(fc->starttime/dt) ) continue; int h, hoy; double t[Nh]; for ( h=0 ; h<Nh ; h++ ) { if (h==0) { /* actual values */ DATETIME ts; if ( !gl_localtime(t0+(h*dt),&ts) ) { GL_THROW("climate::sync -- unable to resolve localtime!"); } int doy = sa->day_of_yr(ts.month,ts.day); hoy = (doy - 1) * 24 + (ts.hour); } else if ( hoy==8760 ) hoy = 0; else hoy++; /* this is an extremely naive model of forecast error */ t[h] = tmy[hoy].temp + gl_random_normal(RNGSTATE,0,h/10.0); } gl_forecast_save(fc,t0,dt,Nh,t); #ifdef NEVER char buffer[1024]; int len = sprintf(buffer,"%d",fc->starttime); for ( h=3; h<72; h+=3 ) len += sprintf(buffer+len,",%.1f",fc->values[h]); printf("%s\n",buffer); #endif } #endif }
EXPORT TIMESTAMP sync_residential_enduse(OBJECT *obj, TIMESTAMP t1) { residential_enduse *my = OBJECTDATA(obj,residential_enduse); try { TIMESTAMP t2 = my->sync(obj->clock, t1); obj->clock = t1; return t2; } catch (char *msg) { DATETIME dt; char ts[64]; gl_localtime(t1,&dt); gl_strtime(&dt,ts,sizeof(ts)); gl_error("%s::%s.init(OBJECT **obj={name='%s', id=%d},TIMESTAMP t1='%s'): %s", obj->oclass->module->name, obj->oclass->name, obj->name, obj->id, ts, msg); return 0; } }
EXPORT TIMESTAMP sync_network(OBJECT *obj, TIMESTAMP t1) { network *my = OBJECTDATA(obj,network); try { TIMESTAMP t2 = my->sync(obj->clock, t1); //obj->clock = t1; // update in commit return t2; } catch (char *msg) { DATETIME dt; char ts[64]; gl_localtime(t1,&dt); gl_strtime(&dt,ts,sizeof(ts)); gl_error("%s::%s.init(OBJECT **obj={name='%s', id=%d},TIMESTAMP t1='%s'): %s", obj->oclass->module->name, obj->oclass->name, obj->name, obj->id, ts, msg); return 0; } }
EXPORT TIMESTAMP sync_thermal_storage(OBJECT *obj, TIMESTAMP t1) { thermal_storage *my = OBJECTDATA(obj,thermal_storage); try { TIMESTAMP t2 = my->sync(obj->clock, t1); obj->clock = t1; return t2; } catch (char *msg) { DATETIME dt; char ts[64]; gl_localtime(t1,&dt); gl_strtime(&dt,ts,sizeof(ts)); gl_error("%s::%s.init(OBJECT **obj={name='%s', id=%d},TIMESTAMP t1='%s'): %s", obj->oclass->module->name, obj->oclass->name, obj->name, obj->id, ts, msg); /* TROUBLESHOOT The synchronization operation of the specified object failed. The message given provide additional details and can be looked up under the Exceptions section. */ return 0; } }
EXPORT TIMESTAMP sync_transmissioncom(OBJECT *obj, TIMESTAMP t1, PASSCONFIG pass){ transmissioncom *my = OBJECTDATA(obj,transmissioncom); try { TIMESTAMP t2 = TS_NEVER; if(pass == PC_BOTTOMUP){ t2 = my->sync(obj->clock, t1); } else if(pass == PC_POSTTOPDOWN){ t2 = my->postsync(obj->clock, t1); } else if(pass == PC_PRETOPDOWN){ t2 = my->presync(obj->clock, t1); } obj->clock = t1; return t2; } catch (const char *msg) { DATETIME dt; char ts[64]; gl_localtime(t1,&dt); gl_strtime(&dt,ts,sizeof(ts)); gl_error("%s::%s.init(OBJECT **obj={name='%s', id=%d},TIMESTAMP t1='%s'): %s", obj->oclass->module->name, obj->oclass->name, obj->name, obj->id, ts, msg); return 0; } }
EXPORT int64 calculate_solar_radiation_shading_radians(OBJECT *obj, double tilt, double orientation, double shading_value, double *value){ static SolarAngles sa; // just for the functions double ghr, dhr, dnr = 0.0; double cos_incident = 0.0; double std_time = 0.0; double solar_time = 0.0; short int doy = 0; DATETIME dt; climate *cli; if(obj == 0 || value == 0){ //throw "climate/calc_solar: null object pointer in arguement"; return 0; } cli = OBJECTDATA(obj, climate); if(gl_object_isa(obj, "climate", "climate") == 0){ //throw "climate/calc_solar: input object is not a climate object"; return 0; } ghr = cli->solar_global; dhr = cli->solar_diffuse; dnr = shading_value * cli->solar_direct; gl_localtime(obj->clock, &dt); std_time = (double)(dt.hour) + ((double)dt.minute)/60.0 + (dt.is_dst ? -1.0:0.0); doy=sa.day_of_yr(dt.month,dt.day); solar_time = sa.solar_time(std_time, doy, RAD(cli->tz_meridian), RAD(obj->longitude)); cos_incident = sa.cos_incident(RAD(obj->latitude), tilt, orientation, solar_time, doy); *value = dnr * cos_incident + dhr * (1 + cos(tilt)) / 2 + ghr * (1 - cos(tilt)) * cli->ground_reflectivity / 2; return 1; }
/* Sync is called when the clock needs to advance on the bottom-up pass */ TIMESTAMP office::sync(TIMESTAMP t0, TIMESTAMP t1) { /* load calculations */ update_lighting(t0,t1); update_plugs(t0,t1); /* local aliases */ const double &Tout = (*(zone.current.pTemperature)); const double &Ua = (zone.design.exterior_ua); const double &Cm = (zone.design.interior_mass); const double &Um = (zone.design.interior_ua); double &Ti = (zone.current.air_temperature); double &dTi = (zone.current.temperature_change); double &Tm = (zone.current.mass_temperature); HCMODE &mode = (zone.hvac.mode); /* advance the thermal state of the building */ const double dt1 = t0>0 ? (double)(t1-t0)*TS_SECOND : 0; if (dt1>0) { const double dt = dt1/3600; /* model operates in units of hours */ /* calculate model update */ if (c2!=0) { /* update temperatures */ const double e1 = k1*exp(r1*dt); const double e2 = k2*exp(r2*dt); Ti = e1 + e2 + Teq; Tm = ((r1-c1)*e1 + (r2-c1)*e2 + c6)/c2 + Teq; if (warn_control) { /* check for air temperature excursion */ if (Ti<warn_low_temp || Ti>warn_high_temp) { OBJECT *obj = OBJECTHDR(this); DATETIME dt0, dt1; gl_localtime(t0,&dt0); gl_localtime(t1,&dt1); char ts0[64], ts1[64]; gl_warning("office:%d (%s) air temperature excursion (%.1f degF) at between %s and %s", obj->id, obj->name?obj->name:"anonymous", Ti, gl_strtime(&dt0,ts0,sizeof(ts0))?ts0:"UNKNOWN", gl_strtime(&dt1,ts1,sizeof(ts1))?ts1:"UNKNOWN"); } /* check for mass temperature excursion */ if (Tm<warn_low_temp || Tm>warn_high_temp) { OBJECT *obj = OBJECTHDR(this); DATETIME dt0, dt1; gl_localtime(t0,&dt0); gl_localtime(t1,&dt1); char ts0[64], ts1[64]; gl_warning("office:%d (%s) mass temperature excursion (%.1f degF) at between %s and %s", obj->id, obj->name?obj->name:"anonymous", Tm, gl_strtime(&dt0,ts0,sizeof(ts0))?ts0:"UNKNOWN", gl_strtime(&dt1,ts1,sizeof(ts1))?ts1:"UNKNOWN"); } } /* calculate the power consumption */ zone.total.energy += zone.total.power * dt; } const double Ca = 0.2402 * 0.0735 * zone.design.floor_height * zone.design.floor_area; /* update enduses and get internal heat gains */ Qi = zone.lights.enduse.heatgain + zone.plugs.enduse.heatgain; /* compute solar gains */ Qs = 0; int i; for (i=0; i<9; i++) Qs += zone.design.window_area[i] * zone.current.pSolar[i]/10; Qs *= 3.412; if (Qs<0) throw "solar gain is negative?!?"; /* compute heating/cooling effect */ Qh = update_hvac(); if (Ca<=0) throw "Ca must be positive"; if (Cm<=0) throw "Cm must be positive"; // split gains to air and mass double f_air = 1.0; /* adjust the fraction of gains that goes to air vs mass */ double Qa = Qh + f_air*(Qi + Qs); double Qm = (1-f_air)*(Qi + Qs); c1 = -(Ua + Um)/Ca; c2 = Um/Ca; c3 = (Qa + Tout*Ua)/Ca; c6 = Qm/Cm; c7 = Qa/Ca; double p1 = 1/c2; if (Cm<=0) throw "Cm must be positive"; c4 = Um/Cm; c5 = -c4; if (c2<=0) throw "Um must be positive"; double p2 = -(c5+c1)/c2; double p3 = c1*c5/c2 - c4; double p4 = -c3*c5/c2 + c6; if (p3==0) throw "Teq is not finite"; Teq = p4/p3; /* compute solution roots */ if (p1==0) throw "internal error (p1==0 -> Ca==0 which should have caught)"; const double ra = 2*p1; const double rb = -p2/ra; const double rr = p2*p2-4*p1*p3; if (rr<0) throw "thermal solution does not exist"; const double rc = sqrt(rr)/ra; r1 = rb+rc; r2 = rb-rc; if (r1>0 || r2>0) throw "thermal solution has runaway condition"; /* compute next initial condition */ dTi = c2*Tm + c1*Ti - (c1+c2)*Tout + c7; k1 = (r2*Ti - r2*Teq - dTi)/(r2-r1); k2 = (dTi - r1*k1)/r2; /* calculate power */ zone.total.power = zone.lights.enduse.power + zone.plugs.enduse.power + zone.hvac.enduse.power; if (warn_control) { /* check for heating equipment sizing problem */ if ((mode==HC_HEAT || mode==HC_AUX) && Teq<TheatOff) { OBJECT *obj = OBJECTHDR(this); DATETIME dt0, dt1; gl_localtime(t0,&dt0); gl_localtime(t1,&dt1); char ts0[64], ts1[64]; gl_warning("office:%d (%s) %s heating undersized between %s and %s", obj->id, obj->name?obj->name:"anonymous", mode==HC_HEAT?"primary":"auxiliary", gl_strtime(&dt0,ts0,sizeof(ts0))?ts0:"UNKNOWN", gl_strtime(&dt1,ts1,sizeof(ts1))?ts1:"UNKNOWN"); } /* check for cooling equipment sizing problem */ else if (mode==HC_COOL && Teq>TcoolOff) { OBJECT *obj = OBJECTHDR(this); DATETIME dt0, dt1; gl_localtime(t0,&dt0); gl_localtime(t1,&dt1); char ts0[64], ts1[64]; gl_warning("office:%d (%s) cooling undersized between %s and %s", obj->id, obj->name?obj->name:"anonymous", mode==HC_COOL?"COOL":"ECON", gl_strtime(&dt0,ts0,sizeof(ts0))?ts0:"UNKNOWN", gl_strtime(&dt1,ts1,sizeof(ts1))?ts1:"UNKNOWN"); } /* check for economizer control problem */ else if (mode==HC_ECON && Teq>TcoolOff) { OBJECT *obj = OBJECTHDR(this); DATETIME dt; gl_localtime(t1,&dt); char ts[64]; gl_warning("office:%d (%s) insufficient economizer control at %s", obj->id, obj->name?obj->name:"anonymous", gl_strtime(&dt,ts,sizeof(ts))?ts:"UNKNOWN"); } } } /* determine the temperature of the next event */ if (Tevent == Teq) return -(t1+(TIMESTAMP)(3600*TS_SECOND)); /* soft return not more than an hour */ /* solve for the time to the next event */ double dt2=(double)TS_NEVER; dt2 = e2solve(k1,r1,k2,r2,Teq-Tevent)*3600; if (isnan(dt2) || !isfinite(dt2) || dt2<0) { if (dTi==0) /* never more than an hour because of the occupancy schedule */ return -(t1+(TIMESTAMP)(3600*TS_SECOND)); /* soft return */ /* do not allow more than 1 degree/hour temperature change before solving again */ dt2 = fabs(3600/dTi); if (dt2>3600) dt2 = 3600; /* never more than an hour because of the occupancy schedule */ return -(t1+(TIMESTAMP)(dt2*TS_SECOND)); /* soft return */ } if (dt2<TS_SECOND) return t1+1; /* need to do a second pass to get next state */ else return t1+(TIMESTAMP)(dt2*TS_SECOND); /* return t2>t1 on success, t2=t1 for retry, t2<t1 on failure */ }
TIMESTAMP metrics::postsync(TIMESTAMP t0, TIMESTAMP t1) { DATETIME dt; int returnval; bool metrics_written; OBJECT *hdr = OBJECTHDR(this); FILE *FPVal; //Initialization if (curr_time == TS_NEVER) { //Update time value trackers if (metric_interval == 0) //No metric update interval - ensure never goes off { next_metric_interval = TS_NEVER; } else //OK, proceed { next_metric_interval = t1 + metric_interval; } next_report_interval = t1 + report_interval; next_annual_interval = t1 + 31536000; //t1 + 365 days of seconds //Outputs in CSV format - solves issue of column alignment - assuming we want event log if (report_event_log == true) { //Open the file FPVal = fopen(report_file,"at"); //Figure out when we are, roughly (use t1 here) gl_localtime(t1,&dt); //Write header stuffs fprintf(FPVal,"Events for year starting at %04d-%02d-%02d %02d:%02d:%02d\n",dt.year,dt.month,dt.day,dt.hour,dt.minute,dt.second); //Determine which header to write if (secondary_interruptions_count == true) { fprintf(FPVal,"Annual Event #,Metric Interval Event #,Starting DateTime (YYYY-MM-DD hh:mm:ss),Ending DateTime (YYYY-MM-DD hh:mm:ss),Object type,Object Name,Inducing Object,\"Protective\" Device,Desired Fault type,Implemented Fault Type,Number customers affected,Secondary number of customers affected\n"); } else //Nope { fprintf(FPVal,"Annual Event #,Metric Interval Event #,Starting DateTime (YYYY-MM-DD hh:mm:ss),Ending DateTime (YYYY-MM-DD hh:mm:ss),Object type,Object Name,Inducing Object,\"Protective\" Device,Desired Fault type,Implemented Fault Type,Number customers affected\n"); } //Close the file handle fclose(FPVal); } //First run - t1 = t0 (t0 is zero) curr_time = t1; } //Only worry about checks once per timestep if (curr_time != t0) { //Reset temp flag metrics_written = false; if (t0 >= next_report_interval) { //Write the output metric values write_metrics(); //Update variable metrics_written = true; //Update the interval next_report_interval = t0 + report_interval; } //See if it is time to write an update if (t0 >= next_metric_interval) { //See if the metrics were written - if not, dumb them again for posterity if (metrics_written == false) write_metrics(); //Update the interval next_metric_interval = t0 + metric_interval; //Reset the stat variables returnval = ((int (*)(OBJECT *, OBJECT *))(*reset_interval_func))(hdr,module_metrics_obj); if (returnval != 1) //See if it failed { GL_THROW("Failed to reset interval metrics for %s by metrics:%s",module_metrics_obj->name,hdr->name); //Defined above } //Reset the counter metric_interval_event_count = 0; //Indicate a new interval is going on - if we aren't a year (otherwise it happens below) if ((report_event_log == true) && (metric_equal_annual == false)) { //Open the file FPVal = fopen(report_file,"at"); //Figure out when we are gl_localtime(t0,&dt); //Write header stuffs fprintf(FPVal,"\nNew Metric Interval started at %04d-%02d-%02d %02d:%02d:%02d\n\n",dt.year,dt.month,dt.day,dt.hour,dt.minute,dt.second); //Close the file handle fclose(FPVal); } }//End interval metrics update //See if we've exceeded a year if (t0 >= next_annual_interval) { //See if we need to write the file - if "metric_interval" just went off, no sense writing the same thing again if (metrics_written == false) write_metrics(); //Update interval next_annual_interval = t0 + 31536000; //t0 + 365 days of seconds //Reset the stats returnval = ((int (*)(OBJECT *, OBJECT *))(*reset_annual_func))(hdr,module_metrics_obj); if (returnval != 1) //See if it failed { GL_THROW("Failed to reset annual metrics for %s by metrics:%s",module_metrics_obj->name,hdr->name); //Defined above } //Reset the counter annual_interval_event_count = 0; //Indicate a new interval is going on if (report_event_log == true) { //Open the file FPVal = fopen(report_file,"at"); //Figure out when we are gl_localtime(t0,&dt); //Write header stuffs fprintf(FPVal,"\nNew Annual Interval started at %04d-%02d-%02d %02d:%02d:%02d\n\n",dt.year,dt.month,dt.day,dt.hour,dt.minute,dt.second); //Close the file handle fclose(FPVal); } }//End annual update //Update tracking variable curr_time = t0; } //See who to return if (next_metric_interval < next_annual_interval) { if (next_metric_interval < next_report_interval) return -next_metric_interval; else return -next_report_interval; } else { if (next_annual_interval < next_report_interval) return -next_annual_interval; else return -next_report_interval; } }
TIMESTAMP house::sync_thermal(TIMESTAMP t1, double nHours){ DATETIME tv; double t = 0.0; gl_localtime(t1, &tv); Tout = *pTout; Tsolar = get_Tsolar(tv.hour, tv.month, Tair, *pTout); solar_load = 0.0; for (int i = 1; i<9; i++) { solar_load += (gross_wall_area*window_wall_ratio/8.0) * glazing_shgc * pSolar[i]; } double netHeatrate = /*hvac_rated_capacity +*/ tload.heatgain*BTUPHPW + solar_load; double Q1 = M_inv11*Tair + M_inv12*Tmaterials; double Q2 = M_inv21*Tair + M_inv22*Tmaterials; if (nHours > ROUNDOFF) { double q1 = exp(s1*nHours)*(Q1 + BB11*Tsolar/s1 + BB12*netHeatrate/s1) - BB11*Tsolar/s1 - BB12*netHeatrate/s1; double q2 = exp(s2*nHours)*(Q2 - BB11*Tsolar/s2 - BB12*netHeatrate/s2) + BB11*Tsolar/s2 + BB12*netHeatrate/s2; Tair = q1*(s1-A22)/A21 + q2*(s2-A22)/A21; Tmaterials = q1 + q2; } else return TS_NEVER; // calculate constants for solving time "t" to reach Tevent const double W = (Q1 + (BB11*Tsolar)/s1 + BB12*netHeatrate/s1)*(s1-A22)/A21; const double X = (BB11*Tsolar/s1 + BB12*netHeatrate/s1)*(s1-A22)/A21; const double Y = (Q2 - (BB11*Tsolar)/s2 - BB12*netHeatrate/s2)*(s2-A22)/A21; const double Z = (BB11*Tsolar/s2 + BB12*netHeatrate/s2)*(s2-A22)/A21; // end new solution // determine next internal event temperature int n_solutions = 0; double Tevent; const double TcoolOn = cooling_setpoint+thermostat_deadband; const double TcoolOff = cooling_setpoint-thermostat_deadband; const double TheatOn = heating_setpoint-thermostat_deadband; const double TheatOff = heating_setpoint+thermostat_deadband; /* determine the temperature of the next event */ #define TPREC 0.01 if (hvac_rated_capacity < 0.0) Tevent = TcoolOff; else if (hvac_rated_capacity > 0.0) Tevent = TheatOff; else if (Tair <= TheatOn+TPREC) Tevent = TheatOn; else if (Tair >= TcoolOn-TPREC) Tevent = TcoolOn; else return TS_NEVER; #ifdef OLD_SOLVER if (nHours > TPREC) // int dual_decay_solve(double *ans, double prec, double start, double end, int f, double a, double n, double b, double m, double c) n_solutions = dual_decay_solve(&t,TPREC,0.0 ,nHours,W,s1,Y,s2,Z-X-Tevent); Tair = Tevent; if (n_solutions<0) gl_error("house: solver error"); else if (n_solutions == 0) return TS_NEVER; else if (t == 0) t = 1.0/3600.0; // one second return t1+(TIMESTAMP)(t*3600*TS_SECOND); #else t = e2solve(W,s1,Y,s2,Z-X-Tevent); Tair = Tevent; if (isfinite(t)) { return t1+(TIMESTAMP)(t*3600*TS_SECOND); } else return TS_NEVER; #endif }
/* Postsync is called when the clock needs to advance on the second top-down pass */ TIMESTAMP stubauction::postsync(TIMESTAMP t0, TIMESTAMP t1) { int64 i = 0; int64 j = 0; DATETIME dt; retry = 0; if (t1>=clearat) { gl_localtime(clearat,&dt); // if (verbose) gl_output(" ...%s clearing process started at %s", gl_name(OBJECTHDR(this),myname,sizeof(myname)), gl_strtime(&dt,buffer,sizeof(buffer))?buffer:"unknown time"); /* clear market */ thishr = dt.hour; last_price = next_price; ++market_id; // if(lasthr != thishr){ if(t0 != t1 && 0 == t1 % 3600){ /* add price/quantity to the history */ prices[count%168] = next_price; ++count; /* update the daily and weekly averages */ if(control_mode == CON_NORMAL){ avg168 = 0.0; for(i = 0; i < count && i < 168; ++i){ avg168 += prices[i]; } avg168 /= (count > 168 ? 168 : count); avg24 = 0.0; for(i = 1; i <= 24 && i <= count; ++i){ j = (168 - i + count) % 168; avg24 += prices[j]; } avg24 /= (count > 24 ? 24 : count); avg72 = 0.0; for(i = 1; i <= 72 && i <= count; ++i){ j = (168 - i + count) % 168; avg72 += prices[j]; } avg72 /= (count > 72 ? 72 : count); /* update the daily & weekly standard deviations */ std168 = 0.0; for(i = 0; i < count && i < 168; ++i){ std168 += prices[i] * prices[i]; } std168 /= (count > 168 ? 168 : count); std168 -= avg168*avg168; std168 = sqrt(fabs(std168)); std24 = 0.0; for(i = 1; i <= 24 && i <= count; ++i){ j = (168 - i + count) % 168; std24 += prices[j] * prices[j]; } std24 /= (count > 24 ? 24 : count); std24 -= avg24*avg24; std24 = sqrt(fabs(std24)); std72 = 0.0; for(i = 1; i <= 72 && i <= count; ++i){ j = (168 - i + count) % 168; std72 += prices[j] * prices[j]; } std72 /= (count > 72 ? 72 : count); std72 -= avg72*avg72; std72 = sqrt(fabs(std72)); retry = 1; } /* update reference hour */ lasthr = thishr; } market_id++; clearat = nextclear(); gl_localtime(clearat,&dt); // if (verbose) gl_output(" ...%s opens for clearing of market_id %d at %s", gl_name(OBJECTHDR(this),name,sizeof(name)), (int32)market_id, gl_strtime(&dt,buffer,sizeof(buffer))?buffer:"unknown time"); } return (retry ? t1 : -clearat); /* soft return t2>t1 on success, t2=t1 for retry, t2<t1 on failure */ }
TIMESTAMP meter::postsync(TIMESTAMP t0, TIMESTAMP t1) { measured_voltage[0] = voltageA; measured_voltage[1] = voltageB; measured_voltage[2] = voltageC; measured_voltageD[0] = voltageA - voltageB; measured_voltageD[1] = voltageB - voltageC; measured_voltageD[2] = voltageC - voltageA; if ((solver_method == SM_NR && NR_cycle == true)||solver_method == SM_FBS) { //Reliability addition - if momentary flag set - clear it if (meter_interrupted_secondary == true) meter_interrupted_secondary = false; if (t1 > last_t) { dt = t1 - last_t; last_t = t1; } else dt = 0; measured_current[0] = current_inj[0]; measured_current[1] = current_inj[1]; measured_current[2] = current_inj[2]; // compute energy use from previous cycle // - everything below this can moved to commit function once tape player is collecting from commit function7 if (dt > 0 && last_t != dt) { measured_real_energy += measured_real_power * TO_HOURS(dt); measured_reactive_energy += measured_reactive_power * TO_HOURS(dt); } // compute demand power indiv_measured_power[0] = measured_voltage[0]*(~measured_current[0]); indiv_measured_power[1] = measured_voltage[1]*(~measured_current[1]); indiv_measured_power[2] = measured_voltage[2]*(~measured_current[2]); measured_power = indiv_measured_power[0] + indiv_measured_power[1] + indiv_measured_power[2]; measured_real_power = (indiv_measured_power[0]).Re() + (indiv_measured_power[1]).Re() + (indiv_measured_power[2]).Re(); measured_reactive_power = (indiv_measured_power[0]).Im() + (indiv_measured_power[1]).Im() + (indiv_measured_power[2]).Im(); if (measured_real_power > measured_demand) measured_demand = measured_real_power; if (bill_mode == BM_UNIFORM || bill_mode == BM_TIERED) { if (dt > 0) process_bill(t1); // Decide when the next billing HAS to be processed (one month later) if (monthly_bill == previous_monthly_bill) { DATETIME t_next; gl_localtime(t1,&t_next); t_next.day = bill_day; if (t_next.month != 12) t_next.month += 1; else { t_next.month = 1; t_next.year += 1; } t_next.tz[0] = 0; next_time = gl_mktime(&t_next); } } if( (bill_mode == BM_HOURLY || bill_mode == BM_TIERED_RTP) && power_market != NULL && price_prop != NULL){ double seconds; if (dt != last_t) seconds = (double)(dt); else seconds = 0; if (seconds > 0) { hourly_acc += seconds/3600 * price * last_measured_real_power/1000; process_bill(t1); } // Now that we've accumulated the bill for the last time period, update to the new price double *pprice = (gl_get_double(power_market, price_prop)); last_price = price = *pprice; last_measured_real_power = measured_real_power; if (monthly_bill == previous_monthly_bill) { DATETIME t_next; gl_localtime(t1,&t_next); t_next.day = bill_day; if (t_next.month != 12) t_next.month += 1; else { t_next.month = 1; t_next.year += 1; } t_next.tz[0] = 0; next_time = gl_mktime(&t_next); } } } return node::postsync(t1); }
TIMESTAMP meter::postsync(TIMESTAMP t0, TIMESTAMP t1) { OBJECT *obj = OBJECTHDR(this); complex temp_current; TIMESTAMP tretval; //Perform node update - do it now, otherwise current_inj isn't populated tretval = node::postsync(t1); measured_voltage[0] = voltageA; measured_voltage[1] = voltageB; measured_voltage[2] = voltageC; measured_voltageD[0] = voltageA - voltageB; measured_voltageD[1] = voltageB - voltageC; measured_voltageD[2] = voltageC - voltageA; if ((solver_method == SM_NR)||solver_method == SM_FBS) { if (t1 > last_t) { dt = t1 - last_t; last_t = t1; } else dt = 0; measured_current[0] = current_inj[0]; measured_current[1] = current_inj[1]; measured_current[2] = current_inj[2]; // compute energy use from previous cycle // - everything below this can moved to commit function once tape player is collecting from commit function7 if (dt > 0 && last_t != dt) { measured_real_energy += measured_real_power * TO_HOURS(dt); measured_reactive_energy += measured_reactive_power * TO_HOURS(dt); } // compute demand power indiv_measured_power[0] = measured_voltage[0]*(~measured_current[0]); indiv_measured_power[1] = measured_voltage[1]*(~measured_current[1]); indiv_measured_power[2] = measured_voltage[2]*(~measured_current[2]); measured_power = indiv_measured_power[0] + indiv_measured_power[1] + indiv_measured_power[2]; measured_real_power = (indiv_measured_power[0]).Re() + (indiv_measured_power[1]).Re() + (indiv_measured_power[2]).Re(); measured_reactive_power = (indiv_measured_power[0]).Im() + (indiv_measured_power[1]).Im() + (indiv_measured_power[2]).Im(); if (measured_real_power > measured_demand) measured_demand = measured_real_power; if (bill_mode == BM_UNIFORM || bill_mode == BM_TIERED) { if (dt > 0) process_bill(t1); // Decide when the next billing HAS to be processed (one month later) if (monthly_bill == previous_monthly_bill) { DATETIME t_next; gl_localtime(t1,&t_next); t_next.day = bill_day; if (t_next.month != 12) t_next.month += 1; else { t_next.month = 1; t_next.year += 1; } t_next.tz[0] = 0; next_time = gl_mktime(&t_next); } } if( (bill_mode == BM_HOURLY || bill_mode == BM_TIERED_RTP) && power_market != NULL && price_prop != NULL){ double seconds; if (dt != last_t) seconds = (double)(dt); else seconds = 0; if (seconds > 0) { hourly_acc += seconds/3600 * price * last_measured_real_power/1000; process_bill(t1); } // Now that we've accumulated the bill for the last time period, update to the new price double *pprice = (gl_get_double(power_market, price_prop)); last_price = price = *pprice; last_measured_real_power = measured_real_power; if (monthly_bill == previous_monthly_bill) { DATETIME t_next; gl_localtime(t1,&t_next); t_next.day = bill_day; if (t_next.month != 12) t_next.month += 1; else { t_next.month = 1; t_next.year += 1; } t_next.tz[0] = 0; next_time = gl_mktime(&t_next); } } } //Multi run (for now) updates to power values if (meter_NR_servered) { // compute demand power indiv_measured_power[0] = voltage[0]*(~current_inj[0]); indiv_measured_power[1] = voltage[1]*(~current_inj[1]); indiv_measured_power[2] = voltage[2]*(~current_inj[2]); } return tretval; }
//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; }
TIMESTAMP csv_reader::get_data(TIMESTAMP t0, double *temp, double *humid, double *direct, double *diffuse, double *global, double *wind, double *rain, double *snow){ DATETIME now, then; // TIMESTAMP until; int next_year = 0; int i = 0; int idx = index; int start = index; int localres; if(t0 < next_ts){ /* still good ~ go home */ return -next_ts; } localres = gl_localtime(t0, &now); // error check if(next_ts == 0){ // initialize to the correct index & next_ts DATETIME guess_dt; TIMESTAMP guess_ts; int i; for(i = 0; i < sample_ct; ++i){ guess_dt.year = now.year; guess_dt.month = samples[sample_ct-i-1]->month; guess_dt.day = samples[sample_ct-i-1]->day; guess_dt.hour = samples[sample_ct-i-1]->hour; guess_dt.minute = samples[sample_ct-i-1]->minute; guess_dt.second = samples[sample_ct-i-1]->second; strcpy(guess_dt.tz, now.tz); // strcpy(guess_dt.tz, "GMT"); guess_ts = (TIMESTAMP)gl_mktime(&guess_dt); if(guess_ts <= t0){ break; } } index = sample_ct - i - 1; if(index > -1){ *temp = samples[index]->temperature; *humid = samples[index]->humidity; *direct = samples[index]->solar_dir; *diffuse = samples[index]->solar_diff; *global = samples[index]->solar_global; *wind = samples[index]->wind_speed; *rain = samples[index]->rainfall; *snow = samples[index]->snowdepth; } else { *temp = samples[sample_ct - 1]->temperature; *humid = samples[sample_ct - 1]->humidity; *direct = samples[sample_ct - 1]->solar_dir; *diffuse = samples[sample_ct - 1]->solar_diff; *global = samples[sample_ct - 1]->solar_global; *wind = samples[sample_ct - 1]->wind_speed; *rain = samples[sample_ct - 1]->rainfall; *snow = samples[sample_ct - 1]->snowdepth; } then.year = now.year + (index+1 == sample_ct ? 1 : 0); then.month = samples[(index+1)%sample_ct]->month; then.day = samples[(index+1)%sample_ct]->day; then.hour = samples[(index+1)%sample_ct]->hour; then.minute = samples[(index+1)%sample_ct]->minute; then.second = samples[(index+1)%sample_ct]->second; strcpy(then.tz, now.tz); next_ts = (TIMESTAMP)gl_mktime(&then); //next_ts = (TIMESTAMP)gl_mktime(&then); return -next_ts; } if(sample_ct == 1){ /* only one sample ~ ignore it and keep feeding the same data back, but in a year */ next_ts += 365 * 24 * 3600; return -next_ts; } do{ // should we roll the year over? if(index+1 >= sample_ct){ index = 0; } else { ++index; } if(index+1 == sample_ct){ next_year = 1; } else { next_year = 0; } then.year = now.year + next_year; then.month = samples[(index+1)%sample_ct]->month; then.day = samples[(index+1)%sample_ct]->day; then.hour = samples[(index+1)%sample_ct]->hour; then.minute = samples[(index+1)%sample_ct]->minute; then.second = samples[(index+1)%sample_ct]->second; strcpy(then.tz, now.tz); // next_ts is the time the current sample is overwritten by another sample. next_ts = (TIMESTAMP)gl_mktime(&then); } while (next_ts < t0 && index != start); // skip samples that try to reverse the time *temp = samples[index]->temperature; *humid = samples[index]->humidity; *direct = samples[index]->solar_dir; *diffuse = samples[index]->solar_diff; *global = samples[index]->solar_global; *wind = samples[index]->wind_speed; *rain = samples[index]->rainfall; *snow = samples[index]->snowdepth; // having found the index, update the data if(index == start){ GL_THROW("something strange happened with the schedule in csv_reader"); /* TROUBLESHOOT An unidentified error occured while reading data and constructing the weather data schedule. Please post a ticket detailing this event on the GridLAB-D SourceForge page. */ } return -next_ts; }
// Synchronize a distribution triplex_meter TIMESTAMP triplex_meter::postsync(TIMESTAMP t0, TIMESTAMP t1) { OBJECT *obj = OBJECTHDR(this); TIMESTAMP rv = TS_NEVER; TIMESTAMP hr = TS_NEVER; //Call node postsync now, otherwise current_inj isn't right rv = triplex_node::postsync(t1); //measured_voltage[0] = voltageA; //measured_voltage[1] = voltageB; //measured_voltage[2] = voltageC; measured_voltage[0].SetPolar(voltageA.Mag(),voltageA.Arg()); measured_voltage[1].SetPolar(voltageB.Mag(),voltageB.Arg()); measured_voltage[2].SetPolar(voltageC.Mag(),voltageC.Arg()); if (t1 > last_t) { dt = t1 - last_t; last_t = t1; } else dt = 0; //READLOCK_OBJECT(obj); measured_current[0] = current_inj[0]; measured_current[1] = current_inj[1]; //READUNLOCK_OBJECT(obj); measured_current[2] = -(measured_current[1]+measured_current[0]); // if (dt > 0 && last_t != dt) if (dt > 0) { measured_real_energy += measured_real_power * TO_HOURS(dt); measured_reactive_energy += measured_reactive_power * TO_HOURS(dt); } indiv_measured_power[0] = measured_voltage[0]*(~measured_current[0]); indiv_measured_power[1] = complex(-1,0) * measured_voltage[1]*(~measured_current[1]); indiv_measured_power[2] = measured_voltage[2]*(~measured_current[2]); measured_power = indiv_measured_power[0] + indiv_measured_power[1] + indiv_measured_power[2]; measured_real_power = (indiv_measured_power[0]).Re() + (indiv_measured_power[1]).Re() + (indiv_measured_power[2]).Re(); measured_reactive_power = (indiv_measured_power[0]).Im() + (indiv_measured_power[1]).Im() + (indiv_measured_power[2]).Im(); if (measured_real_power>measured_demand) measured_demand=measured_real_power; if (bill_mode == BM_UNIFORM || bill_mode == BM_TIERED) { if (dt > 0) process_bill(t1); // Decide when the next billing HAS to be processed (one month later) if (monthly_bill == previous_monthly_bill) { DATETIME t_next; gl_localtime(t1,&t_next); t_next.day = bill_day; if (t_next.month != 12) t_next.month += 1; else { t_next.month = 1; t_next.year += 1; } t_next.tz[0] = 0; next_time = gl_mktime(&t_next); } } if( (bill_mode == BM_HOURLY || bill_mode == BM_TIERED_RTP) && power_market != NULL && price_prop != NULL){ double seconds; if (dt != last_t) seconds = (double)(dt); else seconds = 0; if (seconds > 0) { hourly_acc += seconds/3600 * price * last_measured_real_power/1000; process_bill(t1); } // Now that we've accumulated the bill for the last time period, update to the new price double *pprice = (gl_get_double(power_market, price_prop)); last_price = price = *pprice; last_measured_real_power = measured_real_power; if (monthly_bill == previous_monthly_bill) { DATETIME t_next; gl_localtime(t1,&t_next); t_next.day = bill_day; if (t_next.month != 12) t_next.month += 1; else { t_next.month = 1; t_next.year += 1; } t_next.tz[0] = 0; next_time = gl_mktime(&t_next); } } if (next_time != 0 && next_time < rv) return -next_time; else return rv; }
//Solar radiation calcuation based on solpos and Perez tilt models EXPORT int64 calc_solar_solpos_shading_rad(OBJECT *obj, double tilt, double orientation, double shading_value, double *value) { static SolarAngles sa; // just for the functions double ghr, dhr, dnr; double cos_incident; double temp_value; DATETIME dt; TIMESTAMP offsetclock; climate *cli; if(obj == 0 || value == 0){ return 0; } cli = OBJECTDATA(obj, climate); if(gl_object_isa(obj, "climate", "climate") == 0){ return 0; } ghr = cli->solar_global; dhr = cli->solar_diffuse; dnr = cli->solar_direct; if (cli->reader_type==cli->RT_TMY2) { //Adjust time by half an hour - adjusts per TMY "reading" intervals - what they really represent offsetclock = obj->clock + 1800; } else //Just pass it in { offsetclock = obj->clock; } gl_localtime(offsetclock, &dt); //Convert temperature back to centrigrade - since we seem to like imperial units temp_value = ((cli->temperature - 32.0)*5.0/9.0); //Initialize solpos algorithm sa.S_init(&sa.solpos_vals); //Assign in values sa.solpos_vals.longitude = obj->longitude; sa.solpos_vals.latitude = RAD(obj->latitude); if (dt.is_dst == 1) { sa.solpos_vals.timezone = cli->tz_offset_val-1.0; } else { sa.solpos_vals.timezone = cli->tz_offset_val; } sa.solpos_vals.year = dt.year; sa.solpos_vals.daynum = (dt.yearday+1); sa.solpos_vals.hour = dt.hour; sa.solpos_vals.minute = dt.minute; sa.solpos_vals.second = dt.second; sa.solpos_vals.temp = temp_value; sa.solpos_vals.press = cli->pressure; // Solar constant associated with extraterrestrial DNI, 1367 W/sq m - pull from TMY for now //sa.solpos_vals.solcon = 126.998456; //Use constant value for direct normal extraterrestrial irradiance - doesn't seem right to me sa.solpos_vals.solcon = cli->direct_normal_extra; //Use weather-read version (TMY) sa.solpos_vals.aspect = orientation; sa.solpos_vals.tilt = tilt; sa.solpos_vals.diff_horz = dhr; sa.solpos_vals.dir_norm = dnr; //Calculate different solar position values sa.S_solpos(&sa.solpos_vals); //Pull off new cosine of incidence if (sa.solpos_vals.cosinc >= 0.0) cos_incident = sa.solpos_vals.cosinc; else cos_incident = 0.0; //Apply the adjustment *value = (shading_value*dnr*cos_incident) + dhr*sa.solpos_vals.perez_horz + ghr*((1 - cos(tilt))*cli->ground_reflectivity/2.0); return 1; }
// Synchronize a distribution triplex_meter TIMESTAMP triplex_meter::postsync(TIMESTAMP t0, TIMESTAMP t1) { TIMESTAMP rv = TS_NEVER; TIMESTAMP hr = TS_NEVER; //measured_voltage[0] = voltageA; //measured_voltage[1] = voltageB; //measured_voltage[2] = voltageC; measured_voltage[0].SetPolar(voltageA.Mag(),voltageA.Arg()); measured_voltage[1].SetPolar(voltageB.Mag(),voltageB.Arg()); measured_voltage[2].SetPolar(voltageC.Mag(),voltageC.Arg()); if ((solver_method == SM_NR && NR_cycle == true)||solver_method == SM_FBS) { //Reliability addition - clear momentary flag if set if (tpmeter_interrupted_secondary == true) tpmeter_interrupted_secondary = false; if (t1 > last_t) { dt = t1 - last_t; last_t = t1; } else dt = 0; measured_current[0] = current_inj[0]; measured_current[1] = current_inj[1]; measured_current[2] = -(measured_current[1]+measured_current[0]); // if (dt > 0 && last_t != dt) if (dt > 0) { measured_real_energy += measured_real_power * TO_HOURS(dt); measured_reactive_energy += measured_reactive_power * TO_HOURS(dt); } indiv_measured_power[0] = measured_voltage[0]*(~measured_current[0]); indiv_measured_power[1] = complex(-1,0) * measured_voltage[1]*(~measured_current[1]); indiv_measured_power[2] = measured_voltage[2]*(~measured_current[2]); measured_power = indiv_measured_power[0] + indiv_measured_power[1] + indiv_measured_power[2]; measured_real_power = (indiv_measured_power[0]).Re() + (indiv_measured_power[1]).Re() + (indiv_measured_power[2]).Re(); measured_reactive_power = (indiv_measured_power[0]).Im() + (indiv_measured_power[1]).Im() + (indiv_measured_power[2]).Im(); if (measured_real_power>measured_demand) measured_demand=measured_real_power; if (bill_mode == BM_UNIFORM || bill_mode == BM_TIERED) { if (dt > 0) process_bill(t1); // Decide when the next billing HAS to be processed (one month later) if (monthly_bill == previous_monthly_bill) { DATETIME t_next; gl_localtime(t1,&t_next); t_next.day = bill_day; if (t_next.month != 12) t_next.month += 1; else { t_next.month = 1; t_next.year += 1; } t_next.tz[0] = 0; next_time = gl_mktime(&t_next); } } if( (bill_mode == BM_HOURLY || bill_mode == BM_TIERED_RTP) && power_market != NULL && price_prop != NULL){ double seconds; if (dt != last_t) seconds = (double)(dt); else seconds = 0; if (seconds > 0) { hourly_acc += seconds/3600 * price * measured_real_power/1000; process_bill(t1); } // Now that we've accumulated the bill for the last time period, update to the new price double *pprice = (gl_get_double(power_market, price_prop)); last_price = price = *pprice; if (monthly_bill == previous_monthly_bill) { DATETIME t_next; gl_localtime(t1,&t_next); t_next.day = bill_day; if (t_next.month != 12) t_next.month += 1; else { t_next.month = 1; t_next.year += 1; } t_next.tz[0] = 0; next_time = gl_mktime(&t_next); } } } rv = triplex_node::postsync(t1); if (next_time != 0 && next_time < rv) return -next_time; else return rv; //return triplex_node::postsync(t1); }
TIMESTAMP climate::presync(TIMESTAMP t0) /* called in presync */ { TIMESTAMP rv = 0; if(t0 > TS_ZERO && reader_type == RT_CSV){ DATETIME now; gl_localtime(t0, &now); //OBJECT *obj = OBJECTHDR(this); csv_reader *cr = OBJECTDATA(reader,csv_reader); rv = cr->get_data(t0, &temperature, &humidity, &solar_direct, &solar_diffuse, &solar_global, &wind_speed, &rainfall, &snowdepth, &pressure); // calculate the solar radiation double sol_time = sa->solar_time((double)now.hour+now.minute/60.0+now.second/3600.0 + (now.is_dst ? -1:0),now.yearday,RAD(tz_meridian),RAD(reader->longitude)); double sol_rad = 0.0; 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,now.yearday,RAD(reader->latitude),sol_time,solar_direct,solar_diffuse,solar_global,ground_reflectivity,0.0);//(double)dnr * cos_incident + dhr; else sol_rad = file.calc_solar(c_point,now.yearday,RAD(reader->latitude),sol_time,solar_direct,solar_diffuse,solar_global,ground_reflectivity);//(double)dnr * cos_incident + dhr; /* TMY2 solar radiation data is in Watt-hours per square meter. */ solar_flux[c_point] = sol_rad; } return rv; } if (t0>TS_ZERO && tmy!=NULL) { DATETIME ts; int localres = gl_localtime(t0,&ts); int hoy; double now, hoy0, hoy1, hoy2; if(localres == 0){ GL_THROW("climate::sync -- unable to resolve localtime!"); } int doy = sa->day_of_yr(ts.month,ts.day); hoy = (doy - 1) * 24 + (ts.hour); switch(interpolate){ case CI_NONE: temperature = tmy[hoy].temp; temperature_raw = tmy[hoy].temp_raw; humidity = tmy[hoy].rh; solar_direct = tmy[hoy].dnr; solar_diffuse = tmy[hoy].dhr; solar_global = tmy[hoy].ghr; solar_raw = tmy[hoy].solar_raw; solar_azimuth = tmy[hoy].solar_azimuth; solar_elevation = tmy[hoy].solar_elevation; pressure = tmy[hoy].pressure; direct_normal_extra = tmy[hoy].direct_normal_extra; this->wind_speed = tmy[hoy].windspeed; this->rainfall = tmy[hoy].rainfall; this->snowdepth = tmy[hoy].snowdepth; if(memcmp(solar_flux,tmy[hoy].solar,CP_LAST*sizeof(double))) memcpy(solar_flux,tmy[hoy].solar,CP_LAST*sizeof(double)); break; case CI_LINEAR: now = hoy+ts.minute/60.0; hoy0 = hoy; hoy1 = hoy+1.0; temperature = gl_lerp(now, hoy0, tmy[hoy].temp, hoy1, tmy[hoy+1%8760].temp); temperature_raw = gl_lerp(now, hoy0, tmy[hoy].temp_raw, hoy1, tmy[hoy+1%8760].temp_raw); humidity = gl_lerp(now, hoy0, tmy[hoy].rh, hoy1, tmy[hoy+1%8760].rh); solar_direct = gl_lerp(now, hoy0, tmy[hoy].dnr, hoy1, tmy[hoy+1%8760].dnr); solar_diffuse = gl_lerp(now, hoy0, tmy[hoy].dhr, hoy1, tmy[hoy+1%8760].dhr); solar_global = gl_lerp(now, hoy0, tmy[hoy].ghr, hoy1, tmy[hoy+1%8760].ghr); solar_azimuth = gl_lerp(now, hoy0, tmy[hoy].solar_azimuth, hoy1, tmy[hoy+1%8760].solar_azimuth); solar_elevation = gl_lerp(now, hoy0, tmy[hoy].solar_elevation, hoy1, tmy[hoy+1%8760].solar_elevation); wind_speed = gl_lerp(now, hoy0, tmy[hoy].windspeed, hoy1, tmy[hoy+1%8760].windspeed); rainfall = gl_lerp(now, hoy0, tmy[hoy].rainfall, hoy1, tmy[hoy+1%8760].rainfall); snowdepth = gl_lerp(now, hoy0, tmy[hoy].snowdepth, hoy1, tmy[hoy+1%8760].snowdepth); solar_raw = gl_lerp(now, hoy0, tmy[hoy].solar_raw, hoy1, tmy[hoy+1%8760].solar_raw); pressure = gl_lerp(now, hoy0, tmy[hoy].pressure, hoy1, tmy[hoy+1%8760].pressure); direct_normal_extra = gl_lerp(now, hoy0, tmy[hoy].direct_normal_extra, hoy1, tmy[hoy+1%8760].direct_normal_extra); for(int pt = 0; pt < CP_LAST; ++pt){ solar_flux[pt] = gl_lerp(now, hoy0, tmy[hoy].solar[pt], hoy1, tmy[hoy+1%8760].solar[pt]); } break; case CI_QUADRATIC: now = hoy+ts.minute/60.0; hoy0 = hoy; hoy1 = hoy+1.0; hoy2 = hoy+2.0; temperature = gl_qerp(now, hoy0, tmy[hoy].temp, hoy1, tmy[hoy+1%8760].temp, hoy2, tmy[hoy+2%8760].temp); temperature_raw = gl_qerp(now, hoy0, tmy[hoy].temp_raw, hoy1, tmy[hoy+1%8760].temp_raw, hoy2, tmy[hoy+2%8760].temp_raw); humidity = gl_qerp(now, hoy0, tmy[hoy].rh, hoy1, tmy[hoy+1%8760].rh, hoy2, tmy[hoy+2%8760].rh); if(humidity < 0.0){ humidity = 0.0; gl_verbose("Setting humidity to zero. Quadratic interpolation caused the humidity to drop below zero."); } solar_direct = gl_qerp(now, hoy0, tmy[hoy].dnr, hoy1, tmy[hoy+1%8760].dnr, hoy2, tmy[hoy+2%8760].dnr); if(solar_direct < 0.0){ solar_direct = 0.0; gl_verbose("Setting solar_direct to zero. Quadratic interpolation caused the solar_direct to drop below zero."); } solar_diffuse = gl_qerp(now, hoy0, tmy[hoy].dhr, hoy1, tmy[hoy+1%8760].dhr, hoy2, tmy[hoy+2%8760].dhr); if(solar_diffuse < 0.0){ solar_diffuse = 0.0; gl_verbose("Setting solar_diffuse to zero. Quadratic interpolation caused the solar_diffuse to drop below zero."); } solar_global = gl_qerp(now, hoy0, tmy[hoy].ghr, hoy1, tmy[hoy+1%8760].ghr, hoy2, tmy[hoy+2%8760].ghr); if(solar_global < 0.0){ solar_global = 0.0; gl_verbose("Setting solar_global to zero. Quadratic interpolation caused the solar_global to drop below zero."); } solar_azimuth = gl_qerp(now, hoy0, tmy[hoy].solar_azimuth, hoy1, tmy[hoy+1%8760].solar_azimuth, hoy2, tmy[hoy+2%8760].solar_azimuth); solar_elevation = gl_qerp(now, hoy0, tmy[hoy].solar_elevation, hoy1, tmy[hoy+1%8760].solar_elevation, hoy2, tmy[hoy+2%8760].solar_elevation); wind_speed = gl_qerp(now, hoy0, tmy[hoy].windspeed, hoy1, tmy[hoy+1%8760].windspeed, hoy2, tmy[hoy+2%8760].windspeed); if(wind_speed < 0.0){ wind_speed = 0.0; gl_verbose("Setting wind_speed to zero. Quadratic interpolation caused the wind_speed to drop below zero."); } rainfall = gl_qerp(now, hoy0, tmy[hoy].rainfall, hoy1, tmy[hoy+1%8760].rainfall, hoy2, tmy[hoy+2%8760].rainfall); if(rainfall < 0.0){ rainfall = 0.0; gl_verbose("Setting rainfall to zero. Quadratic interpolation caused the rainfall to drop below zero."); } snowdepth = gl_qerp(now, hoy0, tmy[hoy].snowdepth, hoy1, tmy[hoy+1%8760].snowdepth, hoy2, tmy[hoy+2%8760].snowdepth); if(snowdepth < 0.0){ snowdepth = 0.0; gl_verbose("Setting snowdepth to zero. Quadratic interpolation caused the snowdepth to drop below zero."); } solar_raw = gl_qerp(now, hoy0, tmy[hoy].solar_raw, hoy1, tmy[hoy+1%8760].solar_raw, hoy2, tmy[hoy+2%8760].solar_raw); if(solar_raw < 0.0){ solar_raw = 0.0; gl_verbose("Setting solar_raw to zero. Quadratic interpolation caused the solar_raw to drop below zero."); } pressure = gl_qerp(now, hoy0, tmy[hoy].pressure, hoy1, tmy[hoy+1%8760].pressure, hoy2, tmy[hoy+2%8760].pressure); if(pressure < 0.0){ pressure = 0.0; gl_verbose("Setting pressure to zero. Quadratic interpolation caused the pressure to drop below zero."); } direct_normal_extra = gl_qerp(now, hoy0, tmy[hoy].direct_normal_extra, hoy1, tmy[hoy+1%8760].direct_normal_extra, hoy2, tmy[hoy+2%8760].direct_normal_extra); if(direct_normal_extra < 0.0){ direct_normal_extra = 0.0; gl_verbose("Setting extraterrestrial_direct_normal to zero. Quadratic interpolation caused the extraterrestrial_direct_normal to drop below zero."); } for(int pt = 0; pt < CP_LAST; ++pt){ if(tmy[hoy].solar[pt] == tmy[hoy+1].solar[pt]){ solar_flux[pt] = tmy[hoy].solar[pt]; } else { solar_flux[pt] = gl_qerp(now, hoy0, tmy[hoy].solar[pt], hoy1, tmy[hoy+1%8760].solar[pt], hoy2, tmy[hoy+2%8760].solar[pt]); if(solar_flux[pt] < 0.0) solar_flux[pt] = 0.0; /* quadratic isn't always cooperative... */ } } break; default: GL_THROW("climate::sync -- unrecognize interpolation mode!"); } update_forecasts(t0); return -(t0+(3600*TS_SECOND-t0%(3600 *TS_SECOND))); /// negative means soft event } return TS_NEVER; }
TIMESTAMP controller::sync(TIMESTAMP t0, TIMESTAMP t1){ double bid = -1.0; int64 no_bid = 0; // flag gets set when the current temperature drops in between the the heating setpoint and cooling setpoint curves double demand = 0.0; double rampify = 0.0; extern double bid_offset; OBJECT *hdr = OBJECTHDR(this); /* short circuit if the state variable doesn't change during the specified interval */ if((t1 < next_run) && (market->market_id == lastmkt_id)){ if(t1 == next_run - bid_delay){ ; // continue } else { if (pState == 0) return next_run; else if (*pState == last_pState) return next_run; } } if(control_mode == CN_RAMP){ // if market has updated, continue onwards if(market->market_id != lastmkt_id){// && (*pAvg == 0.0 || *pStd == 0.0 || setpoint0 == 0.0)){ double clear_price; lastmkt_id = market->market_id; lastbid_id = -1; // clear last bid id, refers to an old market // update using last price // T_set,a = T_set + (P_clear - P_avg) * | T_lim - T_set | / (k_T * stdev24) clear_price = market->current_frame.clearing_price; if(fabs(*pStd) < bid_offset){ set_temp = setpoint0; } else if(clear_price < *pAvg && range_low != 0){ set_temp = setpoint0 + (clear_price - *pAvg) * fabs(range_low) / (ramp_low * *pStd); } else if(clear_price > *pAvg && range_high != 0){ set_temp = setpoint0 + (clear_price - *pAvg) * fabs(range_high) / (ramp_high * *pStd); } else { set_temp = setpoint0; } if((use_override == OU_ON) && (pOverride != 0)){ if(clear_price <= last_p){ // if we're willing to pay as much as, or for more than the offered price, then run. *pOverride = 1; } else { *pOverride = -1; } } // clip if(set_temp > max){ set_temp = max; } else if(set_temp < min){ set_temp = min; } *pSetpoint = set_temp; //gl_verbose("controller::postsync(): temp %f given p %f vs avg %f",set_temp, market->next.price, market->avg24); } if(dir > 0){ if(*pMonitor > max){ bid = 9999.0; } else if (*pMonitor < min){ bid = 0.0; no_bid = 1; } } else if(dir < 0){ if(*pMonitor < min){ bid = 9999.0; } else if(*pMonitor > max){ bid = 0.0; no_bid = 1; } } else if(dir == 0){ if(*pMonitor < min){ bid = 9999.0; } else if(*pMonitor > max){ bid = 0.0; no_bid = 1; } else { bid = *pAvg; // override due to lack of "real" curve } } // calculate bid price if(*pMonitor > setpoint0){ k_T = ramp_high; T_lim = range_high; } else if(*pMonitor < setpoint0) { k_T = ramp_low; T_lim = range_low; } else { k_T = 0.0; T_lim = 0.0; } if(bid < 0.0 && *pMonitor != setpoint0) { bid = *pAvg + ( (fabs(*pStd) < bid_offset) ? 0.0 : (*pMonitor - setpoint0) * (k_T * *pStd) / fabs(T_lim) ); } else if(*pMonitor == setpoint0) { bid = *pAvg; } // bid the response part of the load double residual = *pTotal; /* WARNING ~ bid ID check will not work properly */ KEY bid_id = (KEY)(lastmkt_id == market->market_id ? lastbid_id : -1); // override //bid_id = -1; if(*pDemand > 0 && no_bid != 1){ last_p = bid; last_q = *pDemand; if(0 != strcmp(market->unit, "")){ if(0 == gl_convert("kW", market->unit, &(last_q))){ gl_error("unable to convert bid units from 'kW' to '%s'", market->unit); return TS_INVALID; } } //lastbid_id = market->submit(OBJECTHDR(this), -last_q, last_p, bid_id, (BIDDERSTATE)(pState != 0 ? *pState : 0)); if(pState != 0){ lastbid_id = submit_bid_state(pMarket, hdr, -last_q, last_p, (*pState > 0 ? 1 : 0), bid_id); } else { lastbid_id = submit_bid(pMarket, hdr, -last_q, last_p, bid_id); } residual -= *pLoad; } else { last_p = 0; last_q = 0; gl_verbose("%s's is not bidding", hdr->name); } if(residual < -0.001) gl_warning("controller:%d: residual unresponsive load is negative! (%.1f kW)", hdr->id, residual); } else if (control_mode == CN_DOUBLE_RAMP){ /* double heat_range_high; double heat_range_low; double heat_ramp_high; double heat_ramp_low; double cool_range_high; double cool_range_low; double cool_ramp_high; double cool_ramp_low; */ DATETIME t_next; gl_localtime(t1,&t_next); // find crossover double midpoint = 0.0; if(cool_min - heat_max < *pDeadband){ switch(resolve_mode){ case RM_DEADBAND: midpoint = (heat_max + cool_min) / 2; if(midpoint - *pDeadband/2 < heating_setpoint0 || midpoint + *pDeadband/2 > cooling_setpoint0) { gl_error("The midpoint between the max heating setpoint and the min cooling setpoint must be half a deadband away from each base setpoint"); return TS_INVALID; } else { heat_max = midpoint - *pDeadband/2; cool_min = midpoint + *pDeadband/2; } break; case RM_SLIDING: if(heat_max > cooling_setpoint0 - *pDeadband) { gl_error("the max heating setpoint must be a full deadband less than the cooling_base_setpoint"); return TS_INVALID; } if(cool_min < heating_setpoint0 + *pDeadband) { gl_error("The min cooling setpoint must be a full deadband greater than the heating_base_setpoint"); return TS_INVALID; } if(last_mode == TM_OFF || last_mode == TM_COOL){ heat_max = cool_min - *pDeadband; } else if (last_mode == TM_HEAT){ cool_min = heat_max + *pDeadband; } break; default: gl_error("unrecognized resolve_mode when double_ramp overlap resolution is needed"); break; } } // if the market has updated, if(lastmkt_id != market->market_id){ lastmkt_id = market->market_id; lastbid_id = -1; // retrieve cleared price double clear_price; clear_price = market->current_frame.clearing_price; if(clear_price == last_p){ // determine what to do at the marginal price switch(market->clearing_type){ case CT_SELLER: // may need to curtail break; case CT_PRICE: // should not occur case CT_NULL: // q zero or logic error ~ should not occur // occurs during the zero-eth market. //gl_warning("clearing price and bid price are equal with a market clearing type that involves inequal prices"); break; default: break; } } may_run = 1; // calculate setpoints if(fabs(*pStd) < bid_offset){ *pCoolingSetpoint = cooling_setpoint0; *pHeatingSetpoint = heating_setpoint0; } else { if(clear_price > *pAvg){ *pCoolingSetpoint = cooling_setpoint0 + (clear_price - *pAvg) * fabs(cool_range_high) / (cool_ramp_high * *pStd); //*pHeatingSetpoint = heating_setpoint0 + (clear_price - *pAvg) * fabs(heat_range_high) / (heat_ramp_high * *pStd); *pHeatingSetpoint = heating_setpoint0 + (clear_price - *pAvg) * fabs(heat_range_low) / (heat_ramp_low * *pStd); } else if(clear_price < *pAvg){ *pCoolingSetpoint = cooling_setpoint0 + (clear_price - *pAvg) * fabs(cool_range_low) / (cool_ramp_low * *pStd); //*pHeatingSetpoint = heating_setpoint0 + (clear_price - *pAvg) * fabs(heat_range_low) / (heat_ramp_low * *pStd); *pHeatingSetpoint = heating_setpoint0 + (clear_price - *pAvg) * fabs(heat_range_high) / (heat_ramp_high * *pStd); } else { *pCoolingSetpoint = cooling_setpoint0; *pHeatingSetpoint = heating_setpoint0; } } // apply overrides if((use_override == OU_ON)){ if(last_q != 0.0){ if(clear_price == last_p && clear_price != market->pricecap){ if(market->margin_mode == AM_DENY){ *pOverride = -1; } else if(market->margin_mode == AM_PROB){ double r = gl_random_uniform(0, 1.0); if(r < market->current_frame.marginal_frac){ *pOverride = 1; } else { *pOverride = -1; } } } else if(market->current_frame.clearing_price <= last_p){ *pOverride = 1; } else { *pOverride = -1; } } else { // equality *pOverride = 0; // 'normal operation' } } //clip if(*pCoolingSetpoint > cool_max) *pCoolingSetpoint = cool_max; if(*pCoolingSetpoint < cool_min) *pCoolingSetpoint = cool_min; if(*pHeatingSetpoint > heat_max) *pHeatingSetpoint = heat_max; if(*pHeatingSetpoint < heat_min) *pHeatingSetpoint = heat_min; lastmkt_id = market->market_id; } // submit bids double previous_q = last_q; //store the last value, in case we need it last_p = 0.0; last_q = 0.0; // We have to cool if(*pMonitor > cool_max){ last_p = market->pricecap; last_q = *pCoolingDemand; } // We have to heat else if(*pMonitor < heat_min){ last_p = market->pricecap; last_q = *pHeatingDemand; } // We're floating in between heating and cooling else if(*pMonitor > heat_max && *pMonitor < cool_min){ last_p = 0.0; last_q = 0.0; } // We might heat, if the price is right else if(*pMonitor <= heat_max && *pMonitor >= heat_min){ double ramp, range; ramp = (*pMonitor > heating_setpoint0 ? heat_ramp_high : heat_ramp_low); range = (*pMonitor > heating_setpoint0 ? heat_range_high : heat_range_low); if(*pMonitor != cooling_setpoint0){ last_p = *pAvg + ( (fabs(*pStd) < bid_offset) ? 0.0 : (*pMonitor - heating_setpoint0) * ramp * (*pStd) / fabs(range) ); } else { last_p = *pAvg; } last_q = *pHeatingDemand; } // We might cool, if the price is right else if(*pMonitor <= cool_max && *pMonitor >= cool_min){ double ramp, range; ramp = (*pMonitor > cooling_setpoint0 ? cool_ramp_high : cool_ramp_low); range = (*pMonitor > cooling_setpoint0 ? cool_range_high : cool_range_low); if(*pMonitor != cooling_setpoint0){ last_p = *pAvg + ( (fabs(*pStd) < bid_offset) ? 0 : (*pMonitor - cooling_setpoint0) * ramp * (*pStd) / fabs(range) ); } else { last_p = *pAvg; } last_q = *pCoolingDemand; } if(last_p > market->pricecap) last_p = market->pricecap; if(last_p < -market->pricecap) last_p = -market->pricecap; if(0 != strcmp(market->unit, "")){ if(0 == gl_convert("kW", market->unit, &(last_q))){ gl_error("unable to convert bid units from 'kW' to '%s'", market->unit); return TS_INVALID; } } if(last_q > 0.001){ if (pState != 0 ) { KEY bid = (KEY)(lastmkt_id == market->market_id ? lastbid_id : -1); lastbid_id = submit_bid_state(this->pMarket, OBJECTHDR(this), -last_q, last_p, (*pState > 0 ? 1 : 0), bid); } else { KEY bid = (KEY)(lastmkt_id == market->market_id ? lastbid_id : -1); lastbid_id = submit_bid(this->pMarket, OBJECTHDR(this), -last_q, last_p, bid); } } else { if (last_pState != *pState) { KEY bid = (KEY)(lastmkt_id == market->market_id ? lastbid_id : -1); double my_bid = -market->pricecap; if (*pState != 0) my_bid = last_p; lastbid_id = submit_bid_state(this->pMarket, OBJECTHDR(this), -last_q, my_bid, (*pState > 0 ? 1 : 0), bid); } } } if (pState != 0) last_pState = *pState; char timebuf[128]; gl_printtime(t1,timebuf,127); //gl_verbose("controller:%i::sync(): bid $%f for %f kW at %s",hdr->id,last_p,last_q,timebuf); //return postsync(t0, t1); return TS_NEVER; }
TIMESTAMP histogram::sync(TIMESTAMP t0, TIMESTAMP t1) { int i = 0; double value = 0.0; OBJECT *obj = OBJECTHDR(this); if((sampling_interval == -1.0 && t_count > t1) || sampling_interval == 0.0 || (sampling_interval > 0.0 && t1 >= next_sample)) { if(group_list == NULL){ feed_bins(obj->parent); } else { OBJECT *obj = gl_find_next(group_list, NULL); for(; obj != NULL; obj = gl_find_next(group_list, obj)){ feed_bins(obj); } } t_sample = t1; if(sampling_interval > 0.0001){ next_sample = t1 + (int64)(sampling_interval/TS_SECOND); } else { next_sample = TS_NEVER; } } if((counting_interval == -1.0 && t_count < t1) || counting_interval == 0.0 || (counting_interval > 0.0 && t1 >= next_count)) { char line[1025]; char ts[64]; int off=0, i=0; DATETIME dt; /* write the timestamp */ gl_localtime(t1,&dt); gl_strtime(&dt,ts,64); /* write bins */ for(i = 0; i < bin_count; ++i){ off += sprintf(line+off, "%i", binctr[i]); if(i != bin_count){ off += sprintf(line+off, ","); } } /* write line */ ops->write(this, ts, line); /* cleanup */ for(i = 0; i < bin_count; ++i){ binctr[i] = 0; } t_count = t1; if(counting_interval > 0){ next_count = t_count + (int64)(counting_interval/TS_SECOND); } else { next_count = TS_NEVER; } if(--limit < 1){ ops->close(this); next_count = TS_NEVER; next_sample = TS_NEVER; } } return ( next_count < next_sample && counting_interval > 0.0 ? next_count : next_sample ); }