Example #1
0
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);
}
Example #5
0
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;
}
Example #6
0
/** 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);
}
Example #7
0
/** 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);
}