TIMESTAMP dryer::presync(TIMESTAMP t0, TIMESTAMP t1){ if(start_time==0) { start_time = int32(t0); } switch(shape.type){ case MT_UNKNOWN: /* normal, undriven behavior. */ break; case MT_ANALOG: if(shape.params.analog.energy == 0.0){ GL_THROW("dryer does not support fixed energy shaping"); } else if (shape.params.analog.power == 0){ daily_dryer_demand = gl_get_loadshape_value(&shape) / 2.4449; } else { daily_dryer_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 ~ dryer demand X kW, limited by C + Q * h ~ Vdot proportional to power/time */ daily_dryer_demand = gl_get_loadshape_value(&shape) / 2.4449; } break; case MT_MODULATED: if(shape.params.modulated.pulsetype == MPT_TIME){ GL_THROW("Amplitude modulated dryer usage is nonsensical for residential dryers"); } else if(shape.params.modulated.pulsetype == MPT_POWER){ /* frequency modulated */ /* fixed-amplitude, varying length pulses at regular intervals. */ daily_dryer_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 ~ dryer demand X kW, limited by C + Q * h */ daily_dryer_demand = gl_get_loadshape_value(&shape) / 2.4449; } break; default: GL_THROW("dryer load shape has an unknown state!"); break; } return TS_NEVER; }
TIMESTAMP residential_enduse::sync(TIMESTAMP t0, TIMESTAMP t1) { OBJECT *obj = OBJECTHDR(this); gl_debug("%s shape load = %8g", obj->name, gl_get_loadshape_value(&shape)); if (load.voltage_factor>1.2 || load.voltage_factor<0.8) gl_verbose("%s voltage is out of normal +/- 20%% range of nominal (vf=%.2f)", obj->name, load.voltage_factor); /* TROUBLESHOOTING The voltage on the enduse circuit is outside the expected range for that enduse. This is usually caused by an impropely configure circuit (e.g., 110V on 220V or vice versa). Fix the circuit configuration for that enduse and try again. */ return shape.t2>t1 ? shape.t2 : TS_NEVER; }
/** 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); }
/** Initialize water heater model properties - randomized defaults for all published variables **/ int waterheater::init(OBJECT *parent) { OBJECT *hdr = OBJECTHDR(this); if(parent != NULL){ if((parent->flags & OF_INIT) != OF_INIT){ char objname[256]; gl_verbose("waterheater::init(): deferring initialization on %s", gl_name(parent, objname, 255)); return 2; // defer } } hdr->flags |= OF_SKIPSAFE; static double sTair = 74; static double sTout = 68; if(parent){ pTair = gl_get_double_by_name(parent, "air_temperature"); pTout = gl_get_double_by_name(parent, "outdoor_temperature"); } if(pTair == 0){ pTair = &sTair; gl_warning("waterheater parent lacks \'air_temperature\' property, using default"); } if(pTout == 0){ pTout = &sTout; gl_warning("waterheater parent lacks \'outside_temperature\' property, using default"); } /* sanity checks */ /* initialize water tank volume */ if(tank_volume <= 0.0){ // tank_volume = 5*floor((1.0/5.0)*gl_random_uniform(0.90, 1.10) * 50.0 * (pHouse->get_floor_area() /2000.0)); // [gal] if (tank_volume > 100.0) tank_volume = 100.0; else if (tank_volume < 20.0) tank_volume = 20.0; } else { if (tank_volume > 100.0 || tank_volume < 20.0){ gl_error("watertank volume of %f outside the volume bounds of 20 to 100 gallons.", tank_volume); /* TROUBLESHOOT All waterheaters must be set between 40 and 100 gallons. Most waterheaters are assumed to be 50 gallon tanks. */ } } if (tank_setpoint<90 || tank_setpoint>160) gl_error("watertank thermostat is set to %f and is outside the bounds of 90 to 160 degrees Fahrenheit (32.2 - 71.1 Celsius).", tank_setpoint); /* TROUBLESHOOT All waterheaters must be set between 90 degF and 160 degF. /* initialize water tank deadband */ if (thermostat_deadband>10 || thermostat_deadband < 0.0) GL_THROW("watertank deadband of %f is outside accepted bounds of 0 to 10 degrees (5.6 degC).", thermostat_deadband); // initial tank UA if (tank_UA <= 0.0) GL_THROW("Tank UA value is negative."); // Set heating element capacity if not provided by the user if (heating_element_capacity <= 0.0) { if (tank_volume >= 50) heating_element_capacity = 4.500; else { // Smaller tanks can be either 3200, 3500, or 4500... double randVal = gl_random_uniform(RNGSTATE,0,1); if (randVal < 0.33) heating_element_capacity = 3.200; else if (randVal < 0.67) heating_element_capacity = 3.500; else heating_element_capacity = 4.500; } } // set gas electric loads, if not provided by the user if(0 > gas_fan_power){ gas_fan_power = heating_element_capacity * 0.01; } if(0 > gas_standby_power){ gas_standby_power = 0.0; // some units consume 3-5W } // Other initial conditions if(Tw < Tinlet){ // uninit'ed temperature Tw = gl_random_uniform(RNGSTATE,tank_setpoint - thermostat_deadband, tank_setpoint + thermostat_deadband); } current_model = NONE; load_state = STABLE; // initial demand Tset_curtail = tank_setpoint - thermostat_deadband/2 - 10; // Allow T to drop only 10 degrees below lower cut-in T... // Setup derived characteristics... area = (pi * pow(tank_diameter,2))/4; height = tank_volume/GALPCF / area; Cw = tank_volume/GALPCF * RHOWATER * Cp; // [Btu/F] h = height; // initial water temperature if(h == 0){ // discharged Tlower = Tinlet; Tupper = Tinlet + TSTAT_PRECISION; } else { Tlower = Tinlet; } /* schedule checks */ switch(shape.type){ case MT_UNKNOWN: /* normal, undriven behavior. */ break; case MT_ANALOG: if(shape.params.analog.energy == 0.0){ GL_THROW("waterheater does not support fixed energy shaping"); /* TROUBLESHOOT Though it is possible to drive the water demand of a water heater, it is not possible to shape its power or energy draw. Its heater is either on or off, not in between. Change the load shape to not specify the power or energy and try again. */ } else if (shape.params.analog.power == 0){ /* power-driven ~ cheat with W/degF*gpm */ // double heat_per_gallon = RHOWATER * // lb/cf // CFPGAL * // lb/gal // CWATER * // BTU/degF / gal // KWBTUPH / // kW/gal // 1000.0; // W/gal water_demand = gl_get_loadshape_value(&shape) / 2.4449; } else { water_demand = gl_get_loadshape_value(&shape); /* unitless ~ drive gpm */ } break; case MT_PULSED: /* pulsed loadshapes "emit one or more pulses at random times s. t. the total energy is accumulated over the period of the loadshape". * pulsed loadshapes can either user time or kW values per pulse. */ if(shape.params.pulsed.pulsetype == MPT_TIME){ ; /* constant time pulse ~ consumes X gallons to drive heater for Y hours ~ but what's Vdot, what's t? */ } else if(shape.params.pulsed.pulsetype == MPT_POWER){ ; /* constant power pulse ~ draws water to consume X kW, limited by C + Q * h ~ Vdot proportional to power/time */ water_demand = gl_get_loadshape_value(&shape) / 2.4449; } break; case MT_MODULATED: if(shape.params.modulated.pulsetype == MPT_TIME){ GL_THROW("Amplitude modulated water usage is nonsensical for residential water heaters"); /* TROUBLESHOOT Though it is possible to put a constant, low-level water draw on a water heater, it is thoroughly counterintuitive to the normal usage of the waterheater. */ } else if(shape.params.modulated.pulsetype == MPT_POWER){ /* frequency modulated */ /* fixed-amplitude, varying length pulses at regular intervals. */ water_demand = gl_get_loadshape_value(&shape) / 2.4449; } break; case MT_QUEUED: if(shape.params.queued.pulsetype == MPT_TIME){ ; /* constant time pulse ~ consumes X gallons/minute to consume Y thermal energy */ } else if(shape.params.queued.pulsetype == MPT_POWER){ ; /* constant power pulse ~ draws water to consume X kW, limited by C + Q * h */ water_demand = gl_get_loadshape_value(&shape) / 2.4449; } break; default: GL_THROW("waterheater load shape has an unknown state!"); break; } return residential_enduse::init(parent); }
int dishwasher::init(OBJECT *parent) { // @todo This class has serious problems and should be deleted and started from scratch. Fuller 9/27/2013. OBJECT *hdr = OBJECTHDR(this); if(parent != NULL){ if((parent->flags & OF_INIT) != OF_INIT){ char objname[256]; gl_verbose("dishwasher::init(): deferring initialization on %s", gl_name(parent, objname, 255)); return 2; // defer } } int rv = 0; // default properties if (motor_power==0) motor_power = gl_random_uniform(&hdr->rng_state,150,350); if (heat_fraction==0) heat_fraction = 0.2; if (is_240) { load.config = EUC_IS220; if (stall_voltage==0) stall_voltage = 0.6*240; } else if (stall_voltage==0) stall_voltage = 0.6*120; if (trip_delay==0) trip_delay = 10; if (reset_delay==0) reset_delay = 60; count_motor_only = 0; count_motor_only1 =0; count_motor_only_25 = 0; count_coil_only = 0; count_control_only =0; count_control_only1 =0; count_motor_only_68 =0; pulse_interval[0] = 8; pulse_interval[1] = 18; pulse_interval[2] = 21; pulse_interval[3] = 28; pulse_interval[4] = 38; pulse_interval[5] = 50; pulse_interval[6] = 65; pulse_interval[7] = 118; pulse_interval[8] = 150; pulse_interval[9] = 220; pulse_interval[10] = 320; pulse_interval[11] = 355; pulse_interval[12] = 460; pulse_interval[13] = 580; pulse_interval[14] = 615; pulse_interval[15] = 780; pulse_interval[16] = 800; pulse_interval[17] = 950; pulse_interval[18] = 1150; if (coil_power[0]==-1) coil_power[0] = 5800; coil_power[0] = 10; coil_power[1] = 580; coil_power[2] = 695; coil_power[3] = 950; motor_power = 250; enduse_queue = 1; queue_min = 0; queue_max = 2; control_check1 = false; control_check2 = false; control_check3 = false; control_check4 = false; control_check5 = false; control_check6 = false; control_check7 = false; control_check8 = false; control_check9 = false; control_check10 = false; control_check11 = false; control_check12 = false; motor_only_check1 = false; motor_only_check2 = false; motor_only_check3 = false; motor_only_check4 = false; motor_only_check5 = false; motor_only_check6 = false; motor_only_check7 = false; motor_only_check8 = false; motor_only_check9 = false; motor_coil_only_check1 = false; motor_coil_only_check2 = false; heateddry_check1 = false; heateddry_check2 = false; coil_only_check1 = false; coil_only_check2 = false; coil_only_check3 = false; //Heateddry_option_check = false; hdr->flags |= OF_SKIPSAFE; load.power_factor = 0.95; load.breaker_amps = 30; actual_dishwasher_demand = 0; energy_needed = 0; rv = residential_enduse::init(parent); if (rv==SUCCESS) update_state(0.0); /* schedule checks */ switch(shape.type){ case MT_UNKNOWN: gl_warning("This device, %s, is considered very experimental and has not been validated.", get_name()); break; case MT_ANALOG: if(shape.params.analog.energy == 0.0){ GL_THROW("dishwasher does not support fixed energy shaping"); } else if (shape.params.analog.power == 0){ daily_dishwasher_demand = gl_get_loadshape_value(&shape) / 2.4449; } else { daily_dishwasher_demand = gl_get_loadshape_value(&shape); /* unitless ~ drive gpm */ } break; case MT_PULSED: /* pulsed loadshapes "emit one or more pulses at random times s. t. the total energy is accumulated over the period of the loadshape". * pulsed loadshapes can either user time or kW values per pulse. */ if(shape.params.pulsed.pulsetype == MPT_TIME){ ; /* constant time pulse ~ consumes X gallons to drive heater for Y hours ~ but what's Vdot, what's t? */ } else if(shape.params.pulsed.pulsetype == MPT_POWER){ ; /* constant power pulse ~ dishwasher demand X kW, limited by C + Q * h ~ Vdot proportional to power/time */ daily_dishwasher_demand = gl_get_loadshape_value(&shape) / 2.4449; } break; case MT_MODULATED: if(shape.params.modulated.pulsetype == MPT_TIME){ GL_THROW("Amplitude modulated dishwasher usage is nonsensical for residential dishwashers"); /* TROUBLESHOOT Though it is possible to put a constant, low-level dishwasher demand, it is thoroughly counterintuitive to the normal usage of the dishwasher. */ } else if(shape.params.modulated.pulsetype == MPT_POWER){ /* frequency modulated */ /* fixed-amplitude, varying length pulses at regular intervals. */ daily_dishwasher_demand = gl_get_loadshape_value(&shape) / 2.4449; } break; case MT_QUEUED: if(shape.params.queued.pulsetype == MPT_TIME){ ; /* constant time pulse ~ consumes X gallons/minute to consume Y thermal energy */ } else if(shape.params.queued.pulsetype == MPT_POWER){ ; /* constant power pulse ~ dishwasher demand X kW, limited by C + Q * h */ daily_dishwasher_demand = gl_get_loadshape_value(&shape) / 2.4449; } break; default: GL_THROW("dishwasher load shape has an unknown state!"); break; } return residential_enduse::init(parent); //} // must run before update_state() so that pCircuit can be set // return rv; }
/** 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); }
/** 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); }