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 irrigation_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; double deadband_shift = 0.0; double shift_direction = 0.0; double shift_setpoint = 0.0; double prediction_ramp = 0.0; double prediction_range = 0.0; double midpoint = 0.0; OBJECT *hdr = OBJECTHDR(this); if(insync==0) { insync=2; } else if(insync==2) { x=gl_get_double_by_name(parent2,"actual_power_non_zero"); // printf("%d %f ",parent2->id,*x); //system("pause"); insync=1; pDemand=x; initial_zipload_power=x; } if(first_period==0) //for two diffrent periods { //OBJECT *p=gl_get_object("sensor"); // double *humidity=gl_get_double_by_name(p,"humidity"); double *humidity=gl_get_double_by_name(soil_sensor,"humidity"); *pMonitor =*humidity; //printf("irrigaiton:%d %f \n",soil_sensor->id,*pMonitor); // system("pause"); //printf("%f %f",*pMonitor,setpoint0); //system("pause"); /* 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){ if(use_predictive_bidding == TRUE && ((control_mode == CN_RAMP && last_setpoint != setpoint0) || (control_mode == CN_DOUBLE_RAMP && (last_heating_setpoint != heating_setpoint0 || last_cooling_setpoint != cooling_setpoint0)))) { ; } else {// check to see if we have changed states if(pState == 0){ return next_run; } else if(*pState == last_pState){ return next_run; } } } else { return next_run; } } if(use_predictive_bidding == TRUE){ deadband_shift = *pDeadband * 0.5; } 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)){ //printf("EDWWWWWWWWWWWWWWWWWWW\n"); //system("pause"); 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(use_predictive_bidding == TRUE){ if((dir > 0 && clear_price < last_p) || (dir < 0 && clear_price > last_p)){ shift_direction = -1; } else if((dir > 0 && clear_price >= last_p) || (dir < 0 && clear_price <= last_p)){ shift_direction = 1; } else { shift_direction = 0; } } 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) + deadband_shift*shift_direction; } else if(clear_price > *pAvg && range_high != 0){ set_temp = setpoint0 + (clear_price - *pAvg) * fabs(range_high) / (ramp_high * *pStd) + deadband_shift*shift_direction; } else { set_temp = setpoint0 + deadband_shift*shift_direction; } 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("irrigation_controller::postsync(): temp %f given p %f vs avg %f",set_temp, market->next.price, market->avg24); } if(dir > 0){ //edw mpainei if(use_predictive_bidding == TRUE){ if(*pState == 0 && *pMonitor > (max - deadband_shift)){ bid = market->pricecap; } else if(*pState != 0 && *pMonitor < (min + deadband_shift)){ bid = 0.0; no_bid = 1; } else if(*pState != 0 && *pMonitor > max){ bid = market->pricecap; } else if(*pState == 0 && *pMonitor < min){ bid = 0.0; no_bid = 1; } } else { if(*pMonitor > max){ // printf("sto max"); bid = market->pricecap; } else if (*pMonitor < min){ // printf("sto min"); bid = -1.0; no_bid = 0; } } } else if(dir < 0){ if(use_predictive_bidding == TRUE){ if(*pState == 0 && *pMonitor < (min + deadband_shift)){ bid = market->pricecap; } else if(*pState != 0 && *pMonitor > (max - deadband_shift)){ bid = 0.0; no_bid = 1; } else if(*pState != 0 && *pMonitor < min){ bid = market->pricecap; } else if(*pState == 0 && *pMonitor > max){ bid = 0.0; no_bid = 1; } } else { if(*pMonitor < min){ bid = market->pricecap; } else if (*pMonitor > max){ bid = 0.0; no_bid = 0; } } } else if(dir == 0){ if(use_predictive_bidding == TRUE){ if(direction == 0.0) { gl_error("the variable direction did not get set correctly."); } else if((*pMonitor > max + deadband_shift || (*pState != 0 && *pMonitor > min - deadband_shift)) && direction > 0){ bid = market->pricecap; } else if((*pMonitor < min - deadband_shift || (*pState != 0 && *pMonitor < max + deadband_shift)) && direction < 0){ bid = market->pricecap; } else { bid = 0.0; no_bid = 0; } } else { if(*pMonitor < min){ bid = market->pricecap; } else if(*pMonitor > max){ bid = 0.0; no_bid = 0; } else { bid = *pAvg; } } } // calculate bid price //printf("%f,monitor:%f,min:%f max:%f, setpoint:%f\n",*humidity,*pMonitor,min,max,setpoint0); if(*pMonitor > setpoint0){ k_T = ramp_low; T_lim = range_low; bid=0; // printf("values : %f %f %f \n",bid,k_T, T_lim ); } else if(*pMonitor < setpoint0) { //printf("right_side "); k_T = ramp_low; T_lim = range_low; //printf("values : %f %f %f \n",bid,k_T, T_lim ); ///////////////////close all the controllers//////////////////////////// static FINDLIST *xt1=NULL; xt1=gl_find_objects(FL_NEW,FT_CLASS,SAME,"controller",FT_END); OBJECT *firstt1= gl_find_next(xt1,NULL); OBJECT *it1; for(it1=firstt1;it1!=NULL;it1=it1->next) { if(gl_object_isa(it1,"controller")) { gl_set_value_by_name(it1,"second_period_for_market","1") ; } } ////////////////////////////////////////////////////////////////////////////////// } else { k_T = 0.0; T_lim = 0.0; } if(bid < 0.0 && *pMonitor != setpoint0) { gl_set_value_by_name(soil_sensor,"irrigate_flag","1"); last_q = *initial_zipload_power; *pDemand =*initial_zipload_power; bid = *pAvg + ( (fabs(*pStd) < bid_offset) ? 0.0 : (*pMonitor - setpoint0) * (k_T * *pStd) / fabs(T_lim) ); //printf("price:%f %f %f %f\n",bid,(*pMonitor - setpoint0) ,(k_T * *pStd) , fabs(T_lim)); ////////////////////////////////////// char x_position_string[1024]; double *prev=gl_get_double_by_name(parent2,"prev_base_power"); double pos_x = *prev; sprintf(x_position_string, "%f", pos_x); gl_set_value_by_name(parent2,"base_power",x_position_string); ///////////////////////////////////// } else if(*pMonitor == setpoint0) { bid = *pAvg; } else { last_q=0; gl_set_value_by_name(parent2,"base_power","0"); } // 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(last_q > 0 && no_bid != 1){ last_p = bid; last_q= *initial_zipload_power; //if(last_p < 0) //{ //last_p=clear_price; //} 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.get_string()); 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("irrigation_controller:%d: residual unresponsive load is negative! (%.1f kW)", hdr->id, residual); } if (pState != 0) last_pState = *pState; char timebuf[128]; gl_printtime(t1,timebuf,127); return TS_NEVER; //} } //end of first_period==0 }
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; }