int thermal_storage::init(OBJECT *parent) { if(parent != NULL){ if((parent->flags & OF_INIT) != OF_INIT){ char objname[256]; gl_verbose("thermal_storage::init(): deferring initialization on %s", gl_name(parent, objname, 255)); return 2; // defer } } OBJECT *hdr = OBJECTHDR(this); hdr->flags |= OF_SKIPSAFE; double *design_cooling_capacity; //Make sure the parent is a house if (!(gl_object_isa(parent,"house","residential"))) { GL_THROW("thermal_storage:%s must be parented to a house!",hdr->name); /* TROUBLESHOOT The thermal_storage model is only valid for house objects. Please parent it appropriately. */ } //Pull a house link, we'll use it for addresses house_e *house_lnk = OBJECTDATA(parent,house_e); //Link the variables to the parent values (house values) design_cooling_capacity = &house_lnk->design_cooling_capacity; outside_temperature = &house_lnk->outside_temperature; thermal_storage_available = &house_lnk->thermal_storage_present; thermal_storage_active = &house_lnk->thermal_storage_inuse; //Check the cooling capacity if (*design_cooling_capacity == NULL) { gl_warning("\'design_cooling_capacity\' not specified in parent ~ default to 5 ton or 60,000 Btu/hr"); /* TROUBLESHOOT The thermal_storage did not reference a parent object that publishes design_cooling_capacity, so 5 ton was assumed. Confirm or change the parent reference and try again. */ discharge_rate = 5 * 12000; //Btu/hr, is set to 5 ton when not defined water_capacity = 1.7413; //m^3, is set to the same as a 5 ton unit } else { discharge_rate = *design_cooling_capacity; water_capacity = 1.7413 * (discharge_rate / (5 * 12000)); } surface_area = 6 * pow(water_capacity, 0.6667); //suface area of a cube calculated from volume if (total_capacity == 0) total_capacity = (30 / 5) * discharge_rate; //Btu if (state_of_charge < 0 && stored_capacity < 0) //Btu { stored_capacity = total_capacity; state_of_charge = 100; } else if (state_of_charge < 0 && stored_capacity >= 0) { state_of_charge = stored_capacity / total_capacity; } else if (state_of_charge >= 0 && stored_capacity < 0) { stored_capacity = (state_of_charge / 100) * total_capacity; } else if (state_of_charge >= 0 && stored_capacity >= 0) { stored_capacity = (state_of_charge / 100) * total_capacity; gl_warning("stored_capacity and SOC are both defined, SOC being used for initial energy state"); /* TROUBLESHOOT During the initialization of the system, a value was specified for both the stored_capacity and SOC (state of charge). The thermal energy storage object gives precedence to the SOC variable, so the initial stored_capacity will be the SOC percentage of the total_capacity. */ } if (recharge_power == 0) recharge_power = (3.360 * discharge_rate) / (5 * 12000); //kW if (discharge_power == 0) discharge_power = (0.300 * discharge_rate) / (5 * 12000); //kW if (recharge_power_factor == 0) recharge_power_factor = 0.97; //same as used for HVAC compressor in house_e if (discharge_power_factor == 0) discharge_power_factor = 1; //assume ideal pump if (k < 0) k = 0; //assume no thermal conductivity k = k * 0.00052667; //convert k from W/m/K to BTU/sec/m/degF //Determine how to read the scheduling information - charging if (recharge_schedule_type==INTERNAL) { //See if someone else has already created such a schedule recharge_schedule_vals = gl_schedule_find(thermal_default_schedule_list[1].schedule_name); //If not found, create if (recharge_schedule_vals == NULL) { //Populate schedules - charging recharge_schedule_vals = gl_schedule_create(thermal_default_schedule_list[1].schedule_name,thermal_default_schedule_list[1].schedule_definition); //Make sure it worked if (recharge_schedule_vals==NULL) { GL_THROW("Failure to create default charging schedule"); /* TROUBLESHOOT While attempting to create the default charging schedule in the thermal_storage object, an error occurred. Please try again. If the error persists, please submit your code and a bug report via the track website. */ } } gl_verbose("thermal_storage charging defaulting to internal schedule"); /* TROUBLESHOOT recharge_schedule_type was not set to EXTERNAL, so the internal schedule definition will be used for the recharging schedule. */ //Assign to the schedule value recharge_time_ptr = &recharge_schedule_vals->value; } else { //Assign the to published property recharge_time_ptr = &recharge_time; } //Determine how to read the scheduling information - discharging if (discharge_schedule_type==INTERNAL) { //See if someone else has already created such a schedule discharge_schedule_vals = gl_schedule_find(thermal_default_schedule_list[0].schedule_name); //If not found, create if (discharge_schedule_vals == NULL) { //Populate schedules - discharging discharge_schedule_vals = gl_schedule_create(thermal_default_schedule_list[0].schedule_name,thermal_default_schedule_list[0].schedule_definition); //Make sure it worked if (discharge_schedule_vals==NULL) { GL_THROW("Failure to create default discharging schedule"); /* TROUBLESHOOT While attempting to create the default discharging schedule in the thermal_storage object, an error occurred. Please try again. If the error persists, please submit your code and a bug report via the track website. */ } } gl_verbose("thermal_storage discharging defaulting to internal schedule"); /* TROUBLESHOOT discharge_schedule_type was not set to EXTERNAL, so the internal schedule definition will be used for the discharging availability schedule. */ //Assign to the schedule value discharge_time_ptr = &discharge_schedule_vals->value; } else { //Assigned to the published property discharge_time_ptr = &discharge_time; } // waiting this long to initialize the parent class is normal return residential_enduse::init(parent); }
/** initialization process **/ int irrigation_controller::init(OBJECT *parent){ OBJECT *hdr = OBJECTHDR(this); char tname[32]; parent2=parent; insync=0; initial_zipload_power=gl_get_double_by_name(parent,"base_power"); char *namestr = (hdr->name ? hdr->name : tname); sprintf(tname, "irrigation_controller:%i", hdr->id); first=0; cheat(); if(parent == NULL){ gl_error("%s: irrigation_controller has no parent, therefore nothing to control", namestr); return 0; } if(pMarket == NULL){ gl_error("%s: irrigation_controller has no market, therefore no price signals", namestr); return 0; } if(gl_object_isa(pMarket, "auction")){ gl_set_dependent(hdr, pMarket); market = OBJECTDATA(pMarket, auction); } else { gl_error("irrigation_controllers only work when attached to an 'auction' object"); return 0; } if(dPeriod == 0.0){ if((pMarket->flags & OF_INIT) != OF_INIT){ char objname[256]; gl_verbose("irrigation_controller::init(): deferring initialization on %s", gl_name(pMarket, objname, 255)); return 2; // defer } period = market->period; } else { period = (TIMESTAMP)floor(dPeriod + 0.5); } if(bid_delay < 0){ bid_delay = -bid_delay; } if(bid_delay > period){ gl_warning("Bid delay is greater than the irrigation_controller period. Resetting bid delay to 0."); bid_delay = 0; } if(target[0] == 0){ GL_THROW("irrigation_controller: %i, target property not specified", hdr->id); } if(setpoint[0] == 0 && control_mode == CN_RAMP){ GL_THROW("irrigation_controller: %i, setpoint property not specified", hdr->id);; } if(demand[0] == 0 && control_mode == CN_RAMP){ GL_THROW("irrigation_controller: %i, demand property not specified", hdr->id); } if(deadband[0] == 0 && use_predictive_bidding == TRUE && control_mode == CN_RAMP){ GL_THROW("irrigation_controller: %i, deadband property not specified", hdr->id); } if(total[0] == 0){ GL_THROW("irrigation_controller: %i, total property not specified", hdr->id); } if(load[0] == 0){ GL_THROW("irrigation_controller: %i, load property not specified", hdr->id); } fetch(&pMonitor, target, parent); // auto tha einai to soil hmidit tha to pairnei apo to soil_SENSOR if(control_mode == CN_RAMP){ fetch(&pSetpoint, setpoint, parent); fetch(&pDemand, demand, parent); fetch(&pTotal, total, parent); fetch(&pLoad, load, parent); if(use_predictive_bidding == TRUE){ fetch(&pDeadband, deadband.get_string(), parent); } } fetch(&pAvg, avg_target.get_string(), pMarket); fetch(&pStd, std_target.get_string(), pMarket); if(dir == 0){ double high = ramp_high * range_high; double low = ramp_low * range_low; //printf("high:%f, low:%f, rh:%f, rl:%f,gh:%f,gl:%f\n\n\n\n",high,low,ramp_high,ramp_low,range_high,range_low); if(high > low){ dir = 1; } else if(high < low){ dir = -1; } else if((high == low) && (fabs(ramp_high) > 0.001 || fabs(ramp_low) > 0.001)){ dir = 0; if(ramp_high > 0){ direction = 1; } else { direction = -1; } gl_warning("%s: irrigation_controller has no price ramp", namestr); /* occurs given no price variation, or no control width (use a normal thermostat?) */ } if(ramp_low * ramp_high < 0){ gl_warning("%s: irrigation_controller price curve is not injective and may behave strangely"); /* TROUBLESHOOT The price curve 'changes directions' at the setpoint, which may create odd conditions in a number of circumstances. */ } } if(setpoint0==0) setpoint0 = -1; // key to check first thing // double period = market->period; // next_run = gl_globalclock + (TIMESTAMP)(period - fmod(gl_globalclock+period,period)); next_run = gl_globalclock;// + (market->period - gl_globalclock%market->period); init_time = gl_globalclock; time_off = TS_NEVER; if(sliding_time_delay < 0 ) dtime_delay = 21600; // default sliding_time_delay of 6 hours else dtime_delay = (int64)sliding_time_delay; if(state[0] != 0){ // grab state pointer pState = gl_get_enum_by_name(parent, state); last_pState = 0; if(pState == 0){ gl_error("state property name \'%s\' is not published by parent class", state); return 0; } } // get override, if set if(re_override[0] != 0){ pOverride = gl_get_enum_by_name(parent, re_override); } if((pOverride == 0) && (use_override == OU_ON)){ gl_error("use_override is ON but no valid override property name is given"); return 0; } if(control_mode == CN_RAMP){ if(slider_setting < -0.001){ gl_warning("slider_setting is negative, reseting to 0.0"); slider_setting = 0.0; } if(slider_setting > 1.0){ gl_warning("slider_setting is greater than 1.0, reseting to 1.0"); slider_setting = 1.0; } } last_p = market->init_price; /////////////////search for virtual_battery/////////////////////// /* static FINDLIST *xt1=NULL; xt1=gl_find_objects(FL_NEW,FT_CLASS,SAME,"virtual_battery",FT_END); OBJECT *firstt1= gl_find_next(xt1,NULL); OBJECT *it1; for(it1=firstt1;it1!=NULL;it1=it1->next) { if(gl_object_isa(it1,"virtual_battery")) { virtual_battery_object=it1; } else { // virtual_battery_object=NULL; } } */ ////////////////////////////////////////////////////////////////// return 1; }
/* Checks for climate object and maps the climate variables to the windturb object variables. If no climate object is linked, then default pressure, temperature, and wind speed will be used. */ int windturb_dg::init_climate() { OBJECT *hdr = OBJECTHDR(this); // link to climate data static FINDLIST *climates = NULL; int not_found = 0; climates = gl_find_objects(FL_NEW,FT_CLASS,SAME,"climate",FT_END); if (climates==NULL) { not_found = 1; gl_warning("windturb_dg (id:%d)::init_climate(): no climate data found, using static data",hdr->id); //default to mock data static double air_dens = std_air_dens, avgWS = avg_ws, Press = std_air_press, Temp = std_air_temp; pWS = &avgWS; pPress = &Press; pTemp = &Temp; } else if (climates->hit_count>1) { gl_verbose("windturb_dg: %d climates found, using first one defined", climates->hit_count); } // } if (climates!=NULL) { if (climates->hit_count==0) { //default to mock data gl_warning("windturb_dg (id:%d)::init_climate(): no climate data found, using static data",hdr->id); static double air_dens = std_air_dens, avgWS = avg_ws, Press = std_air_press, Temp = std_air_temp; pWS = &avgWS; pPress = &Press; pTemp = &Temp; } else //climate data was found { // force rank of object w.r.t climate OBJECT *obj = gl_find_next(climates,NULL); if (obj->rank<=hdr->rank) gl_set_dependent(obj,hdr); pWS = (double*)GETADDR(obj,gl_get_property(obj,"wind_speed")); pPress = (double*)GETADDR(obj,gl_get_property(obj,"pressure")); pTemp = (double*)GETADDR(obj,gl_get_property(obj,"temperature")); //Make sure it worked if (pWS==NULL) { GL_THROW("windturb_dg (id:%d): Unable to map wind_speed from climate object",obj->id); } if (pPress==NULL) { GL_THROW("windturb_dg (id:%d): Unable to map air pressure from climate object",obj->id); } if (pTemp==NULL) { GL_THROW("windturb_dg (id:%d): Unable to map air temperature from climate object",obj->id); } } } return 1; }
int windturb_dg::init(OBJECT *parent) { OBJECT *obj = OBJECTHDR(this); double ZB, SB, EB; complex tst, tst2, tst3, tst4; switch (Turbine_Model) { case GENERIC_IND_LARGE: case GENERIC_SYNCH_LARGE: //Creates generic 1.5 MW wind turbine. blade_diam = 82.5; turbine_height = 90; q = 3; //number of gearbox stages Rated_VA = 1635000; Max_P = 1500000; Max_Q = 650000; Rated_V = 600; pf = 0.95; CP_Data = GENERAL_LARGE; cut_in_ws = 4; //lowest wind speed cut_out_ws = 25; //highest wind speed Cp_max = 0.302; //rotor specifications for power curve ws_maxcp = 7; Cp_rated = Cp_max-.05; ws_rated = 12.5; if (Turbine_Model == GENERIC_IND_LARGE) { Gen_type = INDUCTION; Rst = 0.12; Xst = 0.17; Rr = 0.12; Xr = 0.15; Rc = 999999; Xm = 9.0; } else if (Turbine_Model == GENERIC_SYNCH_LARGE) { Gen_type = SYNCHRONOUS; Rs = 0.05; Xs = 0.200; Rg = 0.000; Xg = 0.000; } break; case GENERIC_IND_MID: case GENERIC_SYNCH_MID: //Creates generic 100kW wind turbine, northwind 100 blade_diam = 23.2; //in m turbine_height = 30; //in m q = 0; //number of gearbox stages, no gear box Rated_VA = 156604; Max_P = 150000; Max_Q = 45000; Rated_V = 480; pf = 0.9; ///lag and lead of 0.9 CP_Data = GENERAL_MID; cut_in_ws = 3.5; //lowest wind speed in m/s cut_out_ws = 25; //highest wind speed in m/s Cp_max = 0.302; //rotor specifications for power curve ws_maxcp = 7; Cp_rated = Cp_max-.05; ws_rated = 14.5; // in m/s if (Turbine_Model == GENERIC_IND_MID) { // need to check the machine parameters Gen_type = INDUCTION; Rst = 0.12; Xst = 0.17; Rr = 0.12; Xr = 0.15; Rc = 999999; Xm = 9.0; } else if (Turbine_Model == GENERIC_SYNCH_MID) { Gen_type = SYNCHRONOUS; Rs = 0.05; Xs = 0.200; Rg = 0.000; Xg = 0.000; } break; case GENERIC_IND_SMALL: case GENERIC_SYNCH_SMALL: //Creates generic 5 kW wind turbine, Fortis Montana 5 kW wind turbine blade_diam = 5; // in m turbine_height = 16; //in m q = 0; //number of gearbox stages, no gear box Rated_VA = 6315; // calculate from P & Q Max_P = 5800; Max_Q = 2500; Rated_V = 600; pf = 0.95; CP_Data = GENERAL_SMALL; cut_in_ws = 2.5; //lowest wind speed cut_out_ws = 25; //highest wind speed Cp_max = 0.302; //rotor specifications for power curve ws_maxcp = 7; // | Cp_rated = Cp_max-.05; // | ws_rated = 17; // | if (Turbine_Model == GENERIC_IND_SMALL) { Gen_type = INDUCTION; Rst = 0.12; Xst = 0.17; Rr = 0.12; Xr = 0.15; Rc = 999999; Xm = 9.0; } else if (Turbine_Model == GENERIC_SYNCH_SMALL) { Gen_type = SYNCHRONOUS; Rs = 0.05; Xs = 0.200; Rg = 0.000; Xg = 0.000; } break; case VESTAS_V82: //Include manufacturer's data - cases can be added to call other wind turbines turbine_height = 78; blade_diam = 82; Rated_VA = 1808000; Rated_V = 600; Max_P = 1650000; Max_Q = 740000; pf = 0.91; //Can range between 0.65-1.00 depending on controllers and Pout. CP_Data = MANUF_TABLE; cut_in_ws = 3.5; cut_out_ws = 20; q = 2; Gen_type = SYNCHRONOUS; //V82 actually uses a DFIG, but will use synch representation for now Rs = 0.025; //Estimated values for synch representation. Xs = 0.200; Rg = 0.000; Xg = 0.000; break; case GE_25MW: turbine_height = 100; blade_diam = 100; Rated_VA = 2727000; Rated_V = 690; Max_P = 2500000; Max_Q = 1090000; pf = 0.95; //ranges between -0.9 -> 0.9; q = 3; CP_Data = GENERAL_LARGE; cut_in_ws = 3.5; cut_out_ws = 25; Cp_max = 0.28; Cp_rated = 0.275; ws_maxcp = 8.2; ws_rated = 12.5; Gen_type = SYNCHRONOUS; Rs = 0.035; Xs = 0.200; Rg = 0.000; Xg = 0.000; break; case BERGEY_10kW: turbine_height = 24; blade_diam = 7; Rated_VA = 10000; Rated_V = 360; Max_P = 15000; Max_Q = 4000; pf = 0.95; //ranges between -0.9 -> 0.9; q = 0; CP_Data = GENERAL_SMALL; cut_in_ws = 2; cut_out_ws = 20; Cp_max = 0.28; Cp_rated = 0.275; ws_maxcp = 8.2; ws_rated = 12.5; Gen_type = SYNCHRONOUS; Rs = 0.05; Xs = 0.200; Rg = 0.000; Xg = 0.000; break; case USER_DEFINED: CP_Data = USER_SPECIFY; Gen_type = USER_TYPE; Rs = 0.2; Xs = 0.2; Rg = 0.1; Xg = 0; if (turbine_height <=0) GL_THROW ("turbine height cannot have a negative or zero value."); /* TROUBLESHOOT Turbine height must be specified as a value greater than or equal to zero. */ if (blade_diam <=0) GL_THROW ("blade diameter cannot have a negative or zero value."); /* TROUBLESHOOT Blade diameter must be specified as a value greater than or equal to zero. */ if (cut_in_ws <=0) GL_THROW ("cut in wind speed cannot have a negative or zero value."); /* TROUBLESHOOT Cut in wind speed must be specified as a value greater than or equal to zero. */ if (cut_out_ws <=0) GL_THROW ("cut out wind speed cannot have a negative or zero value."); /* TROUBLESHOOT Cut out wind speed must be specified as a value greater than or equal to zero. */ if (ws_rated <=0) GL_THROW ("rated wind speed cannot have a negative or zero value."); /* TROUBLESHOOT Rated wind speed must be specified as a value greater than or equal to zero. */ if (ws_maxcp <=0) GL_THROW ("max cp cannot have a negative or zero value."); /* TROUBLESHOOT Maximum coefficient of performance must be specified as a value greater than or equal to zero. */ break; default: GL_THROW("Unknown turbine model was specified"); /* TROUBLESHOOT An unknown wind turbine model was selected. Please select a Turbine_Model from the available list. */ } // construct circuit variable map to meter -- copied from 'House' module struct { complex **var; char *varname; } map[] = { // local object name, meter object name {&pCircuit_V, "voltage_A"}, // assumes 2 and 3 follow immediately in memory {&pLine_I, "current_A"}, // assumes 2 and 3(N) follow immediately in memory /// @todo use triplex property mapping instead of assuming memory order for meter variables (residential, low priority) (ticket #139) }; static complex default_line123_voltage[3], default_line1_current[3]; int i; //Map phases set *phaseInfo; PROPERTY *tempProp; tempProp = gl_get_property(parent,"phases"); if ((tempProp==NULL || tempProp->ptype!=PT_set)) { GL_THROW("Unable to map phases property - ensure the parent is a powerflow:meter"); /* TROUBLESHOOT While attempting to map the phases property from the parent object, an error was encountered. Please check and make sure your parent object is a meter inside the powerflow module and try again. If the error persists, please submit your code and a bug report via the Trac website. */ } else phaseInfo = (set*)GETADDR(parent,tempProp); int temp_phases=0; // Currently only supports 3-phase connection, so check number of phases of parent if ((*phaseInfo & PHASE_A) == PHASE_A) temp_phases += 1; if ((*phaseInfo & PHASE_B) == PHASE_B) temp_phases += 1; if ((*phaseInfo & PHASE_C) == PHASE_C) temp_phases += 1; if (temp_phases < 3) GL_THROW("The wind turbine model currently only supports a 3-phase connection, please check meter connection"); /* TROUBLESHOOT Currently the wind turbine model only supports 3-phase connnections. Please attach to 3-phase meter. */ // find parent meter, if not defined, use a default meter (using static variable 'default_meter') if (parent!=NULL) { if((parent->flags & OF_INIT) != OF_INIT){ char objname[256]; gl_verbose("windturb_dg::init(): deferring initialization on %s", gl_name(parent, objname, 255)); return 2; // defer } if (gl_object_isa(parent,"meter","powerflow")) //Attach to meter { /* NR_mode = get_bool(parent,"NR_mode"); //Check NR_mode, just to be consistent if (NR_mode == NULL) { GL_THROW("Wind turbine failed to map NR_mode property"); */ /* TROUBLESHOOT While attempting to map up the NR_mode property, an error was encountered. Please try again. If the error persists, please submit your code and a bug report via the trac website. */ /* } */ //Map the voltages double *parNominalVoltage; tempProp = gl_get_property(parent,"nominal_voltage"); if ((tempProp==NULL || tempProp->ptype!=PT_double)) { GL_THROW("Unable to map nominal_voltage property - ensure the parent is a powerflow:meter"); /* TROUBLESHOOT While attempting to map the nominal_voltage property from the parent object, an error was encountered. Please check and make sure your parent object is a meter inside the powerflow module and try again. If the error persists, please submit your code and a bug report via the Trac website. */ } else parNominalVoltage = (double*)GETADDR(parent,tempProp); // check nominal voltage against rated voltage if ( fabs(1 - (*parNominalVoltage * sqrt(3.0) / Rated_V) ) > 0.1 ) gl_warning("windturb_dg (id:%d, name:%s): Rated generator voltage (LL: %.1f) and nominal voltage (LL: %.1f) of meter parent are different by greater than 10 percent. Odd behavior may occur.",obj->id,obj->name,Rated_V,*parNominalVoltage * sqrt(3.0)); /* TROUBLESHOOT Currently, the model allows you to attach the turbine to a voltage that is quite different than the rated terminal voltage of the generator. However, this may cause odd behavior, as the solved powerflow voltage is used to calculate the generator induced voltages and conversion from mechanical power. It is recommended that the nominal voltages of the parent meter be within ~10% of the rated voltage. */ // attach meter variables to each circuit for (i=0; i<sizeof(map)/sizeof(map[0]); i++) { *(map[i].var) = get_complex(parent,map[i].varname); if (*(map[i].var) == NULL) { GL_THROW("Unable to map variable %s",map[i].varname); /* TROUBLESHOOT The variable name was not found when mapping it */ } } } else if (gl_object_isa(parent,"triplex_meter","powerflow")) { GL_THROW("The wind turbine model does currently support direct connection to single phase or triplex meters. Connect through a rectifier-inverter combination."); /* TROUBLESHOOT This model does not currently support connection to a triplex system. Please connect to a 3-phase meter. */ //Map voltage pCircuit_V = get_complex(parent,"voltage_1"); //Make sure it worked if (pCircuit_V == NULL) GL_THROW("Unable to map triplex_meter voltage"); //Map current pLine_I = get_complex(parent,"current_1"); //Make sure it worked if (pLine_I == NULL) GL_THROW("Unable to map triplex_meter current"); //NR_mode = get_bool(parent,"NR_mode"); } else if (gl_object_isa(parent,"rectifier","generators")) { //Map the voltages double *parNominalVoltage; tempProp = gl_get_property(parent,"V_Rated"); if ((tempProp==NULL || tempProp->ptype!=PT_double)) { GL_THROW("Unable to map V_Rated property - ensure the parent is a powerflow:meter"); /* TROUBLESHOOT While attempting to map the nominal_voltage property from the parent object, an error was encountered. Please check and make sure your parent object is a meter inside the powerflow module and try again. If the error persists, please submit your code and a bug report via the Trac website. */ } else parNominalVoltage = (double*)GETADDR(parent,tempProp); // check nominal voltage against rated voltage if ( fabs(1 - (*parNominalVoltage / Rated_V) ) > 0.1 ) gl_warning("windturb_dg (id:%d, name:%s): Rated generator voltage (LL: %.1f) and nominal voltage (LL: %.1f) of meter parent are different by greater than 10 percent. Odd behavior may occur.",obj->id,obj->name,Rated_V,*parNominalVoltage * sqrt(3.0)); /* TROUBLESHOOT Currently, the model allows you to attach the turbine to a voltage that is quite different than the rated terminal voltage of the generator. However, this may cause odd behavior, as the solved powerflow voltage is used to calculate the generator induced voltages and conversion from mechanical power. It is recommended that the nominal voltages of the parent meter be within ~10% of the rated voltage. */ // attach meter variables to each circuit for (i=0; i<sizeof(map)/sizeof(map[0]); i++) { if ((*(map[i].var) = get_complex(parent,map[i].varname))==NULL) { GL_THROW("%s (%s:%d) does not implement rectifier variable %s for %s (windturb_dg:%d)", /* TROUBLESHOOT The rectifier requires that the inverter contains certain published properties in order to properly connect. If you encounter this error, please report it to the developers, along with the version of GridLAB-D that raised this error. */ parent->name?parent->name:"unnamed object", parent->oclass->name, parent->id, map[i].varname, obj->name?obj->name:"unnamed", obj->id); } } } else { GL_THROW("windturb_dg (id:%d): Invalid parent object",obj->id); /* TROUBLESHOOT The wind turbine object must be attached a 3-phase meter object. Please check parent of object. */ } } else { gl_warning("windturb_dg:%d %s", obj->id, parent==NULL?"has no parent meter defined":"parent is not a meter"); // attach meter variables to each circuit in the default_meter *(map[0].var) = &default_line123_voltage[0]; *(map[1].var) = &default_line1_current[0]; // provide initial values for voltages default_line123_voltage[0] = complex(Rated_V/sqrt(3.0),0); default_line123_voltage[1] = complex(Rated_V/sqrt(3.0)*cos(2*PI/3),Rated_V/sqrt(3.0)*sin(2*PI/3)); default_line123_voltage[2] = complex(Rated_V/sqrt(3.0)*cos(-2*PI/3),Rated_V/sqrt(3.0)*sin(-2*PI/3)); NR_mode = &default_NR_mode; } if (Gen_status==OFFLINE) { gl_warning("init_windturb_dg (id:%d,name:%s): Generator is out of service!", obj->id,obj->name); } if (Gen_type == SYNCHRONOUS || Gen_type == INDUCTION) { if (Gen_mode == CONSTANTE) { gl_warning("init_windturb_dg (id:%d,name:%s): Synchronous and induction generators in constant voltage mode has not been fully tested and my not work properly.", obj->id,obj->name); } } if (Rated_VA!=0.0) SB = Rated_VA/3; if (Rated_V!=0.0) EB = Rated_V/sqrt(3.0); if (SB!=0.0) ZB = EB*EB/SB; else GL_THROW("Generator power capacity not specified!"); /* TROUBLESHOOT Rated_VA of generator must be specified so that per unit values can be calculated */ if (Gen_type == INDUCTION) { complex Zrotor(Rr,Xr); complex Zmag = complex(Rc*Xm*Xm/(Rc*Rc + Xm*Xm),Rc*Rc*Xm/(Rc*Rc + Xm*Xm)); complex Zstator(Rst,Xst); //Induction machine two-port matrix. IndTPMat[0][0] = (Zmag + Zstator)/Zmag; IndTPMat[0][1] = Zrotor + Zstator + Zrotor*Zstator/Zmag; IndTPMat[1][0] = complex(1,0) / Zmag; IndTPMat[1][1] = (Zmag + Zrotor) / Zmag; } else if (Gen_type == SYNCHRONOUS) { double Real_Rs = Rs * ZB; double Real_Xs = Xs * ZB; double Real_Rg = Rg * ZB; double Real_Xg = Xg * ZB; tst = complex(Real_Rg,Real_Xg); tst2 = complex(Real_Rs,Real_Xs); AMx[0][0] = tst2 + tst; //Impedance matrix AMx[1][1] = tst2 + tst; AMx[2][2] = tst2 + tst; AMx[0][1] = AMx[0][2] = AMx[1][0] = AMx[1][2] = AMx[2][0] = AMx[2][1] = tst; tst3 = (complex(1,0) + complex(2,0)*tst/tst2)/(tst2 + complex(3,0)*tst); tst4 = (-tst/tst2)/(tst2 + tst); invAMx[0][0] = tst3; //Admittance matrix (inverse of Impedance matrix) invAMx[1][1] = tst3; invAMx[2][2] = tst3; invAMx[0][1] = AMx[0][2] = AMx[1][0] = AMx[1][2] = AMx[2][0] = AMx[2][1] = tst4; } else GL_THROW("Unknown generator type specified"); /* TROUBLESHOOT Shouldn't have been able to specify an unknown generator type. Please report this error to GridLAB-D support. */ init_climate(); return 1; }
TIMESTAMP fuse::sync(TIMESTAMP t0) { OBJECT *obj = OBJECTHDR(this); unsigned char work_phases; bool fuse_blew; TIMESTAMP replacement_time; TIMESTAMP replacement_duration; TIMESTAMP t2; char8 fault_val; int result_val; //Try to map the event_schedule function address, if we haven't tried yet if (event_schedule_map_attempt == false) { //First check to see if a fault_check object even exists if (fault_check_object != NULL) { //It exists, good start! - now see if the proper variable is populated! eventgen_obj = get_object(fault_check_object, "eventgen_object"); //See if it worked - if not, assume it doesn't exist if (*eventgen_obj != NULL) { //It's not null, map up the scheduler function event_schedule = (FUNCTIONADDR)(gl_get_function(*eventgen_obj,"add_event")); //Make sure it was found if (event_schedule == NULL) { gl_warning("Unable to map add_event function in eventgen:%s",*(*eventgen_obj)->name); /* TROUBLESHOOT While attempting to map the "add_event" function from an eventgen object, the function failed to be found. Ensure the target object in fault_check is an eventgen object and this function exists. If the error persists, please submit your code and a bug report via the trac website. */ } } //Defaulted elses - just leave things as is :( } //Defaulted else - doesn't exist, so leave function address empty //Flag the attempt as having occurred event_schedule_map_attempt = true; } //Update time variable if (prev_fuse_time != t0) //New timestep prev_fuse_time = t0; //Code below only applies to NR right now - FBS legacy code has no sync values //May need to be appropriately adjusted once FBS supports reliability if (solver_method == SM_NR) { //Put any fuses back in service, if they're ready if (((fix_time[0] <= t0) || (fix_time[1] <= t0) || (fix_time[2] <= t0)) && (event_schedule == NULL)) //Only needs to be done if reliability isn't present { //Bring the phases back that are necessary if ((fix_time[0] <= t0) && ((NR_branchdata[NR_branch_reference].origphases & 0x04) == 0x04)) //Phase A ready and had a phase A { //Update status phase_A_state = GOOD; //Pop in the variables for the reliability update (if it exists) fix_time[0] = TS_NEVER; //Reset variables } if ((fix_time[1] <= t0) && ((NR_branchdata[NR_branch_reference].origphases & 0x02) == 0x02)) //Phase B ready and had a phase B { //Update status phase_B_state = GOOD; //Pop in the variables for the reliability update (if it exists) fix_time[1] = TS_NEVER; //Reset variables } if ((fix_time[2] <= t0) && ((NR_branchdata[NR_branch_reference].origphases & 0x01) == 0x01)) //Phase C ready and had a phase C { //Update status phase_C_state = GOOD; //Pop in the variables for the reliability update (if it exists) fix_time[2] = TS_NEVER; //Reset variables } }//End back in service //Call syncing function fuse_sync_function(); //Call overlying link sync t2=link::sync(t0); //See if we're in the proper cycle - NR only for now if ((NR_cycle == true) && (solver_method == SM_NR)) { //Start with no assumed outages fuse_blew = false; work_phases = 0x00; replacement_time = 0; //Check them if ((NR_branchdata[NR_branch_reference].phases & 0x04) == 0x04) //Phase A valid - check it { //Link::sync is where current in is calculated. Convert the values current_current_values[0] = current_in[0].Mag(); if ((current_current_values[0] > current_limit) && (phase_A_state == GOOD)) { phase_A_state = BLOWN; //Blow the fuse gl_warning("Phase A of fuse:%s just blew!",obj->name); /* TROUBLESHOOT The current through phase A of the fuse just exceeded the maximum rated value. Use a larger value, or otherwise change your system and try again. */ fuse_blew = true; //Flag a change work_phases |= 0x04; //Flag A change //See if an update is needed (it's A and first, so yes, but just to be generic) if (replacement_time == 0) { //Get length of outage if (restore_dist_type == EXPONENTIAL) { //Update mean repair time mean_repair_time = gl_random_exponential(1.0/mean_replacement_time); replacement_duration = (TIMESTAMP)(mean_repair_time); } else { //Update mean repair time - fuse always overrides link mean_repair_time = mean_replacement_time; replacement_duration = (TIMESTAMP)(mean_repair_time); } //Figure out when it is replacement_time = prev_fuse_time + replacement_duration; } } //Else is leave as is - either blown, or reliability hit it } if ((NR_branchdata[NR_branch_reference].phases & 0x02) == 0x02) //Phase B valid - check it { //Link::sync is where current in is calculated. Convert the values current_current_values[1] = current_in[1].Mag(); if ((current_current_values[1] > current_limit) && (phase_B_state == GOOD)) { phase_B_state = BLOWN; //Blow the fuse gl_warning("Phase B of fuse:%s just blew!",obj->name); /* TROUBLESHOOT The current through phase B of the fuse just exceeded the maximum rated value. Use a larger value, or otherwise change your system and try again. */ fuse_blew = true; //Flag a change work_phases |= 0x02; //Flag B change //See if an update is needed if (replacement_time == 0) { //Get length of outage if (restore_dist_type == EXPONENTIAL) { //Update mean repair time mean_repair_time = gl_random_exponential(1.0/mean_replacement_time); replacement_duration = (TIMESTAMP)(mean_repair_time); } else { //Update mean repair time - fuse always overrides link mean_repair_time = mean_replacement_time; replacement_duration = (TIMESTAMP)(mean_repair_time); } //Figure out when it is replacement_time = prev_fuse_time + replacement_duration; } } //Else is leave as is - either blown, or reliability hit it } if ((NR_branchdata[NR_branch_reference].phases & 0x01) == 0x01) //Phase C valid - check it { //Link::sync is where current in is calculated. Convert the values current_current_values[2] = current_in[2].Mag(); if ((current_current_values[2] > current_limit) && (phase_C_state == GOOD)) { phase_C_state = BLOWN; //Blow the fuse gl_warning("Phase C of fuse:%s just blew!",obj->name); /* TROUBLESHOOT The current through phase C of the fuse just exceeded the maximum rated value. Use a larger value, or otherwise change your system and try again. */ fuse_blew = true; //Flag a change work_phases |= 0x01; //Flag C change //See if an update is needed if (replacement_time == 0) { //Get length of outage if (restore_dist_type == EXPONENTIAL) { //Update mean repair time mean_repair_time = gl_random_exponential(1.0/mean_replacement_time); replacement_duration = (TIMESTAMP)(mean_repair_time); } else { //Update mean repair time - fuse always overrides link mean_repair_time = mean_replacement_time; replacement_duration = (TIMESTAMP)(mean_repair_time); } //Figure out when it is replacement_time = prev_fuse_time + replacement_duration; } } //Else is leave as is - either blown, or reliability hit it } if (fuse_blew == true) { //Set up fault type fault_val[0] = 'F'; fault_val[1] = 'U'; fault_val[2] = 'S'; fault_val[3] = '-'; //Determine who blew and store the time (assumes fuses can be replaced in parallel) switch (work_phases) { case 0x00: //No fuses blown !?? GL_THROW("fuse:%s supposedly blew, but doesn't register the right phases",obj->name); /* TROUBLESHOOT A fuse reported an over-current condition and blew the appropriate link. However, it did not appear to fully propogate this condition. Please try again. If the error persists, please submit your code and a bug report via the trac website. */ break; case 0x01: //Phase C blew fix_time[2] = replacement_time; fault_val[4] = 'C'; fault_val[5] = '\0'; break; case 0x02: //Phase B blew fix_time[1] = replacement_time; fault_val[4] = 'B'; fault_val[5] = '\0'; break; case 0x03: //Phase B and C blew fix_time[1] = replacement_time; fix_time[2] = replacement_time; fault_val[4] = 'B'; fault_val[5] = 'C'; fault_val[6] = '\0'; break; case 0x04: //Phase A blew fix_time[0] = replacement_time; fault_val[4] = 'A'; fault_val[5] = '\0'; break; case 0x05: //Phase A and C blew fix_time[0] = replacement_time; fix_time[2] = replacement_time; fault_val[4] = 'A'; fault_val[5] = 'C'; fault_val[6] = '\0'; break; case 0x06: //Phase A and B blew fix_time[0] = replacement_time; fix_time[1] = replacement_time; fault_val[4] = 'A'; fault_val[5] = 'B'; fault_val[6] = '\0'; break; case 0x07: //All three went fix_time[0] = replacement_time; fix_time[1] = replacement_time; fix_time[2] = replacement_time; fault_val[4] = 'A'; fault_val[5] = 'B'; fault_val[6] = 'C'; fault_val[7] = '\0'; break; default: GL_THROW("fuse:%s supposedly blew, but doesn't register the right phases",obj->name); //Defined above }//End switch if (event_schedule != NULL) //Function was mapped - go for it! { //Call the function result_val = ((int (*)(OBJECT *, OBJECT *, char *, TIMESTAMP, TIMESTAMP, int, bool))(*event_schedule))(*eventgen_obj,obj,fault_val,t0,replacement_duration,-1,false); //Make sure it worked if (result_val != 1) { GL_THROW("Attempt to blow fuse:%s failed in a reliability manner",obj->name); /* TROUBLESHOOT While attempting to propagate a blown fuse's impacts, an error was encountered. Please try again. If the error persists, please submit your code and a bug report via the trac website. */ } //Ensure we don't go anywhere yet t2 = t0; } //End fault object present else //No object, just fail us out - save the iterations { gl_warning("No fault_check object present - Newton-Raphson solver may fail!"); /* TROUBLESHOOT A fuse blew and created an open link. If the system is not meshed, the Newton-Raphson solver will likely fail. Theoretically, this should be a quick fail due to a singular matrix. However, the system occasionally gets stuck and will exhaust iteration cycles before continuing. If the fuse is blowing and the NR solver still iterates for a long time, this may be the case. */ } } }//End NR call }//End NR-only reliability calls else //FBS t2 = link::sync(t0); if (t2==TS_NEVER) return(t2); else return(-t2); //Soft limit it }
climate::climate(MODULE *module) { memset(this, 0, sizeof(climate)); if (oclass==NULL) { oclass = gl_register_class(module,"climate",sizeof(climate),PC_PRETOPDOWN); if (gl_publish_variable(oclass, PT_double,"solar_elevation",PADDR(solar_elevation), //sjin: publish solar elevation variable PT_double,"solar_azimuth",PADDR(solar_azimuth), //sjin: publish solar azimuth variable PT_char32, "city", PADDR(city), PT_char1024,"tmyfile",PADDR(tmyfile), PT_double,"temperature[degF]",PADDR(temperature), PT_double,"humidity[%]",PADDR(humidity), PT_double,"solar_flux[W/sf]",PADDR(solar_flux), PT_SIZE, 9, PT_double,"solar_direct[W/sf]",PADDR(solar_direct), PT_double,"solar_diffuse[W/sf]",PADDR(solar_diffuse), PT_double,"solar_global[W/sf]",PADDR(solar_global), PT_double,"extraterrestrial_direct_normal[W/sf]",PADDR(direct_normal_extra), PT_double,"pressure[mbar]",PADDR(pressure), PT_double,"wind_speed[mph]", PADDR(wind_speed), PT_double,"wind_dir[deg]", PADDR(wind_dir), PT_double,"wind_gust[mph]", PADDR(wind_gust), PT_double,"record.low[degF]", PADDR(record.low), PT_int32,"record.low_day",PADDR(record.low_day), PT_double,"record.high[degF]", PADDR(record.high), PT_int32,"record.high_day",PADDR(record.high_day), PT_double,"record.solar[W/sf]", PADDR(record.solar), PT_double,"rainfall[in/h]",PADDR(rainfall), PT_double,"snowdepth[in]",PADDR(snowdepth), PT_enumeration,"interpolate",PADDR(interpolate),PT_DESCRIPTION,"the interpolation mode used on the climate data", PT_KEYWORD,"NONE",(enumeration)CI_NONE, PT_KEYWORD,"LINEAR",(enumeration)CI_LINEAR, PT_KEYWORD,"QUADRATIC",(enumeration)CI_QUADRATIC, PT_double,"solar_horiz",PADDR(solar_flux[CP_H]), PT_double,"solar_north",PADDR(solar_flux[CP_N]), PT_double,"solar_northeast",PADDR(solar_flux[CP_NE]), PT_double,"solar_east",PADDR(solar_flux[CP_E]), PT_double,"solar_southeast",PADDR(solar_flux[CP_SE]), PT_double,"solar_south",PADDR(solar_flux[CP_S]), PT_double,"solar_southwest",PADDR(solar_flux[CP_SW]), PT_double,"solar_west",PADDR(solar_flux[CP_W]), PT_double,"solar_northwest",PADDR(solar_flux[CP_NW]), PT_double,"solar_raw[W/sf]",PADDR(solar_raw), PT_double,"ground_reflectivity[pu]",PADDR(ground_reflectivity), PT_object,"reader",PADDR(reader), PT_char1024,"forecast",PADDR(forecast),PT_DESCRIPTION,"forecasting specifications", NULL)<1) GL_THROW("unable to publish properties in %s",__FILE__); memset(this,0,sizeof(climate)); strcpy(city,""); strcpy(tmyfile,""); temperature = 59.0; temperature_raw = 15.0; humidity = 75.0; rainfall = 0.0; snowdepth = 0.0; ground_reflectivity = 0.3; direct_normal_extra = 126.998456; //1367 W/m^2 constant in W/ft^2 pressure = 1000; //Sea level assumption //solar_flux = malloc(8 * sizeof(double)); solar_flux[0] = solar_flux[1] = solar_flux[2] = solar_flux[3] = solar_flux[4] = solar_flux[5] = solar_flux[6] = solar_flux[7] = solar_flux[8] = 0.0; // W/sf normal //solar_flux_S = solar_flux_SE = solar_flux_SW = solar_flux_E = solar_flux_W = solar_flux_NE = solar_flux_NW = solar_flux_N = 0.0; // W/sf normal tmy = NULL; sa = new SolarAngles(); defaults = this; gl_publish_function(oclass, "calculate_solar_radiation_degrees", (FUNCTIONADDR)calculate_solar_radiation_degrees); gl_publish_function(oclass, "calculate_solar_radiation_radians", (FUNCTIONADDR)calculate_solar_radiation_radians); gl_publish_function(oclass, "calculate_solar_radiation_shading_degrees", (FUNCTIONADDR)calculate_solar_radiation_shading_degrees); gl_publish_function(oclass, "calculate_solar_radiation_shading_radians", (FUNCTIONADDR)calculate_solar_radiation_shading_radians); //sjin: publish solar elevation and azimuth functions gl_publish_function(oclass, "calculate_solar_elevation", (FUNCTIONADDR)calculate_solar_elevation); gl_publish_function(oclass, "calculate_solar_azimuth", (FUNCTIONADDR)calculate_solar_azimuth); //New solar position algorithm stuff gl_publish_function(oclass, "calc_solpos_radiation_shading_degrees", (FUNCTIONADDR)calc_solar_solpos_shading_deg); gl_publish_function(oclass, "calc_solpos_radiation_shading_radians", (FUNCTIONADDR)calc_solar_solpos_shading_rad); } }
// the constructor registers the class and properties and sets the defaults meter::meter(MODULE *mod) : node(mod) { // first time init if (oclass==NULL) { // link to parent class (used by isa) pclass = node::oclass; // register the class definition oclass = gl_register_class(mod,"meter",sizeof(meter),PC_PRETOPDOWN|PC_BOTTOMUP|PC_POSTTOPDOWN|PC_UNSAFE_OVERRIDE_OMIT); if (oclass==NULL) GL_THROW("unable to register object class implemented by %s",__FILE__); // publish the class properties if (gl_publish_variable(oclass, PT_INHERIT, "node", PT_double, "measured_real_energy[Wh]", PADDR(measured_real_energy), PT_double, "measured_reactive_energy[VAh]",PADDR(measured_reactive_energy), PT_complex, "measured_power[VA]", PADDR(measured_power), PT_complex, "measured_power_A[VA]", PADDR(indiv_measured_power[0]), PT_complex, "measured_power_B[VA]", PADDR(indiv_measured_power[1]), PT_complex, "measured_power_C[VA]", PADDR(indiv_measured_power[2]), PT_double, "measured_demand[W]", PADDR(measured_demand), PT_double, "measured_real_power[W]", PADDR(measured_real_power), PT_double, "measured_reactive_power[VAr]", PADDR(measured_reactive_power), PT_complex, "meter_power_consumption[VA]", PADDR(meter_power_consumption), //Luca Meschiari - Start PT_double,"proj_power",PADDR(proj_power), PT_double, "connection_ratio", PADDR(connection_ratio), //Luca Meschiari - End // added to record last voltage/current PT_complex, "measured_voltage_A[V]", PADDR(measured_voltage[0]), PT_complex, "measured_voltage_B[V]", PADDR(measured_voltage[1]), PT_complex, "measured_voltage_C[V]", PADDR(measured_voltage[2]), PT_complex, "measured_voltage_AB[V]", PADDR(measured_voltageD[0]), PT_complex, "measured_voltage_BC[V]", PADDR(measured_voltageD[1]), PT_complex, "measured_voltage_CA[V]", PADDR(measured_voltageD[2]), PT_complex, "measured_current_A[A]", PADDR(measured_current[0]), PT_complex, "measured_current_B[A]", PADDR(measured_current[1]), PT_complex, "measured_current_C[A]", PADDR(measured_current[2]), PT_bool, "customer_interrupted", PADDR(meter_interrupted), PT_bool, "customer_interrupted_secondary", PADDR(meter_interrupted_secondary), #ifdef SUPPORT_OUTAGES PT_int16, "sustained_count", PADDR(sustained_count), //reliability sustained event counter PT_int16, "momentary_count", PADDR(momentary_count), //reliability momentary event counter PT_int16, "total_count", PADDR(total_count), //reliability total event counter PT_int16, "s_flag", PADDR(s_flag), PT_int16, "t_flag", PADDR(t_flag), PT_complex, "pre_load", PADDR(pre_load), #endif PT_double, "monthly_bill[$]", PADDR(monthly_bill), PT_double, "previous_monthly_bill[$]", PADDR(previous_monthly_bill), PT_double, "previous_monthly_energy[kWh]", PADDR(previous_monthly_energy), PT_double, "monthly_fee[$]", PADDR(monthly_fee), PT_double, "monthly_energy[kWh]", PADDR(monthly_energy), PT_enumeration, "bill_mode", PADDR(bill_mode), PT_KEYWORD,"NONE",BM_NONE, PT_KEYWORD,"UNIFORM",BM_UNIFORM, PT_KEYWORD,"TIERED",BM_TIERED, PT_KEYWORD,"HOURLY",BM_HOURLY, PT_KEYWORD,"TIERED_RTP",BM_TIERED_RTP, PT_object, "power_market", PADDR(power_market), PT_int32, "bill_day", PADDR(bill_day), PT_double, "price[$/kWh]", PADDR(price), PT_double, "price_base[$/kWh]", PADDR(price_base), PT_DESCRIPTION, "Used only in TIERED_RTP mode to describe the price before the first tier", PT_double, "first_tier_price[$/kWh]", PADDR(tier_price[0]), PT_double, "first_tier_energy[kWh]", PADDR(tier_energy[0]), PT_double, "second_tier_price[$/kWh]", PADDR(tier_price[1]), PT_double, "second_tier_energy[kWh]", PADDR(tier_energy[1]), PT_double, "third_tier_price[$/kWh]", PADDR(tier_price[2]), PT_double, "third_tier_energy[kWh]", PADDR(tier_energy[2]), //PT_double, "measured_reactive[kVar]", PADDR(measured_reactive), has not implemented yet NULL)<1) GL_THROW("unable to publish properties in %s",__FILE__); // publish meter reset function if (gl_publish_function(oclass,"reset",(FUNCTIONADDR)meter_reset)==NULL) GL_THROW("unable to publish meter_reset function in %s",__FILE__); } }
//Functionalized switch sync call -- after link call -- for deltamode functionality void switch_object::NR_switch_sync_post(unsigned char *work_phases_pre, unsigned char *work_phases_post, OBJECT *obj, TIMESTAMP *t0, TIMESTAMP *t2) { unsigned char work_phases, work_phases_closed; //work_phases_pre, work_phases_post, work_phases_closed; char fault_val[9]; int result_val, impl_fault; bool fault_mode; TIMESTAMP temp_time; //See if we're in the proper cycle - NR only for now if (*work_phases_pre != *work_phases_post) { //Find out what changed work_phases = (*work_phases_pre ^ *work_phases_post) & 0x07; //See if this transition is a "fault-open" or a "fault-close" work_phases_closed = work_phases & *work_phases_post; //See how it looks if (work_phases_closed == work_phases) //It's a close { fault_mode = true; //work_phases = (~work_phases) & 0x07; } else //It's an open { fault_mode = false; //Work phases is already in the proper format } //Set up fault type fault_val[0] = 'S'; fault_val[1] = 'W'; fault_val[2] = '-'; //Default fault - none - will cause a failure if not caught impl_fault = -1; //Determine who opened and store the time switch (work_phases) { case 0x00: //No switches opened !?? GL_THROW("switch:%s supposedly opened, but doesn't register the right phases",obj->name); /* TROUBLESHOOT A switch reported changing to an open status. However, it did not appear to fully propogate this condition. Please try again. If the error persists, please submit your code and a bug report via the trac website. */ break; case 0x01: //Phase C action fault_val[3] = 'C'; fault_val[4] = '\0'; impl_fault = 20; break; case 0x02: //Phase B action fault_val[3] = 'B'; fault_val[4] = '\0'; impl_fault = 19; break; case 0x03: //Phase B and C action fault_val[3] = 'B'; fault_val[4] = 'C'; fault_val[5] = '\0'; impl_fault = 22; break; case 0x04: //Phase A action fault_val[3] = 'A'; fault_val[4] = '\0'; impl_fault = 18; break; case 0x05: //Phase A and C action fault_val[3] = 'A'; fault_val[4] = 'C'; fault_val[5] = '\0'; impl_fault = 23; break; case 0x06: //Phase A and B action fault_val[3] = 'A'; fault_val[4] = 'B'; fault_val[5] = '\0'; impl_fault = 21; break; case 0x07: //All three went fault_val[3] = 'A'; fault_val[4] = 'B'; fault_val[5] = 'C'; fault_val[6] = '\0'; impl_fault = 24; break; default: GL_THROW("switch:%s supposedly opened, but doesn't register the right phases",obj->name); //Defined above }//End switch if (event_schedule != NULL) //Function was mapped - go for it! { //Call the function if (fault_mode == true) //Restoration - make fail time in the past { if (mean_repair_time != 0) temp_time = 50 + (TIMESTAMP)(mean_repair_time); else temp_time = 50; //Call function result_val = ((int (*)(OBJECT *, OBJECT *, char *, TIMESTAMP, TIMESTAMP, int, bool))(*event_schedule))(*eventgen_obj,obj,fault_val,(*t0-50),temp_time,impl_fault,fault_mode); } else //Failing - normal { result_val = ((int (*)(OBJECT *, OBJECT *, char *, TIMESTAMP, TIMESTAMP, int, bool))(*event_schedule))(*eventgen_obj,obj,fault_val,*t0,TS_NEVER,-1,fault_mode); } //Make sure it worked if (result_val != 1) { GL_THROW("Attempt to change switch:%s failed in a reliability manner",obj->name); /* TROUBLESHOOT While attempting to propagate a changed switch's impacts, an error was encountered. Please try again. If the error persists, please submit your code and a bug report via the trac website. */ } //Ensure we don't go anywhere yet *t2 = *t0; } //End fault object present else //No object, just fail us out - save the iterations { gl_warning("No fault_check object present - Newton-Raphson solver may fail!"); /* TROUBLESHOOT A switch changed and created an open link. If the system is not meshed, the Newton-Raphson solver will likely fail. Theoretically, this should be a quick fail due to a singular matrix. However, the system occasionally gets stuck and will exhaust iteration cycles before continuing. If the fuse is blowing and the NR solver still iterates for a long time, this may be the case. */ } }//End NR call }
/* Class registration is only called once to register the class with the core */ energy_storage::energy_storage(MODULE *module) { if (oclass==NULL) { oclass = gl_register_class(module,"energy_storage",PC_PRETOPDOWN|PC_BOTTOMUP|PC_POSTTOPDOWN); if (oclass==NULL) GL_THROW("unable to register object class implemented by %s", __FILE__); if (gl_publish_variable(oclass, PT_enumeration,"GENERATOR_MODE",PADDR(gen_mode_v), PT_KEYWORD,"UNKNOWN",UNKNOWN, PT_KEYWORD,"CONSTANT_V",CONSTANT_V, PT_KEYWORD,"CONSTANT_PQ",CONSTANT_PQ, PT_KEYWORD,"CONSTANT_PF",CONSTANT_PF, PT_KEYWORD,"SUPPLY_DRIVEN",SUPPLY_DRIVEN, //PV must operate in this mode PT_enumeration,"GENERATOR_STATUS",PADDR(gen_status_v), PT_KEYWORD,"OFFLINE",OFFLINE, PT_KEYWORD,"ONLINE",ONLINE, PT_enumeration,"POWER_TYPE",PADDR(power_type_v), PT_KEYWORD,"AC",AC, PT_KEYWORD,"DC",DC, PT_double, "Rinternal", PADDR(Rinternal), PT_double, "V_Max[V]", PADDR(V_Max), PT_complex, "I_Max[A]", PADDR(I_Max), PT_double, "E_Max", PADDR(E_Max), PT_double, "Energy",PADDR(Energy), PT_double, "efficiency", PADDR(efficiency), PT_double, "Rated_kVA[kVA]", PADDR(Rated_kVA), PT_complex, "V_Out[V]", PADDR(V_Out), PT_complex, "I_Out[A]", PADDR(I_Out), PT_complex, "VA_Out[VA]", PADDR(VA_Out), PT_complex, "V_In[V]", PADDR(V_In), PT_complex, "I_In[A]", PADDR(I_In), PT_complex, "V_Internal[V]", PADDR(V_Internal), PT_complex, "I_Internal[A]",PADDR(I_Internal), PT_complex, "I_Prev[A]", PADDR(I_Prev), //resistasnces and max P, Q PT_set, "phases", PADDR(phases), PT_KEYWORD, "A",PHASE_A, PT_KEYWORD, "B",PHASE_B, PT_KEYWORD, "C",PHASE_C, PT_KEYWORD, "N",PHASE_N, PT_KEYWORD, "S",PHASE_S, NULL)<1) GL_THROW("unable to publish properties in %s",__FILE__); defaults = this; memset(this,0,sizeof(energy_storage)); /* TODO: set the default values of all properties here */ } }
//Function to change sectionalizer states - just call underlying switch routine EXPORT double change_sectionalizer_state(OBJECT *thisobj, unsigned char phase_change, bool state) { double count_values, recloser_count; char desA, desB, desC; switch_object *swtchobj; sectionalizer *sectionobj; FUNCTIONADDR funadd = NULL; bool perform_operation; //Init count_values = 0.0; if (state == false) //Check routine to find a recloser { //Map us up as a proper object sectionobj = OBJECTDATA(thisobj,sectionalizer); //Call to see if a recloser is present if (fault_check_object == NULL) { GL_THROW("Reliability call made without fault_check object present!"); /* TROUBLESHOOT A sectionalizer attempted to call a reliability-related function. However, this function requires a fault_check object to be present in the system. Please add the appropriate object. If the error persists, please submit your code and a bug report via the trac website. */ } //map the function funadd = (FUNCTIONADDR)(gl_get_function(fault_check_object,"handle_sectionalizer")); //make sure it worked if (funadd==NULL) { GL_THROW("Failed to find sectionalizer checking method on object %s",fault_check_object->name); /* TROUBLESHOOT While attempting to find the fault check method, or its subfunction to handle sectionalizers, an error was encountered. Please ensure a proper fault_check object is present in the system. If the error persists, please submit your code and a bug report via the trac website. */ } //Function call recloser_count = ((double (*)(OBJECT *, int))(*funadd))(fault_check_object,sectionobj->NR_branch_reference); if (recloser_count == 0.0) //Failed :( { GL_THROW("Failed to handle sectionalizer check on %s",thisobj->name); /* TROUBLESHOOT While attempting to handle sectionalizer actions for the specified device, an error occurred. Please try again and ensure all parameters are correct. If the error persists, please submit your code and a bug report via the trac website. */ } //Error check if (recloser_count < 0) //No recloser found, get us out of here { count_values = recloser_count; perform_operation = false; //Flag as no change allowed } else //Recloser found - pass the count out and flag a change allowed { perform_operation = true; count_values = recloser_count; } } else //No check required for reconneciton { perform_operation = true; //Flag operation as ok count_values = 1.0; //Arbitrary non-zero value so fail check doesn't go off } if (perform_operation==true) //Either is a "replace" or a recloser was found - operation is a go { //Map the switch swtchobj = OBJECTDATA(thisobj,switch_object); if (swtchobj->switch_banked_mode == switch_object::BANKED_SW) //Banked mode - all become "state", just cause { swtchobj->set_switch(state); } else //Must be individual { //Figure out what we need to call if ((phase_change & 0x04) == 0x04) { if (state==true) desA=1; //Close it else desA=0; //Open it } else //Nope, no A desA=2; //I don't care //Phase B if ((phase_change & 0x02) == 0x02) { if (state==true) desB=1; //Close it else desB=0; //Open it } else //Nope, no B desB=2; //I don't care //Phase C if ((phase_change & 0x01) == 0x01) { if (state==true) desC=1; //Close it else desC=0; //Open it } else //Nope, no A desC=2; //I don't care //Perform the switching! swtchobj->set_switch_full(desA,desB,desC); }//End individual adjustments } return count_values; }
/** oven plc control code to set the oven 'heat_needed' state The thermostat set point, deadband, oven state and current food temperature are used to determine 'heat_needed' state. **/ TIMESTAMP range::presync(TIMESTAMP t0, TIMESTAMP t1){ /* time has passed ~ calculate internal gains, height change, temperature change */ double nHours = (gl_tohours(t1) - gl_tohours(t0))/TS_SECOND; OBJECT *my = OBJECTHDR(this); // update temperature and height update_T_and_or_h(nHours); if(Tw > 212.0){ //GL_THROW("the range is boiling!"); gl_warning("range:%i is using an experimental model and should not be considered reliable", my->id); /* TROUBLESHOOT The range object has a number of VERY experimental features and development is incomplete. If you are receiving this error message, reccommend no longer using this particular feature without considerable overhaul. */ } /* determine loadshape effects */ switch(shape.type){ case MT_UNKNOWN: /* normal, undriven behavior. */ break; case MT_ANALOG: if(shape.params.analog.energy == 0.0){ GL_THROW("range does not support fixed energy shaping"); /* TROUBLESHOOT Though it is possible to drive the demand of a oven, it is not possible to shape its power or energy draw. Its heater is either on or off, not in between. Change the load shape to not specify the power or energy and try again. */ } else if (shape.params.analog.power == 0){ oven_demand = gl_get_loadshape_value(&shape) / 2.4449; } else { oven_demand = gl_get_loadshape_value(&shape); /* unitless ~ drive gpm */ } break; case MT_PULSED: /* pulsed loadshapes "emit one or more pulses at random times s. t. the total energy is accumulated over the period of the loadshape". * pulsed loadshapes can either user time or kW values per pulse. */ if(shape.params.pulsed.pulsetype == MPT_TIME){ ; /* constant time pulse ~ consumes X gallons to drive heater for Y hours ~ but what's Vdot, what's t? */ } else if(shape.params.pulsed.pulsetype == MPT_POWER){ ; /* constant power pulse ~ oven demand X kW, limited by C + Q * h ~ Vdot proportional to power/time */ oven_demand = gl_get_loadshape_value(&shape) / 2.4449; } break; case MT_MODULATED: if(shape.params.modulated.pulsetype == MPT_TIME){ GL_THROW("Amplitude modulated oven usage is nonsensical for residential ovens"); /* TROUBLESHOOT Though it is possible to put a constant,it is thoroughly counterintuitive to the normal usage of the oven. */ } else if(shape.params.modulated.pulsetype == MPT_POWER){ /* frequency modulated */ /* fixed-amplitude, varying length pulses at regular intervals. */ oven_demand = gl_get_loadshape_value(&shape) / 2.4449; } break; case MT_QUEUED: if(shape.params.queued.pulsetype == MPT_TIME){ ; /* constant time pulse ~ consumes X gallons/minute to consume Y thermal energy */ } else if(shape.params.queued.pulsetype == MPT_POWER){ ; /* constant power pulse ~ range demand X kW, limited by C + Q * h */ oven_demand = gl_get_loadshape_value(&shape) / 2.4449; } break; default: GL_THROW("range load shape has an unknown state!"); break; } return TS_NEVER; //return residential_enduse::sync(t0,t1); }
/** Register the class and publish range object properties **/ range::range(MODULE *module) : residential_enduse(module){ // first time init if (oclass==NULL) { pclass = residential_enduse::oclass; // register the class definition oclass = gl_register_class(module,"range",sizeof(range),PC_PRETOPDOWN|PC_BOTTOMUP|PC_POSTTOPDOWN); if (oclass==NULL) GL_THROW("unable to register object class implemented by %s",__FILE__); // publish the class properties if (gl_publish_variable(oclass, PT_INHERIT, "residential_enduse", PT_double,"oven_volume[gal]",PADDR(oven_volume), PT_DESCRIPTION, "the volume of the oven", PT_double,"oven_UA[Btu*h/degF]",PADDR(oven_UA), PT_DESCRIPTION, "the UA of the oven (surface area divided by R-value)", PT_double,"oven_diameter[ft]",PADDR(oven_diameter), PT_DESCRIPTION, "the diameter of the oven", PT_double,"oven_demand[gpm]",PADDR(oven_demand), PT_DESCRIPTION, "the hot food take out from the oven", PT_double,"heating_element_capacity[kW]",PADDR(heating_element_capacity), PT_DESCRIPTION, "the power of the heating element", PT_double,"inlet_food_temperature[degF]",PADDR(Tinlet), PT_DESCRIPTION, "the inlet temperature of the food", PT_enumeration,"heat_mode",PADDR(heat_mode), PT_DESCRIPTION, "the energy source for heating the oven", PT_KEYWORD,"ELECTRIC",(enumeration)ELECTRIC, PT_KEYWORD,"GASHEAT",(enumeration)GASHEAT, PT_enumeration,"location",PADDR(location), PT_DESCRIPTION, "whether the range is inside or outside", PT_KEYWORD,"INSIDE",(enumeration)INSIDE, PT_KEYWORD,"GARAGE",(enumeration)GARAGE, PT_double,"oven_setpoint[degF]",PADDR(oven_setpoint), PT_DESCRIPTION, "the temperature around which the oven will heat its contents", PT_double,"thermostat_deadband[degF]",PADDR(thermostat_deadband), PT_DESCRIPTION, "the degree to heat the food in the oven, when needed", PT_double,"temperature[degF]",PADDR(Tw), PT_DESCRIPTION, "the outlet temperature of the oven", PT_double,"height[ft]",PADDR(h), PT_DESCRIPTION, "the height of the oven", PT_double,"food_density",PADDR(food_density), PT_DESCRIPTION, "food density", PT_double,"specificheat_food",PADDR(specificheat_food), PT_double,"queue_cooktop[unit]",PADDR(enduse_queue_cooktop), PT_DESCRIPTION, "number of loads accumulated", PT_double,"queue_oven[unit]",PADDR(enduse_queue_oven), PT_DESCRIPTION, "number of loads accumulated", PT_double,"queue_min[unit]",PADDR(queue_min), PT_double,"queue_max[unit]",PADDR(queue_max), PT_double,"time_cooktop_operation",PADDR(time_cooktop_operation), PT_double,"time_cooktop_setting",PADDR(time_cooktop_setting), PT_double,"cooktop_run_prob",PADDR(cooktop_run_prob), PT_double,"oven_run_prob",PADDR(oven_run_prob), PT_double,"cooktop_coil_setting_1[kW]",PADDR(cooktop_coil_power[0]), PT_double,"cooktop_coil_setting_2[kW]",PADDR(cooktop_coil_power[1]), PT_double,"cooktop_coil_setting_3[kW]",PADDR(cooktop_coil_power[2]), PT_double,"total_power_oven[kW]",PADDR(total_power_oven), PT_double,"total_power_cooktop[kW]",PADDR(total_power_cooktop), PT_double,"total_power_range[kW]",PADDR(total_power_range), PT_double,"demand_cooktop[unit/day]",PADDR(enduse_demand_cooktop), PT_DESCRIPTION, "number of loads accumulating daily", PT_double,"demand_oven[unit/day]",PADDR(enduse_demand_oven), PT_DESCRIPTION, "number of loads accumulating daily", PT_double,"stall_voltage[V]", PADDR(stall_voltage), PT_double,"start_voltage[V]", PADDR(start_voltage), PT_complex,"stall_impedance[Ohm]", PADDR(stall_impedance), PT_double,"trip_delay[s]", PADDR(trip_delay), PT_double,"reset_delay[s]", PADDR(reset_delay), PT_double,"time_oven_operation[s]", PADDR(time_oven_operation), PT_double,"time_oven_setting[s]", PADDR(time_oven_setting), PT_enumeration,"state_cooktop", PADDR(state_cooktop), PT_KEYWORD,"CT_STOPPED",CT_STOPPED, PT_KEYWORD,"STAGE_6_ONLY",CT_STAGE_1_ONLY, PT_KEYWORD,"STAGE_7_ONLY",CT_STAGE_2_ONLY, PT_KEYWORD,"STAGE_8_ONLY",CT_STAGE_3_ONLY, PT_KEYWORD,"CT_STALLED",CT_STALLED, PT_KEYWORD,"CT_TRIPPED",CT_TRIPPED, PT_double,"cooktop_energy_baseline[kWh]", PADDR(cooktop_energy_baseline), PT_double,"cooktop_energy_used", PADDR(cooktop_energy_used), PT_double,"Toff", PADDR(Toff), PT_double,"Ton", PADDR(Ton), PT_double,"cooktop_interval_setting_1[s]", PADDR(cooktop_interval[0]), PT_double,"cooktop_interval_setting_2[s]", PADDR(cooktop_interval[1]), PT_double,"cooktop_interval_setting_3[s]", PADDR(cooktop_interval[2]), PT_double,"cooktop_energy_needed[kWh]",PADDR(cooktop_energy_needed), PT_bool,"heat_needed",PADDR(heat_needed), PT_bool,"oven_check",PADDR(oven_check), PT_bool,"remainon",PADDR(remainon), PT_bool,"cooktop_check",PADDR(cooktop_check), PT_double,"actual_load[kW]",PADDR(actual_load),PT_DESCRIPTION, "the actual load based on the current voltage across the coils", PT_double,"previous_load[kW]",PADDR(prev_load),PT_DESCRIPTION, "the actual load based on current voltage stored for use in controllers", PT_complex,"actual_power[kVA]",PADDR(range_actual_power), PT_DESCRIPTION, "the actual power based on the current voltage across the coils", PT_double,"is_range_on",PADDR(is_range_on),PT_DESCRIPTION, "simple logic output to determine state of range (1-on, 0-off)", NULL)<1) GL_THROW("unable to publish properties in %s",__FILE__); } }
/** Initialize oven model properties - randomized defaults for all published variables **/ int range::init(OBJECT *parent) { // @todo This class has serious problems and should be deleted and started from scratch. Fuller 9/27/2013. if(parent != NULL){ if((parent->flags & OF_INIT) != OF_INIT){ char objname[256]; gl_verbose("range::init(): deferring initialization on %s", gl_name(parent, objname, 255)); return 2; // defer } } OBJECT *hdr = OBJECTHDR(this); hdr->flags |= OF_SKIPSAFE; static double sTair = 74; static double sTout = 68; if (heat_fraction==0) heat_fraction = 0.2; if(parent){ pTair = gl_get_double_by_name(parent, "air_temperature"); pTout = gl_get_double_by_name(parent, "outdoor_temperature"); } if(pTair == 0){ pTair = &sTair; gl_warning("range parent lacks \'air_temperature\' property, using default"); } if(pTout == 0){ pTout = &sTout; gl_warning("range parent lacks \'outside_temperature\' property, using default"); } /* sanity checks */ /* initialize oven volume */ if(oven_volume <= 0.0){ // oven_volume = 5*floor((1.0/5.0)*gl_random_uniform(0.90, 1.10) * 50.0 * (pHouse->get_floor_area() /2000.0)); // [gal] if (oven_volume > 100.0) oven_volume = 100.0; else if (oven_volume < 20.0) oven_volume = 20.0; } if (oven_setpoint<90 || oven_setpoint>160){ GL_THROW("This model is experimental and not validated: oven thermostat is set to %f and is outside the bounds of 90 to 160 degrees Fahrenheit (32.2 - 71.1 Celsius).", oven_setpoint); /* TROUBLESHOOT TODO. */ } /* initialize oven deadband */ if (thermostat_deadband>10 || thermostat_deadband < 0.0) GL_THROW("oven deadband of %f is outside accepted bounds of 0 to 10 degrees (5.6 degC).", thermostat_deadband); // initial range UA if (oven_UA <= 0.0) GL_THROW("Range UA value is negative."); // Set heating element capacity if not provided by the user if (heating_element_capacity <= 0.0) { if (oven_volume >= 50) heating_element_capacity = 4.500; else { double randVal = gl_random_uniform(&hdr->rng_state,0,1); if (randVal < 0.33) heating_element_capacity = 3.200; else if (randVal < 0.67) heating_element_capacity = 3.500; else heating_element_capacity = 4.500; } } // Other initial conditions if(Tw < Tinlet){ // uninit'ed temperature Tw = gl_random_uniform(&hdr->rng_state,oven_setpoint - thermostat_deadband, oven_setpoint + thermostat_deadband); } current_model = NONE; load_state = STABLE; // initial demand Tset_curtail = oven_setpoint - thermostat_deadband/2 - 10; // Allow T to drop only 10 degrees below lower cut-in T... // Setup derived characteristics... area = (pi * pow(oven_diameter,2))/4; height = oven_volume/GALPCF / area; Cw = oven_volume/GALPCF * food_density * specificheat_food; // [Btu/F] h = height; // initial food temperature if(h == 0){ // discharged Tlower = Tinlet; Tupper = Tinlet + TSTAT_PRECISION; } else { Tlower = Tinlet; } /* schedule checks */ switch(shape.type){ case MT_UNKNOWN: /* normal, undriven behavior. */ gl_warning("This device, %s, is considered very experimental and has not been validated.", get_name()); break; case MT_ANALOG: if(shape.params.analog.energy == 0.0){ GL_THROW("range does not support fixed energy shaping"); /* TROUBLESHOOT Though it is possible to drive the demand of a oven, it is not possible to shape its power or energy draw. Its heater is either on or off, not in between. Change the load shape to not specify the power or energy and try again. */ } else if (shape.params.analog.power == 0){ oven_demand = gl_get_loadshape_value(&shape) / 2.4449; } else { oven_demand = gl_get_loadshape_value(&shape); /* unitless ~ drive gpm */ } break; case MT_PULSED: /* pulsed loadshapes "emit one or more pulses at random times s. t. the total energy is accumulated over the period of the loadshape". * pulsed loadshapes can either user time or kW values per pulse. */ if(shape.params.pulsed.pulsetype == MPT_TIME){ ; /* constant time pulse ~ consumes X gallons to drive heater for Y hours ~ but what's Vdot, what's t? */ } else if(shape.params.pulsed.pulsetype == MPT_POWER){ ; /* constant power pulse ~ oven demand X kW, limited by C + Q * h ~ Vdot proportional to power/time */ oven_demand = gl_get_loadshape_value(&shape) / 2.4449; } break; case MT_MODULATED: if(shape.params.modulated.pulsetype == MPT_TIME){ GL_THROW("Amplitude modulated oven usage is nonsensical for residential ovens"); /* TROUBLESHOOT Though it is possible to put a constant, it is thoroughly counterintuitive to the normal usage of the range. */ } else if(shape.params.modulated.pulsetype == MPT_POWER){ /* frequency modulated */ /* fixed-amplitude, varying length pulses at regular intervals. */ oven_demand = gl_get_loadshape_value(&shape) / 2.4449; } break; case MT_QUEUED: if(shape.params.queued.pulsetype == MPT_TIME){ ; /* constant time pulse ~ consumes X gallons/minute to consume Y thermal energy */ } else if(shape.params.queued.pulsetype == MPT_POWER){ ; /* constant power pulse ~ oven demand X kW, limited by C + Q * h */ oven_demand = gl_get_loadshape_value(&shape) / 2.4449; } break; default: GL_THROW("range load shape has an unknown state!"); break; } return residential_enduse::init(parent); }
// the constructor registers the class and properties and sets the defaults triplex_meter::triplex_meter(MODULE *mod) : triplex_node(mod) { // first time init if (oclass==NULL) { // link to parent class (used by isa) pclass = triplex_node::oclass; // register the class definition oclass = gl_register_class(mod,"triplex_meter",sizeof(triplex_meter),PC_PRETOPDOWN|PC_BOTTOMUP|PC_POSTTOPDOWN|PC_UNSAFE_OVERRIDE_OMIT); if (oclass==NULL) GL_THROW("unable to register object class implemented by %s",__FILE__); // publish the class properties if (gl_publish_variable(oclass, PT_INHERIT, "triplex_node", PT_double, "measured_real_energy[Wh]", PADDR(measured_real_energy),PT_DESCRIPTION,"metered real energy consumption", PT_double, "measured_reactive_energy[VAh]",PADDR(measured_reactive_energy),PT_DESCRIPTION,"metered reactive energy consumption", PT_complex, "measured_power[VA]", PADDR(measured_power),PT_DESCRIPTION,"metered power", PT_complex, "indiv_measured_power_1[VA]", PADDR(indiv_measured_power[0]),PT_DESCRIPTION,"metered power, phase 1", PT_complex, "indiv_measured_power_2[VA]", PADDR(indiv_measured_power[1]),PT_DESCRIPTION,"metered power, phase 2", PT_complex, "indiv_measured_power_N[VA]", PADDR(indiv_measured_power[2]),PT_DESCRIPTION,"metered power, phase N", PT_double, "measured_demand[W]", PADDR(measured_demand),PT_DESCRIPTION,"metered demand (peak of power)", PT_double, "measured_real_power[W]", PADDR(measured_real_power),PT_DESCRIPTION,"metered real power", PT_double, "measured_reactive_power[VAr]", PADDR(measured_reactive_power),PT_DESCRIPTION,"metered reactive power", PT_complex, "meter_power_consumption[VA]", PADDR(tpmeter_power_consumption),PT_DESCRIPTION,"power consumed by meter operation", // added to record last voltage/current PT_complex, "measured_voltage_1[V]", PADDR(measured_voltage[0]),PT_DESCRIPTION,"measured voltage, phase 1 to ground", PT_complex, "measured_voltage_2[V]", PADDR(measured_voltage[1]),PT_DESCRIPTION,"measured voltage, phase 2 to ground", PT_complex, "measured_voltage_N[V]", PADDR(measured_voltage[2]),PT_DESCRIPTION,"measured voltage, phase N to ground", PT_complex, "measured_current_1[A]", PADDR(measured_current[0]),PT_DESCRIPTION,"measured current, phase 1", PT_complex, "measured_current_2[A]", PADDR(measured_current[1]),PT_DESCRIPTION,"measured current, phase 2", PT_complex, "measured_current_N[A]", PADDR(measured_current[2]),PT_DESCRIPTION,"measured current, phase N", PT_bool, "customer_interrupted", PADDR(tpmeter_interrupted),PT_DESCRIPTION,"Reliability flag - goes active if the customer is in an interrupted state", PT_bool, "customer_interrupted_secondary", PADDR(tpmeter_interrupted_secondary),PT_DESCRIPTION,"Reliability flag - goes active if the customer is in a secondary interrupted state - i.e., momentary", #ifdef SUPPORT_OUTAGES PT_int16, "sustained_count", PADDR(sustained_count),PT_DESCRIPTION,"reliability sustained event counter", PT_int16, "momentary_count", PADDR(momentary_count),PT_DESCRIPTION,"reliability momentary event counter", PT_int16, "total_count", PADDR(total_count),PT_DESCRIPTION,"reliability total event counter", PT_int16, "s_flag", PADDR(s_flag),PT_DESCRIPTION,"reliability flag that gets set if the meter experienced more than n sustained interruptions", PT_int16, "t_flag", PADDR(t_flag),PT_DESCRIPTION,"reliability flage that gets set if the meter experienced more than n events total", PT_complex, "pre_load", PADDR(pre_load),PT_DESCRIPTION,"the load prior to being interrupted", #endif PT_double, "monthly_bill[$]", PADDR(monthly_bill),PT_DESCRIPTION,"Accumulator for the current month's bill", PT_double, "previous_monthly_bill[$]", PADDR(previous_monthly_bill),PT_DESCRIPTION,"Total monthly bill for the previous month", PT_double, "previous_monthly_energy[kWh]", PADDR(previous_monthly_energy),PT_DESCRIPTION,"", PT_double, "monthly_fee[$]", PADDR(monthly_fee),PT_DESCRIPTION,"Total monthly energy for the previous month", PT_double, "monthly_energy[kWh]", PADDR(monthly_energy),PT_DESCRIPTION,"Accumulator for the current month's energy", PT_enumeration, "bill_mode", PADDR(bill_mode),PT_DESCRIPTION,"Designates the bill mode to be used", PT_KEYWORD,"NONE",BM_NONE, PT_KEYWORD,"UNIFORM",BM_UNIFORM, PT_KEYWORD,"TIERED",BM_TIERED, PT_KEYWORD,"HOURLY",BM_HOURLY, PT_KEYWORD,"TIERED_RTP",BM_TIERED_RTP, PT_object, "power_market", PADDR(power_market),PT_DESCRIPTION,"Designates the auction object where prices are read from for bill mode", PT_int32, "bill_day", PADDR(bill_day),PT_DESCRIPTION,"Day bill is to be processed (assumed to occur at midnight of that day)", PT_double, "price[$/kWh]", PADDR(price),PT_DESCRIPTION,"Standard uniform pricing", PT_double, "price_base[$/kWh]", PADDR(price_base), PT_DESCRIPTION, "Used only in TIERED_RTP mode to describe the price before the first tier", PT_double, "first_tier_price[$/kWh]", PADDR(tier_price[0]),PT_DESCRIPTION,"first tier price of energy between first and second tier energy", PT_double, "first_tier_energy[kWh]", PADDR(tier_energy[0]),PT_DESCRIPTION,"price of energy on tier above price or price base", PT_double, "second_tier_price[$/kWh]", PADDR(tier_price[1]),PT_DESCRIPTION,"first tier price of energy between second and third tier energy", PT_double, "second_tier_energy[kWh]", PADDR(tier_energy[1]),PT_DESCRIPTION,"price of energy on tier above first tier", PT_double, "third_tier_price[$/kWh]", PADDR(tier_price[2]),PT_DESCRIPTION,"first tier price of energy greater than third tier energy", PT_double, "third_tier_energy[kWh]", PADDR(tier_energy[2]),PT_DESCRIPTION,"price of energy on tier above second tier", NULL)<1) GL_THROW("unable to publish properties in %s",__FILE__); } }
/** Initialize water heater model properties - randomized defaults for all published variables **/ int waterheater::init(OBJECT *parent) { OBJECT *hdr = OBJECTHDR(this); hdr->flags |= OF_SKIPSAFE; static double sTair = 74; static double sTout = 68; if(parent){ pTair = gl_get_double_by_name(parent, "air_temperature"); pTout = gl_get_double_by_name(parent, "outdoor_temperature"); } if(pTair == 0){ pTair = &sTair; gl_warning("waterheater parent lacks \'air_temperature\' property, using default"); } if(pTout == 0){ pTout = &sTout; gl_warning("waterheater parent lacks \'outside_temperature\' property, using default"); } /* sanity checks */ /* initialize water tank volume */ if(tank_volume <= 0.0){ // tank_volume = 5*floor((1.0/5.0)*gl_random_uniform(0.90, 1.10) * 50.0 * (pHouse->get_floor_area() /2000.0)); // [gal] if (tank_volume > 100.0) tank_volume = 100.0; else if (tank_volume < 20.0) tank_volume = 20.0; } else { if (tank_volume > 100.0 || tank_volume < 20.0){ gl_error("watertank volume of %f outside the volume bounds of 20 to 100 gallons.", tank_volume); /* TROUBLESHOOT All waterheaters must be set between 40 and 100 gallons. Most waterheaters are assumed to be 50 gallon tanks. */ } } if (tank_setpoint<90 || tank_setpoint>160) gl_error("watertank thermostat is set to %f and is outside the bounds of 90 to 160 degrees Fahrenheit (32.2 - 71.1 Celsius).", tank_setpoint); /* TROUBLESHOOT All waterheaters must be set between 90 degF and 160 degF. /* initialize water tank deadband */ if (thermostat_deadband>10 || thermostat_deadband < 0.0) GL_THROW("watertank deadband of %f is outside accepted bounds of 0 to 10 degrees (5.6 degC).", thermostat_deadband); // initial tank UA if (tank_UA <= 0.0) GL_THROW("Tank UA value is negative."); // Set heating element capacity if not provided by the user if (heating_element_capacity <= 0.0) { if (tank_volume >= 50) heating_element_capacity = 4.500; else { // Smaller tanks can be either 3200, 3500, or 4500... double randVal = gl_random_uniform(0,1); if (randVal < 0.33) heating_element_capacity = 3.200; else if (randVal < 0.67) heating_element_capacity = 3.500; else heating_element_capacity = 4.500; } } // set gas electric loads, if not provided by the user if(0 > gas_fan_power){ gas_fan_power = heating_element_capacity * 0.01; } if(0 > gas_standby_power){ gas_standby_power = 0.0; // some units consume 3-5W } // Other initial conditions if(Tw < Tinlet){ // uninit'ed temperature Tw = gl_random_uniform(tank_setpoint - thermostat_deadband, tank_setpoint + thermostat_deadband); } current_model = NONE; load_state = STABLE; // initial demand Tset_curtail = tank_setpoint - thermostat_deadband/2 - 10; // Allow T to drop only 10 degrees below lower cut-in T... // Setup derived characteristics... area = (pi * pow(tank_diameter,2))/4; height = tank_volume/GALPCF / area; Cw = tank_volume/GALPCF * RHOWATER * Cp; // [Btu/F] h = height; // initial water temperature if(h == 0){ // discharged Tlower = Tinlet; Tupper = Tinlet + TSTAT_PRECISION; } else { Tlower = Tinlet; } /* schedule checks */ switch(shape.type){ case MT_UNKNOWN: /* normal, undriven behavior. */ break; case MT_ANALOG: if(shape.params.analog.energy == 0.0){ GL_THROW("waterheater does not support fixed energy shaping"); /* TROUBLESHOOT Though it is possible to drive the water demand of a water heater, it is not possible to shape its power or energy draw. Its heater is either on or off, not in between. Change the load shape to not specify the power or energy and try again. */ } else if (shape.params.analog.power == 0){ /* power-driven ~ cheat with W/degF*gpm */ // double heat_per_gallon = RHOWATER * // lb/cf // CFPGAL * // lb/gal // CWATER * // BTU/degF / gal // KWBTUPH / // kW/gal // 1000.0; // W/gal water_demand = gl_get_loadshape_value(&shape) / 2.4449; } else { water_demand = gl_get_loadshape_value(&shape); /* unitless ~ drive gpm */ } break; case MT_PULSED: /* pulsed loadshapes "emit one or more pulses at random times s. t. the total energy is accumulated over the period of the loadshape". * pulsed loadshapes can either user time or kW values per pulse. */ if(shape.params.pulsed.pulsetype == MPT_TIME){ ; /* constant time pulse ~ consumes X gallons to drive heater for Y hours ~ but what's Vdot, what's t? */ } else if(shape.params.pulsed.pulsetype == MPT_POWER){ ; /* constant power pulse ~ draws water to consume X kW, limited by C + Q * h ~ Vdot proportional to power/time */ water_demand = gl_get_loadshape_value(&shape) / 2.4449; } break; case MT_MODULATED: if(shape.params.modulated.pulsetype == MPT_TIME){ GL_THROW("Amplitude modulated water usage is nonsensical for residential water heaters"); /* TROUBLESHOOT Though it is possible to put a constant, low-level water draw on a water heater, it is thoroughly counterintuitive to the normal usage of the waterheater. */ } else if(shape.params.modulated.pulsetype == MPT_POWER){ /* frequency modulated */ /* fixed-amplitude, varying length pulses at regular intervals. */ water_demand = gl_get_loadshape_value(&shape) / 2.4449; } break; case MT_QUEUED: if(shape.params.queued.pulsetype == MPT_TIME){ ; /* constant time pulse ~ consumes X gallons/minute to consume Y thermal energy */ } else if(shape.params.queued.pulsetype == MPT_POWER){ ; /* constant power pulse ~ draws water to consume X kW, limited by C + Q * h */ water_demand = gl_get_loadshape_value(&shape) / 2.4449; } break; default: GL_THROW("waterheater load shape has an unknown state!"); break; } return residential_enduse::init(parent); }
/* Class registration is only called once to register the class with the core */ power_electronics::power_electronics(MODULE *module) { if (oclass==NULL) { oclass = gl_register_class(module,"power_electronics",sizeof(power_electronics),PC_PRETOPDOWN|PC_BOTTOMUP|PC_POSTTOPDOWN); if (oclass==NULL) GL_THROW("unable to register object class implemented by %s", __FILE__); if (gl_publish_variable(oclass, //should this be "GENERATOR_MODE" OR "gen_mode_v"? PT_enumeration,"generator_mode",PADDR(gen_mode_v), PT_KEYWORD,"UNKNOWN",UNKNOWN, PT_KEYWORD,"CONSTANT_V",CONSTANT_V, PT_KEYWORD,"CONSTANT_PQ",CONSTANT_PQ, PT_KEYWORD,"CONSTANT_PF",CONSTANT_PF, PT_KEYWORD,"SUPPLY_DRIVEN",SUPPLY_DRIVEN, PT_enumeration,"generator_status",PADDR(gen_status_v), PT_KEYWORD,"OFFLINE",OFFLINE, PT_KEYWORD,"ONLINE",ONLINE, PT_enumeration,"converter_type",PADDR(converter_type_v), PT_KEYWORD,"VOLTAGE_SOURCED",VOLTAGE_SOURCED, PT_KEYWORD,"CURRENT_SOURCED",CURRENT_SOURCED, PT_enumeration,"switch_type",PADDR(switch_type_v), PT_KEYWORD,"IDEAL_SWITCH",IDEAL_SWITCH, PT_KEYWORD,"BJT",BJT, PT_KEYWORD,"MOSFET",MOSFET, PT_KEYWORD,"SCR",SCR, PT_KEYWORD,"JFET",JFET, PT_KEYWORD,"IBJT",IBJT, PT_KEYWORD,"DARLINGTON",DARLINGTON, PT_enumeration,"filter_type",PADDR(filter_type_v), PT_KEYWORD,"LOW_PASS",LOW_PASS, PT_KEYWORD,"HIGH_PASS",HIGH_PASS, PT_KEYWORD,"BAND_STOP",BAND_STOP, PT_KEYWORD,"BAND_PASS",BAND_PASS, PT_enumeration,"filter_implementation",PADDR(filter_imp_v), PT_KEYWORD,"IDEAL_FILTER",IDEAL_FILTER, PT_KEYWORD,"CAPACITVE",CAPACITIVE, PT_KEYWORD,"INDUCTIVE",INDUCTIVE, PT_KEYWORD,"SERIES_RESONANT",SERIES_RESONANT, PT_KEYWORD,"PARALLEL_RESONANT",PARALLEL_RESONANT, PT_enumeration,"filter_frequency",PADDR(filter_freq_v), PT_KEYWORD,"F120HZ",F120HZ, PT_KEYWORD,"F180HZ",F180HZ, PT_KEYWORD,"F240HZ",F240HZ, PT_enumeration,"power_type",PADDR(power_type_v), PT_KEYWORD,"AC",AC, PT_KEYWORD,"DC",DC, PT_double, "Rated_kW[kW]", PADDR(Rated_kW), //< nominal power in kW PT_double, "Max_P[kW]", PADDR(Max_P),//< maximum real power capacity in kW PT_double, "Min_P[kW]", PADDR(Min_P),//< minimum real power capacity in kW //PT_double, "Max_Q[kVAr]", PADDR(Max_Q),//< maximum reactive power capacity in kVar //PT_double, "Min_Q[kVAr]", PADDR(Min_Q),//< minimum reactive power capacity in kVar PT_double, "Rated_kVA[kVA]", PADDR(Rated_kVA), PT_double, "Rated_kV[kV]", PADDR(Rated_kV), //PT_int64, "generator_mode_choice", PADDR(generator_mode_choice), /* PT_double, Rinternal; PT_double, Rload; PT_double, Rtotal; PT_double, Rphase; PT_double, Rground; PT_double, Rground_storage; PT_double[3], Rfilter; PT_double, Cinternal; PT_double, Cground; PT_double, Ctotal; PT_double[3], Cfilter; PT_double, Linternal; PT_double, Lground; PT_double, Ltotal; PT_double[3], Lfilter; PT_bool, filter_120HZ; PT_bool, filter_180HZ; PT_bool, filter_240HZ; */ //PT_double, "pf_in[double]", PADDR(pf_in), //PT_double, "pf_out[double]", PADDR(pf_out), /* PT_int, number_of_phases_in; PT_int, number_of_phases_out; SWITCH_TYPE switch_type_choice; SWITCH_IMPLEMENTATION switch_implementation_choice; GENERATOR_MODE generator_mode_choice; POWER_TYPE power_in; POWER_TYPE power_out; */ //PT_double, "efficiency[double]", PADDR(efficiency), /* PT_complex, "voltage_A[V]", PADDR(voltage_A), PT_complex, "voltage_B[V]", PADDR(voltage_B), PT_complex, "voltage_C[V]", PADDR(voltage_C), PT_complex, "current_A[A]", PADDR(current_A), PT_complex, "current_B[A]", PADDR(current_B), PT_complex, "current_C[A]", PADDR(current_C), PT_complex, "EfA[V]", PADDR(EfA), PT_complex, "EfB[V]", PADDR(EfB), PT_complex, "EfC[V]", PADDR(EfC), PT_complex, "power_A[VA]", PADDR(power_A), PT_complex, "power_B[VA]", PADDR(power_B), PT_complex, "power_C[VA]", PADDR(power_C), PT_complex, "power_A_sch[VA]", PADDR(power_A_sch), PT_complex, "power_B_sch[VA]", PADDR(power_B_sch), PT_complex, "power_C_sch[VA]", PADDR(power_C_sch), PT_complex, "EfA_sch[V]", PADDR(EfA_sch), PT_complex, "EfB_sch[V]", PADDR(EfB_sch), PT_complex, "EfC_sch[V]", PADDR(EfC_sch), */ PT_set, "phases", PADDR(phases), PT_KEYWORD, "A",(set)PHASE_A, PT_KEYWORD, "B",(set)PHASE_B, PT_KEYWORD, "C",(set)PHASE_C, PT_KEYWORD, "N",(set)PHASE_N, PT_KEYWORD, "S",(set)PHASE_S, NULL)<1) GL_THROW("unable to publish properties in %s",__FILE__); defaults = this; memset(this,0,sizeof(power_electronics)); /* TODO: set the default values of all properties here */ } }
/** Water heater plc control code to set the water heater 'heat_needed' state The thermostat set point, deadband, tank state(height of hot water column) and current water temperature are used to determine 'heat_needed' state. **/ TIMESTAMP waterheater::presync(TIMESTAMP t0, TIMESTAMP t1){ /* time has passed ~ calculate internal gains, height change, temperature change */ double nHours = (gl_tohours(t1) - gl_tohours(t0))/TS_SECOND; OBJECT *my = OBJECTHDR(this); // update temperature and height update_T_and_or_h(nHours); if(Tw > 212.0){ //GL_THROW("the waterheater is boiling!"); gl_warning("waterheater:%i is boiling", my->id); /* TROUBLESHOOT The temperature model for the waterheater has broken, or the environment around the waterheater has burst into flames. Please post this with your model and dump files attached to the bug report. */ } /* determine loadshape effects */ switch(shape.type){ case MT_UNKNOWN: /* normal, undriven behavior. */ break; case MT_ANALOG: if(shape.params.analog.energy == 0.0){ GL_THROW("waterheater does not support fixed energy shaping"); /* TROUBLESHOOT Though it is possible to drive the water demand of a water heater, it is not possible to shape its power or energy draw. Its heater is either on or off, not in between. Change the load shape to not specify the power or energy and try again. */ } else if (shape.params.analog.power == 0){ /* power-driven ~ cheat with W/degF*gpm */ // double heat_per_gallon = RHOWATER * // lb/cf // CFPGAL * // lb/gal // CWATER * // BTU/degF / gal // KWBTUPH / // kW/gal // 1000.0; // W/gal water_demand = gl_get_loadshape_value(&shape) / 2.4449; } else { water_demand = gl_get_loadshape_value(&shape); /* unitless ~ drive gpm */ } break; case MT_PULSED: /* pulsed loadshapes "emit one or more pulses at random times s. t. the total energy is accumulated over the period of the loadshape". * pulsed loadshapes can either user time or kW values per pulse. */ if(shape.params.pulsed.pulsetype == MPT_TIME){ ; /* constant time pulse ~ consumes X gallons to drive heater for Y hours ~ but what's Vdot, what's t? */ } else if(shape.params.pulsed.pulsetype == MPT_POWER){ ; /* constant power pulse ~ draws water to consume X kW, limited by C + Q * h ~ Vdot proportional to power/time */ water_demand = gl_get_loadshape_value(&shape) / 2.4449; } break; case MT_MODULATED: if(shape.params.modulated.pulsetype == MPT_TIME){ GL_THROW("Amplitude modulated water usage is nonsensical for residential water heaters"); /* TROUBLESHOOT Though it is possible to put a constant, low-level water draw on a water heater, it is thoroughly counterintuitive to the normal usage of the waterheater. */ } else if(shape.params.modulated.pulsetype == MPT_POWER){ /* frequency modulated */ /* fixed-amplitude, varying length pulses at regular intervals. */ water_demand = gl_get_loadshape_value(&shape) / 2.4449; } break; case MT_QUEUED: if(shape.params.queued.pulsetype == MPT_TIME){ ; /* constant time pulse ~ consumes X gallons/minute to consume Y thermal energy */ } else if(shape.params.queued.pulsetype == MPT_POWER){ ; /* constant power pulse ~ draws water to consume X kW, limited by C + Q * h */ water_demand = gl_get_loadshape_value(&shape) / 2.4449; } break; default: GL_THROW("waterheater load shape has an unknown state!"); break; } return TS_NEVER; //return residential_enduse::sync(t0,t1); }
int fuse::init(OBJECT *parent) { char jindex, kindex; OBJECT *obj = OBJECTHDR(this); if ((phases & PHASE_S) == PHASE_S) { GL_THROW("fuses cannot be placed on triplex circuits"); /* TROUBLESHOOT Fuses do not currently support triplex circuits. Please place the fuse higher upstream in the three-phase power area or utilize another object (such as a circuit breaker in a house model) to limit the current flow. */ } //Special flag moved to be universal for all solvers - mainly so phase checks catch it now SpecialLnk = SWITCH; int result = link::init(parent); //Check current limit if (current_limit < 0.0) { GL_THROW("fuse:%s has a negative current limit value!",obj->name); /* TROUBLESHOOT The fuse has a negative value current limit specified. Please specify a positive value for the current and try again. */ } if (current_limit == 0.0) { current_limit = 9999.0; //Set to arbitrary large value gl_warning("fuse:%s has a zero current limit - set to 9999.9 Amps",obj->name); /* TROUBLESHOOT A fuse somehow had a current limit of 0.0 Amps set. This is invalid, so a placeholder value of 9999.0 Amps is used. Please adjust this value accordingly. */ } if (mean_replacement_time<=0.0) //Make sure the time makes sense { gl_warning("Fuse:%s has a negative or 0 mean replacement time - defaulting to 1 hour",obj->name); /* TROUBLESHOOT A fuse has a negative or zero time specified for mean_replacement_time. The value has therefore been overridden to 1 hour. If this is unacceptable, please change the value in your GLM file. */ mean_replacement_time = 3600.0; } //Set mean_repair_time to the same value mean_repair_time = mean_replacement_time; if (solver_method==SM_FBS) { gl_warning("Fuses only work for the attached node in the FBS solver, not any deeper."); /* TROUBLESHOOT Under the Forward-Back sweep method, fuses can only affect their directly attached downstream node. Due to the nature of the FBS algorithm, nodes further downstream (especially constant current loads) will cause an oscillatory nature in the voltage and current injections, so they will no longer be accurate. Either ignore these values or figure out a way to work around this limitation (player objects). */ } //Initialize matrices for (jindex=0; jindex<3; jindex++) { for (kindex=0; kindex<3; kindex++) { a_mat[jindex][kindex] = d_mat[jindex][kindex] = A_mat[jindex][kindex] = 0.0; c_mat[jindex][kindex] = 0.0; B_mat[jindex][kindex] = b_mat[jindex][kindex] = 0.0; } } a_mat[0][0] = d_mat[0][0] = A_mat[0][0] = (is_closed() && has_phase(PHASE_A) ? 1.0 : 0.0); a_mat[1][1] = d_mat[1][1] = A_mat[1][1] = (is_closed() && has_phase(PHASE_B) ? 1.0 : 0.0); a_mat[2][2] = d_mat[2][2] = A_mat[2][2] = (is_closed() && has_phase(PHASE_C) ? 1.0 : 0.0); if (solver_method==SM_FBS) { b_mat[0][0] = c_mat[0][0] = B_mat[0][0] = 0.0; b_mat[1][1] = c_mat[1][1] = B_mat[1][1] = 0.0; b_mat[2][2] = c_mat[2][2] = B_mat[2][2] = 0.0; } else if (solver_method==SM_NR) { //Flagged it as special (we'll forgo inversion processes on this) //Initialize off-diagonals just in case From_Y[0][1] = From_Y[0][2] = From_Y[1][0] = 0.0; From_Y[1][2] = From_Y[2][0] = From_Y[2][1] = 0.0; if (status==LS_OPEN) //Take this as all should be open { From_Y[0][0] = complex(0.0,0.0); From_Y[1][1] = complex(0.0,0.0); From_Y[2][2] = complex(0.0,0.0); phase_A_state = phase_B_state = phase_C_state = BLOWN; //All open prev_full_status = 0x00; //Confirm here } else //LS_CLOSED - handle individually { if (has_phase(PHASE_A)) { if (phase_A_state == GOOD) { From_Y[0][0] = complex(1e4,1e4); prev_full_status |= 0x04; } else //Must be open { From_Y[0][0] = complex(0.0,0.0); prev_full_status &=0xFB; } } if (has_phase(PHASE_B)) { if (phase_B_state == GOOD) { From_Y[1][1] = complex(1e4,1e4); prev_full_status |= 0x02; } else //Must be open { From_Y[1][1] = complex(0.0,0.0); prev_full_status &=0xFD; } } if (has_phase(PHASE_C)) { if (phase_C_state == GOOD) { From_Y[2][2] = complex(1e4,1e4); prev_full_status |= 0x01; } else //Must be open { From_Y[2][2] = complex(0.0,0.0); prev_full_status &=0xFE; } } } } else { GL_THROW("Fuses are not supported by this solver method"); /* TROUBLESHOOT Fuses are currently only supported in Forward-Back Sweep and Newton-Raphson Solvers. Using them in other solvers is untested and may provide erroneous answers (if any at all). */ } //Store fuse status - will get updated as things change later phased_fuse_status = prev_full_status; return result; }
void waterheater::update_T_and_or_h(double nHours) { /* When this gets called (right after the waterheater gets sync'd), all states are exactly as they were at the end of the last sync. We calculate what has happened to the water temperature (or the warm/cold boundarly location, depending on the current state) in the interim. If nHours equals our previously requested timeToTransition, we should find things landing on a new state. If not, we should find ourselves in the same state again. But this routine doesn't try to figure that out...it just calculates the new T/h. */ // set the model and load state switch (current_model) { case ONENODE: // Handy that the 1-node model doesn't care which way // things are moving (RECOVERING vs DEPLETING)... SingleZone: Tw = new_temp_1node(Tw, nHours); /*Tupper*/ Tw = Tw; Tlower = Tinlet; break; case TWONODE: // overriding the plc code ignoring thermostat logic // heating will always be on while in two zone model heat_needed = TRUE; switch (load_state) { case STABLE: // Change nothing... break; case DEPLETING: // Fall through... case RECOVERING: try { h = new_h_2zone(h, nHours); } catch (WRONGMODEL m) { if (m==MODEL_NOT_2ZONE) { current_model = ONENODE; goto SingleZone; } else GL_THROW("unexpected exception in update_T_and_or_h(%+.1f hrs)", nHours); } break; } // Correct h if it overshot... if (h < ROUNDOFF) { // We've over-depleted the tank slightly. Make a quickie // adjustment to Tlower/Tw to account for it... double vol_over = tank_volume/GALPCF * h/height; // Negative... double energy_over = vol_over * RHOWATER * Cp * (/*Tupper*/ Tw - Tlower); double Tnew = Tlower + energy_over/Cw; Tw = Tlower = Tnew; h = 0; } else if (h > height) { // Ditto for over-recovery... double vol_over = tank_volume/GALPCF * (h-height)/height; double energy_over = vol_over * RHOWATER * Cp * (/*Tupper*/ Tw - Tlower); double Tnew = /*Tupper*/ Tw + energy_over/Cw; Tw = /*Tupper*/ Tw = Tnew; Tlower = Tinlet; h = height; } else { // Note that as long as h stays between 0 and height, we don't // adjust Tlower, even if the Tinlet has changed. This avoids // the headache of adjusting h and is of minimal consequence because // Tinlet changes so slowly... /*Tupper*/ Tw = Tw; } break; default: break; } if (heat_needed == TRUE) power_state = PS_ON; else power_state = PS_OFF; return; }
/** * Fuse checking function * Hold over code from previous functionality - lets FBS work as-is, for now. * * functionalized so don't have to change 4 entries in 3 different sets every time * * @param phase_to_check - the current phase to check fusing action for * @param fcurr - array of from (line input) currents */ void fuse::fuse_check(set phase_to_check, complex *fcurr) { char indexval; char phase_verbose; unsigned char work_phase; FUSESTATE *valstate; TIMESTAMP *fixtime; OBJECT *hdr = OBJECTHDR(this); if (phase_to_check == PHASE_A) { indexval = 0; valstate = (FUSESTATE*)&phase_A_state; phase_verbose='A'; fixtime = &fix_time[0]; } else if (phase_to_check == PHASE_B) { indexval = 1; valstate = (FUSESTATE*)&phase_B_state; phase_verbose='B'; fixtime = &fix_time[1]; } else if (phase_to_check == PHASE_C) { indexval = 2; valstate = (FUSESTATE*)&phase_C_state; phase_verbose='C'; fixtime = &fix_time[2]; } else { GL_THROW("Unknown phase to check in fuse:%d",OBJECTHDR(this)->id); /* TROUBLESHOOT An invalid phase was specified for the phase check in a fuse. Please check your code and continue. If it persists, submit your code and a bug using the trac website. */ } //See which phases we need to check if ((phases & phase_to_check) == phase_to_check) //Check phase { work_phase = 0x04 >> indexval; //Working variable, primarily for NR if (*valstate == GOOD) //Only bother if we are in service { //Check both directions, that way if we are reverse flowed it doesn't matter if (fcurr[indexval].Mag() > current_limit) //We've exceeded the limit { *valstate = BLOWN; //Trip us //Set us up appropriately A_mat[indexval][indexval] = d_mat[indexval][indexval] = 0.0; //Get an update time *fixtime = prev_fuse_time + (int64)(3600*gl_random_exponential(1.0/mean_replacement_time)); //Announce it for giggles gl_warning("Phase %c of fuse:%d (%s) just blew",phase_verbose,hdr->id,hdr->name); } else //Still good { //Ensure matrices are up to date in case someone manually set things A_mat[indexval][indexval] = d_mat[indexval][indexval] = 1.0; } } else //We're blown { if (*fixtime <= prev_fuse_time) //Technician has arrived and replaced us!! { //Fix us A_mat[indexval][indexval] = d_mat[indexval][indexval] = 1.0; *valstate = GOOD; *fixtime = TS_NEVER; //Update the time check just in case //Send an announcement for giggles gl_warning("Phase %c of fuse:%d (%s) just returned to service",phase_verbose,hdr->id,hdr->name); } else //Still driving there or on break, no fixed yet { //Ensure matrices are up to date in case someone manually blew us (or a third, off state is implemented) A_mat[indexval][indexval] = d_mat[indexval][indexval] = 0.0; } } }
/* Object initialization is called once after all object have been created */ int rectifier::init(OBJECT *parent) { OBJECT *obj = OBJECTHDR(this); //initialize variables that are used internally //set_terminal_voltage = 240; //V //max_current_step_size = 100; //A Rated_kW = 1000; //< nominal power in kW Max_P = 1000;//< maximum real power capacity in kW Min_P = 0;//< minimum real power capacity in kW //Max_Q = 1000;//< maximum reactive power capacity in kVar //Min_Q = 1000;//< minimum reactive power capacity in kVar Rated_kVA = 1500; //< nominal capacity in kVA Rated_kV = 10; //< nominal line-line voltage in kV Rinternal = 0.035; Rload = 1; Rtotal = 0.05; //XphaseA = complex(1 * 1,0); //XphaseB = complex(1 * -0.5, 1 * 0.866); //XphaseC = complex(1 * -0.5, 1 * -0.866); XphaseA = complex(5 * 1,0); XphaseB = complex(5 * 1,0); XphaseC = complex(5 * 1,0); Rground = 0.03; Rground_storage = 0.05; Vdc = 480; Cinternal = 0; Cground = 0; Ctotal = 0; Linternal = 0; Lground = 0; Ltotal = 0; filter_120HZ = false; filter_180HZ = false; filter_240HZ = false; pf_in = 1; pf_out = 0; number_of_phases_in = 3; number_of_phases_out = 0; phaseAIn = true; phaseBIn = true; phaseCIn = true; phaseAOut = false; phaseBOut = false; phaseCOut = false; V_In_Set_A = complex(360,0); V_In_Set_B = complex(-180, 311.769); V_In_Set_C = complex(-180,-311.769); switch_type_choice = IDEAL_SWITCH; //generator_mode_choice = CONSTANT_PQ; gen_status_v = ONLINE; filter_type_v = BAND_PASS; filter_imp_v = IDEAL_FILTER; rectifier_type_v = SIX_PULSE; power_in = AC; power_out = DC; P_Out = 500; // P_Out and Q_Out are set by the user as set values to output in CONSTANT_PQ mode Q_Out = 0; margin = 50; V_Rated = 360; I_out_prev = 0; I_step_max = 100; internal_losses = 0; C_Storage_Out = 0; efficiency = 0; losses = 0; on_ratio = 0; input_frequency = 2000; frequency_losses = 0; gl_verbose("rectifier init: initialized the variables"); int i; if (parent!=NULL && gl_object_isa(parent,"meter")) { // TO DO: Figure out how to connect a DC device to meter. } if (parent!=NULL && gl_object_isa(parent,"inverter")) { parent_string = "inverter"; struct { complex **var; char *varname; } map[] = { // local object name, meter object name {&pCircuit_V, "Vdc"}, {&pLine_I, "I_In"} }; // attach meter variables to each circuit for (i=0; i<sizeof(map)/sizeof(map[0]); i++) { if ((*(map[i].var) = get_complex(parent,map[i].varname))==NULL) { GL_THROW("%s (%s:%d) does not implement triplex_meter variable %s for %s (inverter:%d)", /* TROUBLESHOOT The rectifier requires that the inverter contains certain published properties in order to properly connect. If you encounter this error, please report it to the developers, along with the version of GridLAB-D that raised this error. */ parent->name?parent->name:"unnamed object", parent->oclass->name, parent->id, map[i].varname, obj->name?obj->name:"unnamed", obj->id); } } } if (parent_string == NULL){ GL_THROW("Rectifier's parent is not an inverter. The rectifier object's parent must be an inverter."); } /* TODO: set the context-dependent initial value of properties */ if (gen_mode_v==UNKNOWN) { GL_THROW("Generator control mode is not specified."); } else if(gen_mode_v == CONSTANT_V) { GL_THROW("Generator mode CONSTANT_V is not implemented yet."); } else if(gen_mode_v == CONSTANT_PQ) { GL_THROW("Generator mode CONSTANT_PQ is not implemented yet."); } else if(gen_mode_v == CONSTANT_PF) { GL_THROW("Generator mode CONSTANT_PF is not implemented yet."); } //need to check for parameters SWITCH_TYPE, FILTER_TYPE, FILTER_IMPLEMENTATION, GENERATOR_MODE /* if (Rated_kW!=0.0) SB = Rated_kW/sqrt(1-Rated_pf*Rated_pf); if (Rated_kVA!=0.0) SB = Rated_kVA/3; if (Rated_kV!=0.0) EB = Rated_kV/sqrt(3.0); if (SB!=0.0) ZB = EB*EB/SB; else throw("Generator power capacity not specified!"); double Real_Rinternal = Rinternal * ZB; double Real_Rload = Rload * ZB; double Real_Rtotal = Rtotal * ZB; double Real_Xphase = Xphase * ZB; double Real_Rground = Rground * ZB; double Real_Rground_storage = Rground_storage * ZB; double[3] Real_Rfilter = Rfilter * ZB; double Real_Cinternal = Cinternal * ZB; double Real_Cground = Cground * ZB; double Real_Ctotal = Ctotal * ZB; double[3] Real_Cfilter = Cfilter * ZB; double Real_Linternal = Linternal * ZB; double Real_Lground = Lground * ZB; double Real_Ltotal = Ltotal * ZB; double[3] Real_Lfilter = Lfilter * ZB; tst = complex(Real_Rground,Real_Lground); AMx[0][0] = complex(Real_Rinternal,Real_Linternal) + tst; AMx[1][1] = complex(Real_Rinternal,Real_Linternal) + tst; AMx[2][2] = complex(Real_Rinternal,Real_Linternal) + tst; // AMx[0][0] = AMx[1][1] = AMx[2][2] = complex(Real_Rs+Real_Rg,Real_Xs+Real_Xg); AMx[0][1] = AMx[0][2] = AMx[1][0] = AMx[1][2] = AMx[2][0] = AMx[2][1] = tst; */ //all other variables set in input file through public parameters switch(rectifier_type_v){ case ONE_PULSE: efficiency = 0.5; break; case TWO_PULSE: efficiency = 0.7; break; case THREE_PULSE: efficiency = 0.7; break; case SIX_PULSE: efficiency = 0.8; break; case TWELVE_PULSE: efficiency = 0.9; break; default: efficiency = 0.8; break; } internal_switch_resistance(switch_type_choice); filter_circuit_impact((power_electronics::FILTER_TYPE)filter_type_v, (power_electronics::FILTER_IMPLEMENTATION)filter_imp_v); gl_verbose("rectifier init: about to exit"); return 1; }
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; }
/* Class registration is only called once to register the class with the core */ rectifier::rectifier(MODULE *module) { if (oclass==NULL) { oclass = gl_register_class(module,"rectifier",sizeof(rectifier),PC_PRETOPDOWN|PC_BOTTOMUP|PC_POSTTOPDOWN|PC_AUTOLOCK); if (oclass==NULL) throw "unable to register class rectifier"; else oclass->trl = TRL_PROOF; if (gl_publish_variable(oclass, PT_enumeration,"rectifier_type",PADDR(rectifier_type_v), PT_KEYWORD,"ONE_PULSE",(enumeration)ONE_PULSE, PT_KEYWORD,"TWO_PULSE",(enumeration)TWO_PULSE, PT_KEYWORD,"THREE_PULSE",(enumeration)THREE_PULSE, PT_KEYWORD,"SIX_PULSE",(enumeration)SIX_PULSE, PT_KEYWORD,"TWELVE_PULSE",(enumeration)TWELVE_PULSE, PT_enumeration,"generator_mode",PADDR(gen_mode_v), PT_KEYWORD,"UNKNOWN",(enumeration)UNKNOWN, PT_KEYWORD,"CONSTANT_V",(enumeration)CONSTANT_V, PT_KEYWORD,"CONSTANT_PQ",(enumeration)CONSTANT_PQ, PT_KEYWORD,"CONSTANT_PF",(enumeration)CONSTANT_PF, PT_KEYWORD,"SUPPLY_DRIVEN",(enumeration)SUPPLY_DRIVEN, PT_complex, "V_Out[V]",PADDR(V_Out), PT_double, "V_Rated[V]",PADDR(V_Rated), PT_complex, "I_Out[A]",PADDR(I_Out), PT_complex, "VA_Out[VA]", PADDR(VA_Out), PT_double, "P_Out", PADDR(P_Out), PT_double, "Q_Out", PADDR(Q_Out), PT_complex, "Vdc[V]", PADDR(Vdc), PT_complex, "voltage_A[V]", PADDR(voltage_A), PT_complex, "voltage_B[V]", PADDR(voltage_B), PT_complex, "voltage_C[V]", PADDR(voltage_C), PT_complex, "current_A[V]", PADDR(current_A), PT_complex, "current_B[V]", PADDR(current_B), PT_complex, "current_C[V]", PADDR(current_C), PT_complex, "power_A_In[VA]", PADDR(power_A_In), PT_complex, "power_B_In[VA]", PADDR(power_B_In), PT_complex, "power_C_In[VA]", PADDR(power_C_In), //PT_int64, "generator_mode_choice", PADDR(generator_mode_choice), PT_set, "phases", PADDR(phases), PT_KEYWORD, "A",(set)PHASE_A, PT_KEYWORD, "B",(set)PHASE_B, PT_KEYWORD, "C",(set)PHASE_C, PT_KEYWORD, "N",(set)PHASE_N, PT_KEYWORD, "S",(set)PHASE_S, NULL)<1) GL_THROW("unable to publish properties in %s",__FILE__); defaults = this; memset(this,0,sizeof(rectifier)); /* TODO: set the default values of all properties here */ } }
windturb_dg::windturb_dg(MODULE *module) { if (oclass==NULL) { // register to receive notice for first top down. bottom up, and second top down synchronizations oclass = gl_register_class(module,"windturb_dg",sizeof(windturb_dg),PC_PRETOPDOWN|PC_BOTTOMUP|PC_POSTTOPDOWN|PC_AUTOLOCK); if (oclass==NULL) throw "unable to register class windturb_dg"; else oclass->trl = TRL_PROOF; if (gl_publish_variable(oclass, PT_enumeration,"Gen_status",PADDR(Gen_status), PT_DESCRIPTION, "Describes whether the generator is currently online or offline", PT_KEYWORD,"OFFLINE",(enumeration)OFFLINE, PT_DESCRIPTION, "Generator is currently not supplying power", PT_KEYWORD,"ONLINE",(enumeration)ONLINE, PT_DESCRIPTION, "Generator is currently available to supply power", PT_enumeration,"Gen_type",PADDR(Gen_type), PT_DESCRIPTION, "Type of generator", PT_KEYWORD,"INDUCTION",(enumeration)INDUCTION, PT_DESCRIPTION, "Standard induction generator", PT_KEYWORD,"SYNCHRONOUS",(enumeration)SYNCHRONOUS, PT_DESCRIPTION, "Standard synchronous generator; is also used to 'fake' a doubly-fed induction generator for now", PT_enumeration,"Gen_mode",PADDR(Gen_mode), PT_DESCRIPTION, "Control mode that is used for the generator output", PT_KEYWORD,"CONSTANTE",(enumeration)CONSTANTE, PT_DESCRIPTION, "Maintains the voltage at the terminals", PT_KEYWORD,"CONSTANTP",(enumeration)CONSTANTP, PT_DESCRIPTION, "Maintains the real power output at the terminals", PT_KEYWORD,"CONSTANTPQ",(enumeration)CONSTANTPQ, PT_DESCRIPTION, "Maintains the real and reactive output at the terminals - currently unsupported", PT_enumeration,"Turbine_Model",PADDR(Turbine_Model), PT_DESCRIPTION, "Type of turbine being represented; using any of these except USER_DEFINED also specifies a large number of defaults", PT_KEYWORD,"GENERIC_SYNCH_SMALL",(enumeration)GENERIC_SYNCH_SMALL, PT_DESCRIPTION, "Generic model for a small, fixed pitch synchronous turbine", PT_KEYWORD,"GENERIC_SYNCH_MID",(enumeration)GENERIC_SYNCH_MID, PT_DESCRIPTION, "Generic model for a mid-size, fixed pitch synchronous turbine", PT_KEYWORD,"GENERIC_SYNCH_LARGE",(enumeration)GENERIC_SYNCH_LARGE, PT_DESCRIPTION, "Generic model for a large, fixed pitch synchronous turbine", PT_KEYWORD,"GENERIC_IND_SMALL",(enumeration)GENERIC_IND_SMALL, PT_DESCRIPTION, "Generic model for a small induction, fixed pitch generator turbine", PT_KEYWORD,"GENERIC_IND_MID",(enumeration)GENERIC_IND_MID, PT_DESCRIPTION, "Generic model for a mid-size induction, fixed pitch generator turbine", PT_KEYWORD,"GENERIC_IND_LARGE",(enumeration)GENERIC_IND_LARGE, PT_DESCRIPTION, "Generic model for a large induction, fixed pitch generator turbine", PT_KEYWORD,"USER_DEFINED",(enumeration)USER_DEFINED, PT_DESCRIPTION, "Allows user to specify all parameters - is not currently supported", // TODO: Doesnt work yet PT_KEYWORD,"VESTAS_V82",(enumeration)VESTAS_V82, PT_DESCRIPTION, "Sets all defaults to represent the power output of a VESTAS V82 turbine", PT_KEYWORD,"GE_25MW",(enumeration)GE_25MW, PT_DESCRIPTION, "Sets all defaults to represent the power output of a GE 2.5MW turbine", PT_KEYWORD,"BERGEY_10kW",(enumeration)BERGEY_10kW, PT_DESCRIPTION, "Sets all defaults to represent the power output of a Bergey 10kW turbine", // TODO: There are a number of repeat variables due to poor variable name formatting. // These need to be corrected through the deprecation process. PT_double, "turbine_height[m]", PADDR(turbine_height), PT_DESCRIPTION, "Describes the height of the wind turbine hub above the ground", PT_double, "roughness_length_factor", PADDR(roughness_l), PT_DESCRIPTION, "European Wind Atlas unitless correction factor for adjusting wind speed at various heights above ground and terrain types, default=0.055", PT_double, "blade_diam[m]", PADDR(blade_diam), PT_DESCRIPTION, "Diameter of blades", PT_double, "blade_diameter[m]", PADDR(blade_diam), PT_DESCRIPTION, "Diameter of blades", PT_double, "cut_in_ws[m/s]", PADDR(cut_in_ws), PT_DESCRIPTION, "Minimum wind speed for generator operation", PT_double, "cut_out_ws[m/s]", PADDR(cut_out_ws), PT_DESCRIPTION, "Maximum wind speed for generator operation", PT_double, "ws_rated[m/s]", PADDR(ws_rated), PT_DESCRIPTION, "Rated wind speed for generator operation", PT_double, "ws_maxcp[m/s]", PADDR(ws_maxcp), PT_DESCRIPTION, "Wind speed at which generator reaches maximum Cp", PT_double, "Cp_max[pu]", PADDR(Cp_max), PT_DESCRIPTION, "Maximum coefficient of performance", PT_double, "Cp_rated[pu]", PADDR(Cp_rated), PT_DESCRIPTION, "Rated coefficient of performance", PT_double, "Cp[pu]", PADDR(Cp), PT_DESCRIPTION, "Calculated coefficient of performance", PT_double, "Rated_VA[VA]", PADDR(Rated_VA), PT_DESCRIPTION, "Rated generator power output", PT_double, "Rated_V[V]", PADDR(Rated_V), PT_DESCRIPTION, "Rated generator terminal voltage", PT_double, "Pconv[W]", PADDR(Pconv), PT_DESCRIPTION, "Amount of electrical power converted from mechanical power delivered", PT_double, "P_converted[W]", PADDR(Pconv), PT_DESCRIPTION, "Amount of electrical power converted from mechanical power delivered", PT_double, "GenElecEff[%]", PADDR(GenElecEff), PT_DESCRIPTION, "Calculated generator electrical efficiency", PT_double, "generator_efficiency[%]", PADDR(GenElecEff), PT_DESCRIPTION, "Calculated generator electrical efficiency", PT_double, "TotalRealPow[W]", PADDR(TotalRealPow), PT_DESCRIPTION, "Total real power output", PT_double, "total_real_power[W]", PADDR(TotalRealPow), PT_DESCRIPTION, "Total real power output", PT_double, "TotalReacPow[VA]", PADDR(TotalReacPow), PT_DESCRIPTION, "Total reactive power output", PT_double, "total_reactive_power[VA]", PADDR(TotalReacPow), PT_DESCRIPTION, "Total reactive power output", PT_complex, "power_A[VA]", PADDR(power_A), PT_DESCRIPTION, "Total complex power injected on phase A", PT_complex, "power_B[VA]", PADDR(power_B), PT_DESCRIPTION, "Total complex power injected on phase B", PT_complex, "power_C[VA]", PADDR(power_C), PT_DESCRIPTION, "Total complex power injected on phase C", PT_double, "WSadj[m/s]", PADDR(WSadj), PT_DESCRIPTION, "Speed of wind at hub height", PT_double, "wind_speed_adjusted[m/s]", PADDR(WSadj), PT_DESCRIPTION, "Speed of wind at hub height", PT_double, "Wind_Speed[m/s]", PADDR(Wind_Speed), PT_DESCRIPTION, "Wind speed at 5-15m level (typical measurement height)", PT_double, "wind_speed[m/s]", PADDR(Wind_Speed), PT_DESCRIPTION, "Wind speed at 5-15m level (typical measurement height)", PT_double, "air_density[kg/m^3]", PADDR(air_dens), PT_DESCRIPTION, "Estimated air density", PT_double, "R_stator[pu*Ohm]", PADDR(Rst), PT_DESCRIPTION, "Induction generator primary stator resistance in p.u.", PT_double, "X_stator[pu*Ohm]", PADDR(Xst), PT_DESCRIPTION, "Induction generator primary stator reactance in p.u.", PT_double, "R_rotor[pu*Ohm]", PADDR(Rr), PT_DESCRIPTION, "Induction generator primary rotor resistance in p.u.", PT_double, "X_rotor[pu*Ohm]", PADDR(Xr), PT_DESCRIPTION, "Induction generator primary rotor reactance in p.u.", PT_double, "R_core[pu*Ohm]", PADDR(Rc), PT_DESCRIPTION, "Induction generator primary core resistance in p.u.", PT_double, "X_magnetic[pu*Ohm]", PADDR(Xm), PT_DESCRIPTION, "Induction generator primary core reactance in p.u.", PT_double, "Max_Vrotor[pu*V]", PADDR(Max_Vrotor), PT_DESCRIPTION, "Induction generator maximum induced rotor voltage in p.u., e.g. 1.2", PT_double, "Min_Vrotor[pu*V]", PADDR(Min_Vrotor), PT_DESCRIPTION, "Induction generator minimum induced rotor voltage in p.u., e.g. 0.8", PT_double, "Rs[pu*Ohm]", PADDR(Rs), PT_DESCRIPTION, "Synchronous generator primary stator resistance in p.u.", PT_double, "Xs[pu*Ohm]", PADDR(Xs), PT_DESCRIPTION, "Synchronous generator primary stator reactance in p.u.", PT_double, "Rg[pu*Ohm]", PADDR(Rg), PT_DESCRIPTION, "Synchronous generator grounding resistance in p.u.", PT_double, "Xg[pu*Ohm]", PADDR(Xg), PT_DESCRIPTION, "Synchronous generator grounding reactance in p.u.", PT_double, "Max_Ef[pu*V]", PADDR(Max_Ef), PT_DESCRIPTION, "Synchronous generator maximum induced rotor voltage in p.u., e.g. 0.8", PT_double, "Min_Ef[pu*V]", PADDR(Min_Ef), PT_DESCRIPTION, "Synchronous generator minimum induced rotor voltage in p.u., e.g. 0.8", PT_double, "pf[pu]", PADDR(pf), PT_DESCRIPTION, "Desired power factor in CONSTANTP mode (can be modified over time)", PT_double, "power_factor[pu]", PADDR(pf), PT_DESCRIPTION, "Desired power factor in CONSTANTP mode (can be modified over time)", PT_complex, "voltage_A[V]", PADDR(voltage_A), PT_DESCRIPTION, "Terminal voltage on phase A", PT_complex, "voltage_B[V]", PADDR(voltage_B), PT_DESCRIPTION, "Terminal voltage on phase B", PT_complex, "voltage_C[V]", PADDR(voltage_C), PT_DESCRIPTION, "Terminal voltage on phase C", PT_complex, "current_A[A]", PADDR(current_A), PT_DESCRIPTION, "Calculated terminal current on phase A", PT_complex, "current_B[A]", PADDR(current_B), PT_DESCRIPTION, "Calculated terminal current on phase B", PT_complex, "current_C[A]", PADDR(current_C), PT_DESCRIPTION, "Calculated terminal current on phase C", PT_complex, "EfA[V]", PADDR(EfA), PT_DESCRIPTION, "Synchronous generator induced voltage on phase A", PT_complex, "EfB[V]", PADDR(EfB), PT_DESCRIPTION, "Synchronous generator induced voltage on phase B", PT_complex, "EfC[V]", PADDR(EfC), PT_DESCRIPTION, "Synchronous generator induced voltage on phase C", PT_complex, "Vrotor_A[V]", PADDR(Vrotor_A), PT_DESCRIPTION, "Induction generator induced voltage on phase A in p.u.",//Induction Generator PT_complex, "Vrotor_B[V]", PADDR(Vrotor_B), PT_DESCRIPTION, "Induction generator induced voltage on phase B in p.u.", PT_complex, "Vrotor_C[V]", PADDR(Vrotor_C), PT_DESCRIPTION, "Induction generator induced voltage on phase C in p.u.", PT_complex, "Irotor_A[V]", PADDR(Irotor_A), PT_DESCRIPTION, "Induction generator induced current on phase A in p.u.", PT_complex, "Irotor_B[V]", PADDR(Irotor_B), PT_DESCRIPTION, "Induction generator induced current on phase B in p.u.", PT_complex, "Irotor_C[V]", PADDR(Irotor_C), PT_DESCRIPTION, "Induction generator induced current on phase C in p.u.", PT_set, "phases", PADDR(phases), PT_DESCRIPTION, "Specifies which phases to connect to - currently not supported and assumes three-phase connection", PT_KEYWORD, "A",(set)PHASE_A, PT_KEYWORD, "B",(set)PHASE_B, PT_KEYWORD, "C",(set)PHASE_C, PT_KEYWORD, "N",(set)PHASE_N, PT_KEYWORD, "S",(set)PHASE_S, NULL)<1) GL_THROW("unable to publish properties in %s",__FILE__); } }
TIMESTAMP switch_object::sync(TIMESTAMP t0) { OBJECT *obj = OBJECTHDR(this); TIMESTAMP temp_time; unsigned char work_phases, work_phases_pre, work_phases_post, work_phases_closed; char fault_val[9]; int result_val, impl_fault; bool fault_mode; //Try to map the event_schedule function address, if we haven't tried yet if (event_schedule_map_attempt == false) { //First check to see if a fault_check object even exists if (fault_check_object != NULL) { //It exists, good start! - now see if the proper variable is populated! eventgen_obj = get_object(fault_check_object, "eventgen_object"); //See if it worked - if not, assume it doesn't exist if (*eventgen_obj != NULL) { //It's not null, map up the scheduler function event_schedule = (FUNCTIONADDR)(gl_get_function(*eventgen_obj,"add_event")); //Make sure it was found if (event_schedule == NULL) { gl_warning("Unable to map add_event function in eventgen:%s",*(*eventgen_obj)->name); /* TROUBLESHOOT While attempting to map the "add_event" function from an eventgen object, the function failed to be found. Ensure the target object in fault_check is an eventgen object and this function exists. If the error persists, please submit your code and a bug report via the trac website. */ } } //Defaulted elses - just leave things as is :( } //Defaulted else - doesn't exist, so leave function address empty //Flag the attempt as having occurred event_schedule_map_attempt = true; } //Update time variable if (prev_SW_time != t0) //New timestep prev_SW_time = t0; if ((solver_method == SM_NR) && (event_schedule != NULL)) //NR-reliability-related stuff { //Store our phases going in work_phases_pre = NR_branchdata[NR_branch_reference].phases & 0x07; //Call syncing function work_phases_post = switch_expected_sync_function(); //Store our phases going out work_phases_post &= 0x07; } else //Normal execution { //Call syncing function switch_sync_function(); //Set phases the same work_phases_pre = 0x00; work_phases_post = 0x00; } //Call overlying link sync TIMESTAMP t2=link_object::sync(t0); //See if we're in the proper cycle - NR only for now if ((solver_method == SM_NR) && (work_phases_pre != work_phases_post)) { //Find out what changed work_phases = (work_phases_pre ^ work_phases_post) & 0x07; //See if this transition is a "fault-open" or a "fault-close" work_phases_closed = work_phases & work_phases_post; //See how it looks if (work_phases_closed == work_phases) //It's a close { fault_mode = true; //work_phases = (~work_phases) & 0x07; } else //It's an open { fault_mode = false; //Work phases is already in the proper format } //Set up fault type fault_val[0] = 'S'; fault_val[1] = 'W'; fault_val[2] = '-'; //Default fault - none - will cause a failure if not caught impl_fault = -1; //Determine who opened and store the time switch (work_phases) { case 0x00: //No switches opened !?? GL_THROW("switch:%s supposedly opened, but doesn't register the right phases",obj->name); /* TROUBLESHOOT A switch reported changing to an open status. However, it did not appear to fully propogate this condition. Please try again. If the error persists, please submit your code and a bug report via the trac website. */ break; case 0x01: //Phase C action fault_val[3] = 'C'; fault_val[4] = '\0'; impl_fault = 20; break; case 0x02: //Phase B action fault_val[3] = 'B'; fault_val[4] = '\0'; impl_fault = 19; break; case 0x03: //Phase B and C action fault_val[3] = 'B'; fault_val[4] = 'C'; fault_val[5] = '\0'; impl_fault = 22; break; case 0x04: //Phase A action fault_val[3] = 'A'; fault_val[4] = '\0'; impl_fault = 18; break; case 0x05: //Phase A and C action fault_val[3] = 'A'; fault_val[4] = 'C'; fault_val[5] = '\0'; impl_fault = 23; break; case 0x06: //Phase A and B action fault_val[3] = 'A'; fault_val[4] = 'B'; fault_val[5] = '\0'; impl_fault = 21; break; case 0x07: //All three went fault_val[3] = 'A'; fault_val[4] = 'B'; fault_val[5] = 'C'; fault_val[6] = '\0'; impl_fault = 24; break; default: GL_THROW("switch:%s supposedly opened, but doesn't register the right phases",obj->name); //Defined above }//End switch if (event_schedule != NULL) //Function was mapped - go for it! { //Call the function if (fault_mode == true) //Restoration - make fail time in the past { if (mean_repair_time != 0) temp_time = 50 + (TIMESTAMP)(mean_repair_time); else temp_time = 50; //Call function result_val = ((int (*)(OBJECT *, OBJECT *, char *, TIMESTAMP, TIMESTAMP, int, bool))(*event_schedule))(*eventgen_obj,obj,fault_val,(t0-50),temp_time,impl_fault,fault_mode); } else //Failing - normal { result_val = ((int (*)(OBJECT *, OBJECT *, char *, TIMESTAMP, TIMESTAMP, int, bool))(*event_schedule))(*eventgen_obj,obj,fault_val,t0,TS_NEVER,-1,fault_mode); } //Make sure it worked if (result_val != 1) { GL_THROW("Attempt to change switch:%s failed in a reliability manner",obj->name); /* TROUBLESHOOT While attempting to propagate a changed switch's impacts, an error was encountered. Please try again. If the error persists, please submit your code and a bug report via the trac website. */ } //Ensure we don't go anywhere yet t2 = t0; } //End fault object present else //No object, just fail us out - save the iterations { gl_warning("No fault_check object present - Newton-Raphson solver may fail!"); /* TROUBLESHOOT A switch changed and created an open link. If the system is not meshed, the Newton-Raphson solver will likely fail. Theoretically, this should be a quick fail due to a singular matrix. However, the system occasionally gets stuck and will exhaust iteration cycles before continuing. If the fuse is blowing and the NR solver still iterates for a long time, this may be the case. */ } }//End NR call if (t2==TS_NEVER) return(t2); else return(-t2); //Soft limit it }
TIMESTAMP windturb_dg::sync(TIMESTAMP t0, TIMESTAMP t1) { TIMESTAMP t2 = TS_NEVER; //if ( *NR_mode == false ) //{ double Pwind, Pmech, detCp, F, G, gearbox_eff; double matCp[2][3]; store_last_current = current_A.Mag() + current_B.Mag() + current_C.Mag(); // convert press to Pascals and temp to Kelvins // TODO: convert this to using gl_convert air_dens = (*pPress*100) * Molar / (Ridealgas * ( (*pTemp - 32)*5/9 + 273.15)); //wind speed at height of hub - uses European Wind Atlas method WSadj = *pWS * log(turbine_height/roughness_l)/log(ref_height/roughness_l); double test = *pPress; /* TODO: import previous and future wind data and then pseudo-randomize the wind speed values beween 1st and 2nd WSadj = gl_pseudorandomvalue(RT_RAYLEIGH,&c,(WS1/sqrt(PI/2)));*/ Pwind = 0.5 * (air_dens) * PI * pow(blade_diam/2,2) * pow(WSadj,3); if (CP_Data == GENERAL_LARGE || CP_Data == GENERAL_MID || CP_Data == GENERAL_SMALL) { if (WSadj <= cut_in_ws) { Cp = 0; } else if (WSadj > cut_out_ws) { Cp = 0; } else if(WSadj > ws_rated) { Cp = Cp_rated * pow(ws_rated,3) / pow(WSadj,3); } else { if (WSadj == 0 || WSadj <= cut_in_ws || WSadj >= cut_out_ws) { Cp = 0; } else { matCp[0][0] = pow((ws_maxcp/cut_in_ws - 1),2); //Coeff of Performance found matCp[0][1] = pow((ws_maxcp/cut_in_ws - 1),3); //by using method described in [1] matCp[0][2] = 1; matCp[1][0] = pow((ws_maxcp/ws_rated - 1),2); matCp[1][1] = pow((ws_maxcp/ws_rated - 1),3); matCp[1][2] = 1 - Cp_rated/Cp_max; detCp = matCp[0][0]*matCp[1][1] - matCp[0][1]*matCp[1][0]; F = (matCp[0][2]*matCp[1][1] - matCp[0][1]*matCp[1][2])/detCp; G = (matCp[0][0]*matCp[1][2] - matCp[0][2]*matCp[1][0])/detCp; Cp = Cp_max*(1 - F*pow((ws_maxcp/WSadj - 1),2) - G*pow((ws_maxcp/WSadj - 1),3)); } } } else if (CP_Data == MANUF_TABLE) //Coefficient of Perfomance generated from Manufacturer's table { switch (Turbine_Model) { case VESTAS_V82: if (WSadj <= cut_in_ws || WSadj >= cut_out_ws) { Cp = 0; } else //TO DO: possibly replace polynomial with spline library function interpolation { //Uses a centered, 10th-degree polynomial Matlab interpolation of original Manuf. data double z = (WSadj - 10.5)/5.9161; //Original data [0 0 0 0 0.135 0.356 0.442 0.461 .458 .431 .397 .349 .293 .232 .186 .151 .125 .104 .087 .074 .064] from 4-20 m/s Cp = -0.08609*pow(z,10) + 0.078599*pow(z,9) + 0.50509*pow(z,8) - 0.45466*pow(z,7) - 0.94154*pow(z,6) + 0.77922*pow(z,5) + 0.59082*pow(z,4) - 0.23196*pow(z,3) - 0.25009*pow(z,2) - 0.24282*z + 0.37502; } break; default: GL_THROW("Coefficient of Performance model not determined."); } } else if (CP_Data == CALCULATED) { matCp[0][0] = pow((ws_maxcp/cut_in_ws - 1),2); //Coeff of Performance found matCp[0][1] = pow((ws_maxcp/cut_in_ws - 1),3); //by using method described in [1] matCp[0][2] = 1; matCp[1][0] = pow((ws_maxcp/ws_rated - 1),2); matCp[1][1] = pow((ws_maxcp/ws_rated - 1),3); matCp[1][2] = 1 - Cp_rated/Cp_max; detCp = matCp[0][0]*matCp[1][1] - matCp[0][1]*matCp[1][0]; F = (matCp[0][2]*matCp[1][1] - matCp[0][1]*matCp[1][2])/detCp; G = (matCp[0][0]*matCp[1][2] - matCp[0][2]*matCp[1][0])/detCp; Cp = Cp_max*(1 - F*pow((ws_maxcp/WSadj - 1),2) - G*pow((ws_maxcp/WSadj - 1),3)); } else { GL_THROW("CP_Data not defined."); } /// define user defined data , also catch for future cases Pmech = Pwind * Cp; if (Pmech != 0) { gearbox_eff = 1 - (q*.01*Rated_VA / Pmech); //Method described in [2]. if (gearbox_eff < .1) { gearbox_eff = .1; //Prevents efficiency from becoming negative at low power. } Pmech = Pwind * Cp * gearbox_eff; } Pconv = 1 * Pmech; //TODO: Assuming 0% losses due to friction and miscellaneous losses if (Gen_status==ONLINE) { int k; voltage_A = pCircuit_V[0]; //Syncs the meter parent to the generator. voltage_B = pCircuit_V[1]; voltage_C = pCircuit_V[2]; double Pconva = (voltage_A.Mag() / (voltage_A.Mag() + voltage_B.Mag() + voltage_C.Mag()))*Pconv; double Pconvb = (voltage_B.Mag() / (voltage_A.Mag() + voltage_B.Mag() + voltage_C.Mag()))*Pconv; double Pconvc = (voltage_C.Mag() / (voltage_A.Mag() + voltage_B.Mag() + voltage_C.Mag()))*Pconv; if (Gen_type == INDUCTION) //TO DO: Induction gen. Ef not working correctly yet. { Pconva = Pconva/Rated_VA; //induction generator solved in pu Pconvb = Pconvb/Rated_VA; Pconvc = Pconvc/Rated_VA; Vapu = voltage_A/(Rated_V/sqrt(3.0)); Vbpu = voltage_B/(Rated_V/sqrt(3.0)); Vcpu = voltage_C/(Rated_V/sqrt(3.0)); Vrotor_A = Vapu; Vrotor_B = Vbpu; Vrotor_C = Vcpu; complex detTPMat = IndTPMat[1][1]*IndTPMat[0][0] - IndTPMat[1][0]*IndTPMat[0][1]; if (Pconv > 0) { switch (Gen_mode) { case CONSTANTE: for(k = 0; k < 6; k++) //TODO: convert to a convergence { Irotor_A = (~((complex(Pconva,0)/Vrotor_A))); Irotor_B = (~((complex(Pconvb,0)/Vrotor_B))); Irotor_C = (~((complex(Pconvc,0)/Vrotor_C))); Iapu = IndTPMat[1][0]*Vrotor_A + IndTPMat[1][1]*Irotor_A; Ibpu = IndTPMat[1][0]*Vrotor_B + IndTPMat[1][1]*Irotor_B; Icpu = IndTPMat[1][0]*Vrotor_C + IndTPMat[1][1]*Irotor_C; Vrotor_A = complex(1,0)/detTPMat * (IndTPMat[1][1]*Vapu - IndTPMat[0][1]*Iapu); Vrotor_B = complex(1,0)/detTPMat * (IndTPMat[1][1]*Vbpu - IndTPMat[0][1]*Ibpu); Vrotor_C = complex(1,0)/detTPMat * (IndTPMat[1][1]*Vcpu - IndTPMat[0][1]*Icpu); Vrotor_A = Vrotor_A * Max_Vrotor / Vrotor_A.Mag(); Vrotor_B = Vrotor_B * Max_Vrotor / Vrotor_B.Mag(); Vrotor_C = Vrotor_C * Max_Vrotor / Vrotor_C.Mag(); } break; case CONSTANTPQ: double last_Ipu = 0; double current_Ipu = 1; unsigned int temp_ind = 1; while ( fabs( (last_Ipu-current_Ipu)/current_Ipu) > 0.005 ) { last_Ipu = current_Ipu; Irotor_A = -(~complex(-Pconva/Vrotor_A.Mag()*cos(Vrotor_A.Arg()),Pconva/Vrotor_A.Mag()*sin(Vrotor_A.Arg()))); Irotor_B = -(~complex(-Pconvb/Vrotor_B.Mag()*cos(Vrotor_B.Arg()),Pconvb/Vrotor_B.Mag()*sin(Vrotor_B.Arg()))); Irotor_C = -(~complex(-Pconvc/Vrotor_C.Mag()*cos(Vrotor_C.Arg()),Pconvc/Vrotor_C.Mag()*sin(Vrotor_C.Arg()))); Iapu = IndTPMat[1][0]*Vrotor_A - IndTPMat[1][1]*Irotor_A; Ibpu = IndTPMat[1][0]*Vrotor_B - IndTPMat[1][1]*Irotor_B; Icpu = IndTPMat[1][0]*Vrotor_C - IndTPMat[1][1]*Irotor_C; Vrotor_A = complex(1,0)/detTPMat * (IndTPMat[1][1]*Vapu - IndTPMat[0][1]*Iapu); Vrotor_B = complex(1,0)/detTPMat * (IndTPMat[1][1]*Vbpu - IndTPMat[0][1]*Ibpu); Vrotor_C = complex(1,0)/detTPMat * (IndTPMat[1][1]*Vcpu - IndTPMat[0][1]*Icpu); current_Ipu = Iapu.Mag() + Ibpu.Mag() + Icpu.Mag(); temp_ind += 1; if (temp_ind > 100) { OBJECT *obj = OBJECTHDR(this); gl_warning("windturb_dg (id:%d,name:%s): internal iteration limit reached, breaking out of loop. Injected current may not be solved sufficiently.",obj->id,obj->name); /* TROUBLESHOOT This may need some work. The generator models are solved iteratively by using the system voltage as the boundary condition. The current model iterates on solving the current injection, but then breaks out if not solved within 100 iterations. May indicate some issues with the model (i.e., voltage is incorrectly set on the connection node) or it may indicate poor programming. Please report if you see this message. */ break; } } break; } // convert current back out of p.u. current_A = Iapu * Rated_VA/(Rated_V/sqrt(3.0)); current_B = Ibpu * Rated_VA/(Rated_V/sqrt(3.0)); current_C = Icpu * Rated_VA/(Rated_V/sqrt(3.0)); } else // Generator is offline { current_A = 0; current_B = 0; current_C = 0; } } else if (Gen_type == SYNCHRONOUS) //synch gen is NOT solved in pu { //sg ef mode is not working yet double Mxef, Mnef, PoutA, PoutB, PoutC, QoutA, QoutB, QoutC; complex SoutA, SoutB, SoutC; complex lossesA, lossesB, lossesC; Mxef = Max_Ef * Rated_V/sqrt(3.0); Mnef = Min_Ef * Rated_V/sqrt(3.0); //TODO: convert to a convergence if (Gen_mode == CONSTANTE) //Ef is controllable to give a needed power output. { current_A = invAMx[0][0]*(voltage_A - EfA) + invAMx[0][1]*(voltage_B - EfB) + invAMx[0][2]*(voltage_C - EfC); current_B = invAMx[1][0]*(voltage_A - EfA) + invAMx[1][1]*(voltage_B - EfB) + invAMx[1][2]*(voltage_C - EfC); current_C = invAMx[2][0]*(voltage_A - EfA) + invAMx[2][1]*(voltage_B - EfB) + invAMx[2][2]*(voltage_C - EfC); SoutA = -voltage_A * (~(current_A)); //TO DO: unbalanced SoutB = -voltage_B * (~(current_B)); SoutC = -voltage_C * (~(current_C)); } //Gives a constant output power of real power converted Pout,then Qout is found through a controllable power factor. else if (Gen_mode == CONSTANTP) { //If air density increases, power extracted can be much greater than the default specifications - cap it. if (Pconva > 1.025*Max_P/3) { Pconva = 1.025*Max_P/3; } if (Pconvb > 1.025*Max_P/3) { Pconvb = 1.025*Max_P/3; } if (Pconvc > 1.025*Max_P/3) { Pconvc = 1.025*Max_P/3; } current_A = -(~(complex(Pconva,Pconva*tan(acos(pf)))/voltage_A)); current_B = -(~(complex(Pconvb,Pconvb*tan(acos(pf)))/voltage_B)); current_C = -(~(complex(Pconvc,Pconvc*tan(acos(pf)))/voltage_C)); if (Pconv > 0) { double last_current = 0; double current_current = current_A.Mag() + current_B.Mag() + current_C.Mag(); unsigned int temp_count = 1; while ( fabs( (last_current-current_current)/current_current) > 0.005 ) { last_current = current_current; PoutA = Pconva - current_A.Mag()*current_A.Mag()*(AMx[0][0] - AMx[0][1]).Re(); PoutB = Pconvb - current_B.Mag()*current_B.Mag()*(AMx[1][1] - AMx[0][1]).Re(); PoutC = Pconvc - current_C.Mag()*current_C.Mag()*(AMx[2][2] - AMx[0][1]).Re(); QoutA = pf/fabs(pf)*PoutA*sin(acos(pf)); QoutB = pf/fabs(pf)*PoutB*sin(acos(pf)); QoutC = pf/fabs(pf)*PoutC*sin(acos(pf)); current_A = -(~(complex(PoutA,QoutA)/voltage_A)); current_B = -(~(complex(PoutB,QoutB)/voltage_B)); current_C = -(~(complex(PoutC,QoutC)/voltage_C)); current_current = current_A.Mag() + current_B.Mag() + current_C.Mag(); temp_count += 1; if ( temp_count > 100 ) { OBJECT *obj = OBJECTHDR(this); gl_warning("windturb_dg (id:%d,name:%s): internal iteration limit reached, breaking out of loop. Injected current may not be solved sufficiently.",obj->id,obj->name); /* TROUBLESHOOT This may need some work. The generator models are solved iteratively by using the system voltage as the boundary condition. The current model iterates on solving the current injection, but then breaks out if not solved within 100 iterations. May indicate some issues with the model (i.e., voltage is incorrectly set on the connection node) or it may indicate poor programming. Please report if you see this message. */ break; } } gl_debug("windturb_dg iteration count = %d",temp_count); } else { current_A = 0; current_B = 0; current_C = 0; } EfA = voltage_A - (AMx[0][0] - AMx[0][1])*current_A - AMx[0][2]*(current_A + current_B + current_C); EfB = voltage_B - (AMx[1][1] - AMx[1][0])*current_A - AMx[1][2]*(current_A + current_B + current_C); EfC = voltage_C - (AMx[2][2] - AMx[2][0])*current_A - AMx[2][1]*(current_A + current_B + current_C); } else GL_THROW("Unknown generator mode"); } //sum up and finalize everything for output double PowerA, PowerB, PowerC, QA, QB, QC; PowerA = -voltage_A.Mag()*current_A.Mag()*cos(voltage_A.Arg() - current_A.Arg()); PowerB = -voltage_B.Mag()*current_B.Mag()*cos(voltage_B.Arg() - current_B.Arg()); PowerC = -voltage_C.Mag()*current_C.Mag()*cos(voltage_C.Arg() - current_C.Arg()); QA = -voltage_A.Mag()*current_A.Mag()*sin(voltage_A.Arg() - current_A.Arg()); QB = -voltage_B.Mag()*current_B.Mag()*sin(voltage_B.Arg() - current_B.Arg()); QC = -voltage_C.Mag()*current_C.Mag()*sin(voltage_C.Arg() - current_C.Arg()); power_A = complex(PowerA,QA); power_B = complex(PowerB,QB); power_C = complex(PowerC,QC); TotalRealPow = PowerA + PowerB + PowerC; TotalReacPow = QA + QB + QC; GenElecEff = TotalRealPow/Pconv * 100; Wind_Speed = WSadj; complex testCurrent; testCurrent = pLine_I[0]; complex *testCurrentPointer; testCurrentPointer = pLine_I; pLine_I[0] += current_A; pLine_I[1] += current_B; pLine_I[2] += current_C; } // Generator is offline else { current_A = 0; current_B = 0; current_C = 0; power_A = complex(0,0); power_B = complex(0,0); power_C = complex(0,0); } //} // Double check to make sure it is actually converged to a steady answer // Mostly applies to NR mode to make sure terminal voltage has converged enough // in NR to give a good boundary condition for solving the generator circuit double store_current_current = current_A.Mag() + current_B.Mag() + current_C.Mag(); if ( fabs((store_current_current - store_last_current) / store_last_current) > 0.005 ) t2 = t1; return t2; /* return t2>t1 on success, t2=t1 for retry, t2<t1 on failure */ }
EXPORT CLASS *init(CALLBACKS *fntable, MODULE *module, int argc, char *argv[]) { if (set_callback(fntable)==NULL) { errno = EINVAL; return NULL; } // open a connection to the Matlab engine int status=0; static char server[1024]; if (gl_global_getvar("matlab_server",server,sizeof(server))) matlab_server = server; if (strcmp(matlab_server,"standalone")==0) engine = engOpenSingleUse(NULL,NULL,&status); else engine = engOpen(matlab_server); if (engine==NULL) { gl_error("unable to start Matlab engine (code %d)",status); return NULL; } // prepare session char debug[8]; if (gl_global_getvar("debug",debug,sizeof(debug))) debugmode = (atoi(debug)==1); engSetVisible(engine,debugmode?1:0); engEvalString(engine,"clear all;"); char env[1024]; _snprintf(env,sizeof(env),"NEVER=%g;INVALID=%g;",TOSERIAL(TS_NEVER),TOSERIAL(TS_INVALID)); engEvalString(engine,env); // collect output from Matlab engOutputBuffer(engine,output,sizeof(output)); // setup the Matlab module and run the class constructor engEvalString(engine,"global passconfig;"); if (engEvalString(engine,argv[0])!=0) gl_error("unable to evaluate function '%s' in Matlab", argv[0]); else gl_matlab_output(); // read the pass configuration mxArray *pcfg= engGetVariable(engine,"passconfig"); if (pcfg && mxIsChar(pcfg)) { char passinfo[1024]; KEYWORD keys[] = { {"NOSYNC",PC_NOSYNC,keys+1}, {"PRETOPDOWN",PC_PRETOPDOWN,keys+2}, {"BOTTOMUP",PC_BOTTOMUP,keys+3}, {"POSTTOPDOWN",PC_POSTTOPDOWN,NULL}, }; PROPERTY pctype = {0,"passconfig",PT_set,1,PA_PUBLIC,NULL,&passconfig,NULL,keys,NULL}; set passdata; if (mxGetString(pcfg,passinfo,sizeof(passinfo))==0 && callback->convert.string_to_property(&pctype,&passdata,passinfo)>0) { passconfig = (PASSCONFIG)passdata; oclass=gl_register_class(module,argv[0],passconfig); if (oclass==NULL) gl_error("unable to register '%s' as a class",argv[0]); DELEGATEDTYPE *pDelegate = new DELEGATEDTYPE; pDelegate->oclass = oclass; strncpy(pDelegate->type,"matlab",sizeof(pDelegate->type)); pDelegate->from_string = object_from_string; pDelegate->to_string = object_to_string; if (gl_publish_variable(oclass,PT_delegated,pDelegate,"data",0,NULL)<1) GL_THROW("unable to publish properties in %s",__FILE__); } else gl_error("passconfig is invalid (expected set of NOSYNC, PRETOPDOWN, BOTTOMUP, and POSTTOPDOWN)", passinfo); } else gl_error("passconfig not specified"); // read the pass configuration mxArray *ans= engGetVariable(engine,"ans"); if (ans && mxIsStruct(ans)) { defaults = mxDuplicateArray(ans); // process the answer int nFields = mxGetNumberOfFields(ans), i; for (i=0; i<nFields; i++) { const char *name = mxGetFieldNameByNumber(ans,i); mxArray *data = mxGetFieldByNumber(ans,0,i); // @todo publish the structure } } else gl_error("result of call to matlab::%s did not return a structure", argv[0]); #ifdef OPTIONAL /* TODO: publish global variables (see class_define_map() for details) */ gl_global_create(char *name, ..., NULL); /* TODO: use gl_global_setvar, gl_global_getvar, and gl_global_find for access */ #endif /* always return the first class registered */ return oclass; }
triplex_node::triplex_node(MODULE *mod) : node(mod) { if(oclass == NULL) { pclass = node::oclass; oclass = gl_register_class(mod,"triplex_node",sizeof(triplex_node),PC_PRETOPDOWN|PC_BOTTOMUP|PC_POSTTOPDOWN|PC_UNSAFE_OVERRIDE_OMIT|PC_AUTOLOCK); if (oclass==NULL) throw "unable to register class triplex_node"; else oclass->trl = TRL_PROVEN; if(gl_publish_variable(oclass, PT_INHERIT, "powerflow_object", // this is critical to avoid publishing node's 3phase properties PT_enumeration, "bustype", PADDR(bustype),PT_DESCRIPTION,"defines whether the node is a PQ, PV, or SWING node", PT_KEYWORD, "PQ", (enumeration)PQ, PT_KEYWORD, "PV", (enumeration)PV, PT_KEYWORD, "SWING", (enumeration)SWING, PT_set, "busflags", PADDR(busflags),PT_DESCRIPTION,"flag indicates node has a source for voltage, i.e. connects to the swing node", PT_KEYWORD, "HASSOURCE", (set)NF_HASSOURCE, PT_object, "reference_bus", PADDR(reference_bus),PT_DESCRIPTION,"reference bus from which frequency is defined", PT_double,"maximum_voltage_error[V]",PADDR(maximum_voltage_error),PT_DESCRIPTION,"convergence voltage limit or convergence criteria", PT_complex, "voltage_1[V]", PADDR(voltage1),PT_DESCRIPTION,"bus voltage, phase 1 to ground", PT_complex, "voltage_2[V]", PADDR(voltage2),PT_DESCRIPTION,"bus voltage, phase 2 to ground", PT_complex, "voltage_N[V]", PADDR(voltageN),PT_DESCRIPTION,"bus voltage, phase N to ground", PT_complex, "voltage_12[V]", PADDR(voltage12),PT_DESCRIPTION,"bus voltage, phase 1 to 2", PT_complex, "voltage_1N[V]", PADDR(voltage1N),PT_DESCRIPTION,"bus voltage, phase 1 to N", PT_complex, "voltage_2N[V]", PADDR(voltage2N),PT_DESCRIPTION,"bus voltage, phase 2 to N", PT_complex, "current_1[A]", PADDR(current1),PT_DESCRIPTION,"constant current load on phase 1, also acts as accumulator", PT_complex, "current_2[A]", PADDR(current2),PT_DESCRIPTION,"constant current load on phase 2, also acts as accumulator", PT_complex, "current_N[A]", PADDR(currentN),PT_DESCRIPTION,"constant current load on phase N, also acts as accumulator", PT_double, "current_1_real[A]", PADDR(current1.Re()),PT_DESCRIPTION,"constant current load on phase 1, real", PT_double, "current_2_real[A]", PADDR(current2.Re()),PT_DESCRIPTION,"constant current load on phase 2, real", PT_double, "current_N_real[A]", PADDR(currentN.Re()),PT_DESCRIPTION,"constant current load on phase N, real", PT_double, "current_1_reac[A]", PADDR(current1.Im()),PT_DESCRIPTION,"constant current load on phase 1, imag", PT_double, "current_2_reac[A]", PADDR(current2.Im()),PT_DESCRIPTION,"constant current load on phase 2, imag", PT_double, "current_N_reac[A]", PADDR(currentN.Im()),PT_DESCRIPTION,"constant current load on phase N, imag", PT_complex, "current_12[A]", PADDR(current12),PT_DESCRIPTION,"constant current load on phase 1 to 2", PT_double, "current_12_real[A]", PADDR(current12.Re()),PT_DESCRIPTION,"constant current load on phase 1 to 2, real", PT_double, "current_12_reac[A]", PADDR(current12.Im()),PT_DESCRIPTION,"constant current load on phase 1 to 2, imag", PT_complex, "residential_nominal_current_1[A]", PADDR(nom_res_curr[0]),PT_DESCRIPTION,"posted current on phase 1 from a residential object, if attached", PT_complex, "residential_nominal_current_2[A]", PADDR(nom_res_curr[1]),PT_DESCRIPTION,"posted current on phase 2 from a residential object, if attached", PT_complex, "residential_nominal_current_12[A]", PADDR(nom_res_curr[2]),PT_DESCRIPTION,"posted current on phase 1 to 2 from a residential object, if attached", PT_double, "residential_nominal_current_1_real[A]", PADDR(nom_res_curr[0].Re()),PT_DESCRIPTION,"posted current on phase 1, real, from a residential object, if attached", PT_double, "residential_nominal_current_1_imag[A]", PADDR(nom_res_curr[0].Im()),PT_DESCRIPTION,"posted current on phase 1, imag, from a residential object, if attached", PT_double, "residential_nominal_current_2_real[A]", PADDR(nom_res_curr[1].Re()),PT_DESCRIPTION,"posted current on phase 2, real, from a residential object, if attached", PT_double, "residential_nominal_current_2_imag[A]", PADDR(nom_res_curr[1].Im()),PT_DESCRIPTION,"posted current on phase 2, imag, from a residential object, if attached", PT_double, "residential_nominal_current_12_real[A]", PADDR(nom_res_curr[2].Re()),PT_DESCRIPTION,"posted current on phase 1 to 2, real, from a residential object, if attached", PT_double, "residential_nominal_current_12_imag[A]", PADDR(nom_res_curr[2].Im()),PT_DESCRIPTION,"posted current on phase 1 to 2, imag, from a residential object, if attached", PT_complex, "power_1[VA]", PADDR(power1),PT_DESCRIPTION,"constant power on phase 1 (120V)", PT_complex, "power_2[VA]", PADDR(power2),PT_DESCRIPTION,"constant power on phase 2 (120V)", PT_complex, "power_12[VA]", PADDR(power12),PT_DESCRIPTION,"constant power on phase 1 to 2 (240V)", PT_double, "power_1_real[W]", PADDR(power1.Re()),PT_DESCRIPTION,"constant power on phase 1, real", PT_double, "power_2_real[W]", PADDR(power2.Re()),PT_DESCRIPTION,"constant power on phase 2, real", PT_double, "power_12_real[W]", PADDR(power12.Re()),PT_DESCRIPTION,"constant power on phase 1 to 2, real", PT_double, "power_1_reac[VAr]", PADDR(power1.Im()),PT_DESCRIPTION,"constant power on phase 1, imag", PT_double, "power_2_reac[VAr]", PADDR(power2.Im()),PT_DESCRIPTION,"constant power on phase 2, imag", PT_double, "power_12_reac[VAr]", PADDR(power12.Im()),PT_DESCRIPTION,"constant power on phase 1 to 2, imag", PT_complex, "shunt_1[S]", PADDR(pub_shunt[0]),PT_DESCRIPTION,"constant shunt impedance on phase 1", PT_complex, "shunt_2[S]", PADDR(pub_shunt[1]),PT_DESCRIPTION,"constant shunt impedance on phase 2", PT_complex, "shunt_12[S]", PADDR(pub_shunt[2]),PT_DESCRIPTION,"constant shunt impedance on phase 1 to 2", PT_complex, "impedance_1[Ohm]", PADDR(impedance[0]),PT_DESCRIPTION,"constant series impedance on phase 1", PT_complex, "impedance_2[Ohm]", PADDR(impedance[1]),PT_DESCRIPTION,"constant series impedance on phase 2", PT_complex, "impedance_12[Ohm]", PADDR(impedance[2]),PT_DESCRIPTION,"constant series impedance on phase 1 to 2", PT_double, "impedance_1_real[Ohm]", PADDR(impedance[0].Re()),PT_DESCRIPTION,"constant series impedance on phase 1, real", PT_double, "impedance_2_real[Ohm]", PADDR(impedance[1].Re()),PT_DESCRIPTION,"constant series impedance on phase 2, real", PT_double, "impedance_12_real[Ohm]", PADDR(impedance[2].Re()),PT_DESCRIPTION,"constant series impedance on phase 1 to 2, real", PT_double, "impedance_1_reac[Ohm]", PADDR(impedance[0].Im()),PT_DESCRIPTION,"constant series impedance on phase 1, imag", PT_double, "impedance_2_reac[Ohm]", PADDR(impedance[1].Im()),PT_DESCRIPTION,"constant series impedance on phase 2, imag", PT_double, "impedance_12_reac[Ohm]", PADDR(impedance[2].Im()),PT_DESCRIPTION,"constant series impedance on phase 1 to 2, imag", PT_bool, "house_present", PADDR(house_present),PT_DESCRIPTION,"boolean for detecting whether a house is attached, not an input", PT_enumeration, "service_status", PADDR(service_status),PT_DESCRIPTION,"In and out of service flag", PT_KEYWORD, "IN_SERVICE", (enumeration)ND_IN_SERVICE, PT_KEYWORD, "OUT_OF_SERVICE", (enumeration)ND_OUT_OF_SERVICE, PT_double, "service_status_double", PADDR(service_status_dbl),PT_DESCRIPTION,"In and out of service flag - type double - will indiscriminately override service_status - useful for schedules", PT_double, "previous_uptime[min]", PADDR(previous_uptime),PT_DESCRIPTION,"Previous time between disconnects of node in minutes", PT_double, "current_uptime[min]", PADDR(current_uptime),PT_DESCRIPTION,"Current time since last disconnect of node in minutes", PT_object, "topological_parent", PADDR(TopologicalParent),PT_DESCRIPTION,"topological parent as per GLM configuration", NULL) < 1) GL_THROW("unable to publish properties in %s",__FILE__); //Deltamode functions if (gl_publish_function(oclass, "delta_linkage_node", (FUNCTIONADDR)delta_linkage)==NULL) GL_THROW("Unable to publish triplex_node delta_linkage function"); if (gl_publish_function(oclass, "interupdate_pwr_object", (FUNCTIONADDR)interupdate_triplex_node)==NULL) GL_THROW("Unable to publish triplex_node deltamode function"); if (gl_publish_function(oclass, "delta_freq_pwr_object", (FUNCTIONADDR)delta_frequency_node)==NULL) GL_THROW("Unable to publish triplex_node deltamode function"); } }
irrigation_controller::irrigation_controller(MODULE *module){ if (oclass==NULL) { oclass = gl_register_class(module,"irrigation_controller",sizeof(irrigation_controller),PC_PRETOPDOWN|PC_BOTTOMUP|PC_POSTTOPDOWN|PC_AUTOLOCK); if (oclass==NULL) throw "unable to register class irrigation_controller"; else oclass->trl = TRL_QUALIFIED; if (gl_publish_variable(oclass, PT_int16, "insync", PADDR(insync),PT_DESCRIPTION, "0 not in sync, 1 in sync", PT_int16, "test", PADDR(test),PT_DESCRIPTION, " ", PT_object,"soil_sensor",PADDR(soil_sensor), PT_enumeration, "simple_mode", PADDR(simplemode), PT_KEYWORD, "IRRIGATION_LOAD", (enumeration)SM_IRRIGATION_LOAD, PT_KEYWORD, "NONE", (enumeration)SM_NONE, PT_KEYWORD, "HOUSE_HEAT", (enumeration)SM_HOUSE_HEAT, PT_KEYWORD, "HOUSE_COOL", (enumeration)SM_HOUSE_COOL, PT_KEYWORD, "HOUSE_PREHEAT", (enumeration)SM_HOUSE_PREHEAT, PT_KEYWORD, "HOUSE_PRECOOL", (enumeration)SM_HOUSE_PRECOOL, PT_KEYWORD, "WATERHEATER", (enumeration)SM_WATERHEATER, PT_KEYWORD, "DOUBLE_RAMP", (enumeration)SM_DOUBLE_RAMP, PT_enumeration, "bid_mode", PADDR(bidmode), PT_KEYWORD, "ON", (enumeration)BM_ON, PT_KEYWORD, "OFF", (enumeration)BM_OFF, PT_enumeration, "use_override", PADDR(use_override), PT_KEYWORD, "OFF", (enumeration)OU_OFF, PT_KEYWORD, "ON", (enumeration)OU_ON, PT_double, "ramp_low[degF]", PADDR(ramp_low), PT_DESCRIPTION, "the comfort response below the setpoint", PT_double, "ramp_high[degF]", PADDR(ramp_high), PT_DESCRIPTION, "the comfort response above the setpoint", PT_double, "range_low", PADDR(range_low), PT_DESCRIPTION, "the setpoint limit on the low side", PT_double, "range_high", PADDR(range_high), PT_DESCRIPTION, "the setpoint limit on the high side", PT_char32, "target", PADDR(target), PT_DESCRIPTION, "the observed property (e.g., air temperature)", PT_char32, "setpoint", PADDR(setpoint), PT_DESCRIPTION, "the controlled property (e.g., heating setpoint)", PT_char32, "demand", PADDR(demand), PT_DESCRIPTION, "the controlled load when on", PT_char32, "load", PADDR(load), PT_DESCRIPTION, "the current controlled load", PT_char32, "total", PADDR(total), PT_DESCRIPTION, "the uncontrolled load (if any)", PT_object, "market", PADDR(pMarket), PT_DESCRIPTION, "the market to bid into", PT_char32, "state", PADDR(state), PT_DESCRIPTION, "the state property of the controlled load", PT_char32, "avg_target", PADDR(avg_target), PT_char32, "std_target", PADDR(std_target), PT_double, "bid_price", PADDR(last_p), PT_ACCESS, PA_REFERENCE, PT_DESCRIPTION, "the bid price", PT_double, "bid_quantity", PADDR(last_q), PT_ACCESS, PA_REFERENCE, PT_DESCRIPTION, "the bid quantity", PT_double, "set_temp[degF]", PADDR(set_temp), PT_ACCESS, PA_REFERENCE, PT_DESCRIPTION, "the reset value", PT_double, "base_setpoint[degF]", PADDR(setpoint0), // new stuff PT_double, "market_price", PADDR(clear_price), PT_DESCRIPTION, "the current market clearing price seen by the irrigation_controller.", PT_double, "period[s]", PADDR(dPeriod), PT_DESCRIPTION, "interval of time between market clearings", PT_enumeration, "control_mode", PADDR(control_mode), PT_KEYWORD, "RAMP", (enumeration)CN_RAMP, PT_KEYWORD, "DOUBLE_RAMP", (enumeration)CN_DOUBLE_RAMP, PT_enumeration, "resolve_mode", PADDR(resolve_mode), PT_KEYWORD, "DEADBAND", (enumeration)RM_DEADBAND, PT_KEYWORD, "SLIDING", (enumeration)RM_SLIDING, PT_double, "slider_setting",PADDR(slider_setting), PT_double, "slider_setting_heat", PADDR(slider_setting_heat), PT_double, "slider_setting_cool", PADDR(slider_setting_cool), PT_char32, "override", PADDR(re_override), // double ramp PT_double, "heating_range_high[degF]", PADDR(heat_range_high), PT_double, "heating_range_low[degF]", PADDR(heat_range_low), PT_double, "heating_ramp_high", PADDR(heat_ramp_high), PT_double, "heating_ramp_low", PADDR(heat_ramp_low), PT_double, "cooling_range_high[degF]", PADDR(cool_range_high), PT_double, "cooling_range_low[degF]", PADDR(cool_range_low), PT_double, "cooling_ramp_high", PADDR(cool_ramp_high), PT_double, "cooling_ramp_low", PADDR(cool_ramp_low), PT_double, "heating_base_setpoint[degF]", PADDR(heating_setpoint0), PT_double, "cooling_base_setpoint[degF]", PADDR(cooling_setpoint0), PT_char32, "deadband", PADDR(deadband), PT_char32, "heating_setpoint", PADDR(heating_setpoint), PT_char32, "heating_demand", PADDR(heating_demand), PT_char32, "cooling_setpoint", PADDR(cooling_setpoint), PT_char32, "cooling_demand", PADDR(cooling_demand), PT_double, "sliding_time_delay[s]", PADDR(sliding_time_delay), PT_DESCRIPTION, "time interval desired for the sliding resolve mode to change from cooling or heating to off", PT_bool, "use_predictive_bidding", PADDR(use_predictive_bidding), // redefinitions PT_char32, "average_target", PADDR(avg_target), PT_char32, "standard_deviation_target", PADDR(std_target), #ifdef _DEBUG PT_enumeration, "current_mode", PADDR(thermostat_mode), PT_KEYWORD, "INVALID", (enumeration)TM_INVALID, PT_KEYWORD, "OFF", (enumeration)TM_OFF, PT_KEYWORD, "HEAT", (enumeration)TM_HEAT, PT_KEYWORD, "COOL", (enumeration)TM_COOL, PT_enumeration, "dominant_mode", PADDR(last_mode), PT_KEYWORD, "INVALID", (enumeration)TM_INVALID, PT_KEYWORD, "OFF", (enumeration)TM_OFF, PT_KEYWORD, "HEAT", (enumeration)TM_HEAT, PT_KEYWORD, "COOL", (enumeration)TM_COOL, PT_enumeration, "previous_mode", PADDR(previous_mode), PT_KEYWORD, "INVALID", TM_INVALID, PT_KEYWORD, "OFF", (enumeration)TM_OFF, PT_KEYWORD, "HEAT", (enumeration)TM_HEAT, PT_KEYWORD, "COOL", (enumeration)TM_COOL, PT_double, "heat_max", PADDR(heat_max), PT_double, "cool_min", PADDR(cool_min), #endif PT_int32, "bid_delay", PADDR(bid_delay), NULL)<1) GL_THROW("unable to publish properties in %s",__FILE__); memset(this,0,sizeof(irrigation_controller)); } }