TIMESTAMP refrigerator::presync(TIMESTAMP t0, TIMESTAMP t1){
	OBJECT *hdr = OBJECTHDR(this);
	double t = 0.0, dt = 0.0;
	double nHours = (gl_tohours(t1)- gl_tohours(t0))/TS_SECOND;

	Tout = *pTout;

	if(nHours > 0 && t0 > 0){ /* skip this on TS_INIT */
		const double COP = COPcoef*((-3.5/45)*(Tout-70)+4.5); /* come from ??? */

		if(t1 == next_time){
			/* lazy skip-ahead */
			load.heatgain = (-((Tair - Tout) * exp(-(UAr+UAf)/Cf) + Tout - Tair) * Cf + Qr * COP) * KWPBTUPH;
			Tair = Tevent;
		} else {
			/* run calculations */
			const double C1 = Cf/(UAr+UAf);
			const double C2 = Tout - Qr/UAr;
			load.heatgain = (-((Tair - Tout) * exp(-(UAr+UAf)/Cf) + Tout - Tair) * Cf  + Qr * COP) * KWPBTUPH;;
			Tair = (Tair-C2)*exp(-nHours/C1)+C2;
		}
		if (Tair < 32 || Tair > 55)
			throw "refrigerator air temperature out of control";
		last_time = t1;
	}

	return TS_NEVER;
}
Esempio n. 2
0
TIMESTAMP house::sync(TIMESTAMP t0, TIMESTAMP t1)
{
	OBJECT *obj = OBJECTHDR(this);
	double nHours = (gl_tohours(t1)- gl_tohours(t0));
	//load.energy += load.total * nHours;
	/* TIMESTAMP sync_time = */ sync_hvac_load(t1, nHours);

	// sync circuit panel
	TIMESTAMP panel_time = sync_panel(t0,t1);
	TIMESTAMP sync_time = sync_thermal(t1, nHours);

	if (panel_time < sync_time)
		sync_time = panel_time;

	/// @todo check panel main breaker (residential, medium priority) (ticket #140)
	return sync_time;
	
}
Esempio n. 3
0
TIMESTAMP office::update_plugs(TIMESTAMP t0, TIMESTAMP t1)
{
	//power calculation
	zone.plugs.enduse.power.SetPowerFactor(zone.plugs.capacity * 
		zone.plugs.fraction, zone.plugs.enduse.power_factor, J);
	// energy calculation
	if (t0>0 && t1>t0)
		zone.plugs.enduse.energy += zone.plugs.enduse.power * gl_tohours(t1-t0);
	// heatgain calculation 
	zone.plugs.enduse.heatgain = zone.plugs.enduse.power.Mag() *
		zone.plugs.enduse.heatgain_fraction;
	return TS_NEVER;
}
TIMESTAMP refrigerator::sync(TIMESTAMP t0, TIMESTAMP t1) 
{
	double nHours = (gl_tohours(t1)- gl_tohours(t0))/TS_SECOND;
	double t = 0.0, dt = 0.0;

	const double COP = COPcoef*((-3.5/45)*(Tout-70)+4.5);

	// change control mode if appropriate
	if(motor_state == S_ON){
		Qr = rated_capacity;
	} else if(motor_state == S_OFF){
		Qr = 0;
	} else{
		throw "refrigerator motor state is ambiguous";
	}

	// calculate power from motor state
	load.power = Qr * KWPBTUPH * COP;

	// compute constants
	const double C1 = Cf/(UAr+UAf);
	const double C2 = Tout - Qr/UAr;
	
	// compute time to next internal event
	dt = t = -log((Tevent - C2)/(Tair-C2))*C1;

	if(t == 0){
		GL_THROW("refrigerator control logic error, dt = 0");
	} else if(t < 0){
		GL_THROW("refrigerator control logic error, dt < 0");
	}

	TIMESTAMP t2 = gl_enduse_sync(&(residential_enduse::load),t1);

	// if fridge is undersized or time exceeds balance of time or external event pending
	next_time = (TIMESTAMP)(t1 +  (t > 0 ? t : -t) * (3600.0/TS_SECOND) + 1);
	return next_time > TS_NEVER ? TS_NEVER : -next_time;
}
Esempio n. 5
0
TIMESTAMP house::presync(TIMESTAMP t0, TIMESTAMP t1) 
{
	if (t0>0 && t1>t0)
		load.energy += load.total.Mag() * gl_tohours(t1-t0);

	// reset accumulators for the next sync
	/* HVAC accumulators */
	load.heatgain = 0;
	load.total = complex(0,0,J);
	load.admittance = complex(0,0,J);
	load.current = complex(0,0,J);

	/* main panel accumulators */
	tload.heatgain = 0;
	tload.total = complex(0,0,J);
	tload.admittance = complex(0,0,J);
	tload.current = complex(0,0,J);

	return TS_NEVER;
}
/** Water heater synchronization determines the time to next
	synchronization state and the power drawn since last synch
 **/
TIMESTAMP waterheater::sync(TIMESTAMP t0, TIMESTAMP t1) 
{
	double internal_gain = 0.0;
	double nHours = (gl_tohours(t1) - gl_tohours(t0))/TS_SECOND;
	double Tamb = get_Tambient(location);

	// use re_override to control heat_needed state
	// runs after thermostat() but before "the usual" calculations
	if(re_override == OV_ON){
		heat_needed = TRUE;
	} else if(re_override == OV_OFF){
		heat_needed = FALSE;
	}

	if(Tw > 212.0 - thermostat_deadband){ // if it's trying boil, turn it off!
		heat_needed = FALSE;
		is_waterheater_on = 0;
	}


	TIMESTAMP t2 = residential_enduse::sync(t0,t1);
	
	// Now find our current temperatures and boundary height...
	// And compute the time to the next transition...
	//Adjusted because shapers go on sync, not presync

	set_time_to_transition();

	// determine internal gains
	if (location == INSIDE){
		if(this->current_model == ONENODE){
			internal_gain = tank_UA * (Tw - get_Tambient(location));
		} else if(this->current_model == TWONODE){
			internal_gain = tank_UA * (Tw - Tamb) * h / height;
			internal_gain += tank_UA * (Tlower - Tamb) * (1 - h / height);
		}
	} else {
		internal_gain = 0;
	}

	// determine the power used
	if (heat_needed == TRUE){
		/* power_kw */ load.total = (heat_mode == GASHEAT ? gas_fan_power : heating_element_capacity);
		is_waterheater_on = 1;
	} else {
		/* power_kw */ load.total = (heat_mode == GASHEAT ? gas_standby_power : 0.0);
		is_waterheater_on = 0;
	}

	//load.total = load.power = /* power_kw */ load.power;
	load.power = load.total * load.power_fraction;
	load.admittance = load.total * load.impedance_fraction;
	load.current = load.total * load.current_fraction;
	load.heatgain = internal_gain;

	waterheater_actual_power = load.power + (load.current + load.admittance * load.voltage_factor )* load.voltage_factor;
	actual_load = waterheater_actual_power.Re();

	if (actual_load != 0.0)
	{
		prev_load = actual_load;
		power_state = PS_ON;
	}
	else
		power_state = PS_OFF;

//	gl_enduse_sync(&(residential_enduse::load),t1);

	if(re_override == OV_NORMAL){
		if (time_to_transition >= (1.0/3600.0))	// 0.0167 represents one second
		{
			TIMESTAMP t_to_trans = (TIMESTAMP)(t1+time_to_transition*3600.0/TS_SECOND);
			return -(t_to_trans); // negative means soft transition
		}
		// less than one second means never
		else
			return TS_NEVER; 
	} else {
		return TS_NEVER; // keep running until the forced state ends
	}
}
/** 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);
}
Esempio n. 8
0
TIMESTAMP house::sync_panel(TIMESTAMP t0, TIMESTAMP t1)
{
	TIMESTAMP sync_time = TS_NEVER;
	OBJECT *obj = OBJECTHDR(this);

	// clear accumulators for panel currents
	complex I[3]; I[X12] = I[X23] = I[X13] = complex(0,0);

	// clear heatgain accumulator
	double heatgain = 0;

	// gather load power and compute current for each circuit
	CIRCUIT *c;
	for (c=panel.circuits; c!=NULL; c=c->next)
	{
		// get circuit type
		int n = (int)c->type;
		if (n<0 || n>2)
			GL_THROW("%s:%d circuit %d has an invalid circuit type (%d)", obj->oclass->name, obj->id, c->id, (int)c->type);
		/*	TROUBLESHOOT
			Invalid circuit types are an internal error for the house panel.  Please report this error.  The likely causes
			include an object that is not a house is being processed by the house model, or the panel was not correctly
			initialized.
		*/

		// if breaker is open and reclose time has arrived
		if (c->status==BRK_OPEN && t1>=c->reclose)
		{
			c->status = BRK_CLOSED;
			c->reclose = TS_NEVER;
			sync_time = t1; // must immediately reevaluate devices affected
			gl_debug("house:%d panel breaker %d closed", obj->id, c->id);
		}

		// if breaker is closed
		if (c->status==BRK_CLOSED)
		{
			// compute circuit current
			if ((c->pV)->Mag() == 0)
			{
				gl_debug("house:%d circuit %d (enduse %s) voltage is zero", obj->id, c->id, c->pLoad->name);
				break;
			}
			
			complex current = ~(c->pLoad->total*1000 / *(c->pV)); 

			// check breaker
			if (c->max_amps>0 && current.Mag()>c->max_amps)
			{
				// probability of breaker failure increases over time
				if (c->tripsleft>0 && gl_random_bernoulli(RNGSTATE,1/(c->tripsleft--))==0)
				{
					// breaker opens
					c->status = BRK_OPEN;

					// average five minutes before reclosing, exponentially distributed
					c->reclose = t1 + (TIMESTAMP)(gl_random_exponential(RNGSTATE,1/300.0)*TS_SECOND); 
					gl_debug("house:%d circuit breaker %d tripped - enduse %s overload at %.0f A", obj->id, c->id,
						c->pLoad->name, current.Mag());
				}

				// breaker fails from too frequent operation
				else
				{
					c->status = BRK_FAULT;
					c->reclose = TS_NEVER;
					gl_debug("house:%d circuit breaker %d failed", obj->id, c->id);
				}

				// must immediately reevaluate everything
				sync_time = t1; 
			}

			// add to panel current
			else
			{
				tload.power += c->pLoad->power;	// reminder: |a| + |b| != |a+b|
				tload.current += c->pLoad->current;
				tload.admittance += c->pLoad->admittance; // should this be additive? I don't buy t.a = c->pL->a ... -MH
				tload.total += c->pLoad->total;
				tload.heatgain += c->pLoad->heatgain;
				tload.energy += c->pLoad->power * gl_tohours(t1-t0);
				I[n] += current;
				c->reclose = TS_NEVER;
			}
		}

		// sync time
		if (sync_time > c->reclose)
			sync_time = c->reclose;
	}

	// compute line currents and post to meter
	if (obj->parent != NULL)
		LOCK_OBJECT(obj->parent);

	pLine_I[0] = I[X13];
	pLine_I[1] = I[X23];
	pLine_I[2] = 0;
	*pLine12 = I[X12];

	if (obj->parent != NULL)
		UNLOCK_OBJECT(obj->parent);

	return sync_time;
}
Esempio n. 9
0
/** oven synchronization determines the time to next
	synchronization state and the power drawn since last synch
 **/
TIMESTAMP range::sync(TIMESTAMP t0, TIMESTAMP t1) 
{
	double internal_gain = 0.0;
	double nHours = (gl_tohours(t1) - gl_tohours(t0))/TS_SECOND;
	double Tamb = get_Tambient(location);
	double dt = gl_toseconds(t0>0?t1-t0:0);

	if (oven_check == true || remainon == true)	
	time_oven_operation +=dt;

	if (remainon == false) 
	time_oven_operation=0;

	enduse_queue_oven += enduse_demand_oven * dt/3600/24;
	

			if (t0>TS_ZERO && t1>t0)
		{
			// compute the total energy usage in this interval
			load.energy += load.total * dt/3600.0;
		}		

	if(re_override == OV_ON){
		heat_needed = TRUE;
	} else if(re_override == OV_OFF){
		heat_needed = FALSE;
	}

	if(Tw > 212.0 - thermostat_deadband){ // if it's trying boil, turn it off!
		heat_needed = FALSE;
		is_range_on = 0;
	}
	// determine the power used
	if (heat_needed == TRUE){
		/* power_kw */ load.total = heating_element_capacity * (heat_mode == GASHEAT ? 0.01 : 1.0);
		is_range_on = 1;
	} else {
		/* power_kw */ load.total = 0.0;
		is_range_on = 0;
	}

	TIMESTAMP t2 = residential_enduse::sync(t0,t1);
	
	set_time_to_transition();

	if (location == INSIDE){
		if(this->current_model == ONENODE){
			internal_gain = oven_UA * (Tw - get_Tambient(location));
		} 

	} else {
		internal_gain = 0;
	}

	dt = update_state(dt, t1);

	

	//load.total = load.power = /* power_kw */ load.power;
	load.power = load.total * load.power_fraction;
	load.admittance = load.total * load.impedance_fraction;
	load.current = load.total * load.current_fraction;
	load.heatgain = internal_gain;

	range_actual_power = load.power + (load.current + load.admittance * load.voltage_factor )* load.voltage_factor;
	actual_load = range_actual_power.Re();
	if (heat_needed == true)
	total_power_oven = actual_load;
	else
	total_power_oven =0;

	if (actual_load != 0.0)
	{
		prev_load = actual_load;
		power_state = PS_ON;
	}
	else
		power_state = PS_OFF;

//	gl_enduse_sync(&(residential_enduse::load),t1);

	if(re_override == OV_NORMAL){
		if (time_to_transition < dt)
		{
			if (time_to_transition >= (1.0/3600.0))	// 0.0167 represents one second
			{
				TIMESTAMP t_to_trans = (t1+time_to_transition*3600.0/TS_SECOND);
				return -(t_to_trans); // negative means soft transition
			}
			// less than one second means never
			else
				return TS_NEVER; 
		}
		else
			return (TIMESTAMP)(t1+dt);
	} else {
		return TS_NEVER; // keep running until the forced state ends
	}


}
Esempio n. 10
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);
}