Beispiel #1
0
TIMESTAMP house::sync_thermal(TIMESTAMP t1, double nHours){
	DATETIME tv;
	double t = 0.0;
	gl_localtime(t1, &tv);
	
	Tout = *pTout;

	Tsolar = get_Tsolar(tv.hour, tv.month, Tair, *pTout);
	solar_load = 0.0;

	for (int i = 1; i<9; i++)
	{
		solar_load += (gross_wall_area*window_wall_ratio/8.0) * glazing_shgc * pSolar[i];
	}
	double netHeatrate = /*hvac_rated_capacity +*/ tload.heatgain*BTUPHPW + solar_load;
	double Q1 = M_inv11*Tair + M_inv12*Tmaterials;
	double Q2 = M_inv21*Tair + M_inv22*Tmaterials;

	if (nHours > ROUNDOFF)
	{
		double q1 = exp(s1*nHours)*(Q1 + BB11*Tsolar/s1 + BB12*netHeatrate/s1) - BB11*Tsolar/s1 
			- BB12*netHeatrate/s1;
		double q2 = exp(s2*nHours)*(Q2 - BB11*Tsolar/s2 - BB12*netHeatrate/s2) + BB11*Tsolar/s2 
			+ BB12*netHeatrate/s2;

		Tair = q1*(s1-A22)/A21 + q2*(s2-A22)/A21;
		Tmaterials = q1 + q2;
	}
    else
        return TS_NEVER;

	// calculate constants for solving time "t" to reach Tevent
	const double W = (Q1 + (BB11*Tsolar)/s1 + BB12*netHeatrate/s1)*(s1-A22)/A21;
	const double X = (BB11*Tsolar/s1 + BB12*netHeatrate/s1)*(s1-A22)/A21;
	const double Y = (Q2 - (BB11*Tsolar)/s2 - BB12*netHeatrate/s2)*(s2-A22)/A21;
	const double Z = (BB11*Tsolar/s2 + BB12*netHeatrate/s2)*(s2-A22)/A21;
	// end new solution

	// determine next internal event temperature
	int n_solutions = 0;
	double Tevent;
	const double TcoolOn = cooling_setpoint+thermostat_deadband;
	const double TcoolOff = cooling_setpoint-thermostat_deadband;
	const double TheatOn = heating_setpoint-thermostat_deadband;
	const double TheatOff = heating_setpoint+thermostat_deadband;

	/* determine the temperature of the next event */
#define TPREC 0.01
	if (hvac_rated_capacity < 0.0)
		Tevent = TcoolOff;
	else if (hvac_rated_capacity > 0.0)
		Tevent = TheatOff;
	else if (Tair <= TheatOn+TPREC)
		Tevent = TheatOn;
	else if (Tair >= TcoolOn-TPREC)
		Tevent = TcoolOn;
	else
		return TS_NEVER;

#ifdef OLD_SOLVER
    if (nHours > TPREC)
		// int dual_decay_solve(double *ans, double prec, double start, double end, int f, double a, double n, double b, double m, double c)
		n_solutions = dual_decay_solve(&t,TPREC,0.0 ,nHours,W,s1,Y,s2,Z-X-Tevent);

	Tair = Tevent;

	if (n_solutions<0)
		gl_error("house: solver error");
	else if (n_solutions == 0)
		return TS_NEVER;
	else if (t == 0)
		t = 1.0/3600.0;  // one second
	return t1+(TIMESTAMP)(t*3600*TS_SECOND);
#else
	t =  e2solve(W,s1,Y,s2,Z-X-Tevent);
	    Tair = Tevent;

	if (isfinite(t))
    {
		return t1+(TIMESTAMP)(t*3600*TS_SECOND);
    }
	else
		return TS_NEVER;
#endif
}
Beispiel #2
0
/* Sync is called when the clock needs to advance on the bottom-up pass */
TIMESTAMP office::sync(TIMESTAMP t0, TIMESTAMP t1) 
{	
	/* load calculations */
	update_lighting(t0,t1);
	update_plugs(t0,t1);
	
	/* local aliases */
	const double &Tout = (*(zone.current.pTemperature));
	const double &Ua = (zone.design.exterior_ua);
	const double &Cm = (zone.design.interior_mass);
	const double &Um = (zone.design.interior_ua);
	double &Ti = (zone.current.air_temperature);
	double &dTi = (zone.current.temperature_change);
	double &Tm = (zone.current.mass_temperature);
	HCMODE &mode = (zone.hvac.mode);

	/* advance the thermal state of the building */

	const double dt1 = t0>0 ? (double)(t1-t0)*TS_SECOND : 0;
	if (dt1>0)
	{
		const double dt = dt1/3600; /* model operates in units of hours */

		/* calculate model update */
		if (c2!=0)
		{
			/* update temperatures */
			const double e1 = k1*exp(r1*dt);
			const double e2 = k2*exp(r2*dt);
			Ti = e1 + e2 + Teq;
			Tm = ((r1-c1)*e1 + (r2-c1)*e2 + c6)/c2 + Teq;

			if (warn_control)
			{
				/* check for air temperature excursion */
				if (Ti<warn_low_temp || Ti>warn_high_temp)
				{
					OBJECT *obj = OBJECTHDR(this);
					DATETIME dt0, dt1;
					gl_localtime(t0,&dt0);
					gl_localtime(t1,&dt1);
					char ts0[64], ts1[64];
					gl_warning("office:%d (%s) air temperature excursion (%.1f degF) at between %s and %s", 
						obj->id, obj->name?obj->name:"anonymous", Ti,
						gl_strtime(&dt0,ts0,sizeof(ts0))?ts0:"UNKNOWN", gl_strtime(&dt1,ts1,sizeof(ts1))?ts1:"UNKNOWN");
				}

				/* check for mass temperature excursion */
				if (Tm<warn_low_temp || Tm>warn_high_temp)
				{
					OBJECT *obj = OBJECTHDR(this);
					DATETIME dt0, dt1;
					gl_localtime(t0,&dt0);
					gl_localtime(t1,&dt1);
					char ts0[64], ts1[64];
					gl_warning("office:%d (%s) mass temperature excursion (%.1f degF) at between %s and %s", 
						obj->id, obj->name?obj->name:"anonymous", Tm,
						gl_strtime(&dt0,ts0,sizeof(ts0))?ts0:"UNKNOWN", gl_strtime(&dt1,ts1,sizeof(ts1))?ts1:"UNKNOWN");
				}
			}

			/* calculate the power consumption */
			zone.total.energy += zone.total.power * dt;
		}

		const double Ca = 0.2402 * 0.0735 * zone.design.floor_height * zone.design.floor_area;

		/* update enduses and get internal heat gains */
		Qi = zone.lights.enduse.heatgain + zone.plugs.enduse.heatgain;

		/* compute solar gains */
		Qs = 0; 
		int i;
		for (i=0; i<9; i++)
			Qs += zone.design.window_area[i] * zone.current.pSolar[i]/10;
		Qs *= 3.412;
		if (Qs<0)
			throw "solar gain is negative?!?";

		/* compute heating/cooling effect */
		Qh = update_hvac(); 

		if (Ca<=0)
			throw "Ca must be positive";
		if (Cm<=0)
			throw "Cm must be positive";

		// split gains to air and mass
		double f_air = 1.0; /* adjust the fraction of gains that goes to air vs mass */
		double Qa = Qh + f_air*(Qi + Qs);
		double Qm = (1-f_air)*(Qi + Qs);

		c1 = -(Ua + Um)/Ca;
		c2 = Um/Ca;
		c3 = (Qa + Tout*Ua)/Ca;
		c6 = Qm/Cm;
		c7 = Qa/Ca;
		double p1 = 1/c2;
		if (Cm<=0)
			throw "Cm must be positive";
		c4 = Um/Cm;
		c5 = -c4;
		if (c2<=0)
			throw "Um must be positive";
		double p2 = -(c5+c1)/c2;
		double p3 = c1*c5/c2 - c4;
		double p4 = -c3*c5/c2 + c6;
		if (p3==0)
			throw "Teq is not finite";
		Teq = p4/p3;

		/* compute solution roots */
		if (p1==0)
			throw "internal error (p1==0 -> Ca==0 which should have caught)";
		const double ra = 2*p1;
		const double rb = -p2/ra;
		const double rr = p2*p2-4*p1*p3;
		if (rr<0)
			throw "thermal solution does not exist";
		const double rc = sqrt(rr)/ra;
		r1 = rb+rc;
		r2 = rb-rc;
		if (r1>0 || r2>0)
			throw "thermal solution has runaway condition";
	
		/* compute next initial condition */
		dTi = c2*Tm + c1*Ti - (c1+c2)*Tout + c7;
		k1 = (r2*Ti - r2*Teq - dTi)/(r2-r1);
		k2 = (dTi - r1*k1)/r2;

		/* calculate power */
		zone.total.power = zone.lights.enduse.power + zone.plugs.enduse.power + zone.hvac.enduse.power;

		if (warn_control)
		{
			/* check for heating equipment sizing problem */
			if ((mode==HC_HEAT || mode==HC_AUX) && Teq<TheatOff)
			{
				OBJECT *obj = OBJECTHDR(this);
				DATETIME dt0, dt1;
				gl_localtime(t0,&dt0);
				gl_localtime(t1,&dt1);
				char ts0[64], ts1[64];
				gl_warning("office:%d (%s) %s heating undersized between %s and %s", 
					obj->id, obj->name?obj->name:"anonymous", mode==HC_HEAT?"primary":"auxiliary", 
					gl_strtime(&dt0,ts0,sizeof(ts0))?ts0:"UNKNOWN", gl_strtime(&dt1,ts1,sizeof(ts1))?ts1:"UNKNOWN");
			}

			/* check for cooling equipment sizing problem */
			else if (mode==HC_COOL && Teq>TcoolOff)
			{
				OBJECT *obj = OBJECTHDR(this);
				DATETIME dt0, dt1;
				gl_localtime(t0,&dt0);
				gl_localtime(t1,&dt1);
				char ts0[64], ts1[64];
				gl_warning("office:%d (%s) cooling undersized between %s and %s", 
					obj->id, obj->name?obj->name:"anonymous", mode==HC_COOL?"COOL":"ECON", 
					gl_strtime(&dt0,ts0,sizeof(ts0))?ts0:"UNKNOWN", gl_strtime(&dt1,ts1,sizeof(ts1))?ts1:"UNKNOWN");
			}

			/* check for economizer control problem */
			else if (mode==HC_ECON && Teq>TcoolOff)
			{
				OBJECT *obj = OBJECTHDR(this);
				DATETIME dt;
				gl_localtime(t1,&dt);
				char ts[64];
				gl_warning("office:%d (%s) insufficient economizer control at %s", 
					obj->id, obj->name?obj->name:"anonymous", gl_strtime(&dt,ts,sizeof(ts))?ts:"UNKNOWN");
			}
		}
	}

	/* determine the temperature of the next event */
	if (Tevent == Teq)
		return -(t1+(TIMESTAMP)(3600*TS_SECOND)); /* soft return not more than an hour */

	/* solve for the time to the next event */
	double dt2=(double)TS_NEVER;
	dt2 = e2solve(k1,r1,k2,r2,Teq-Tevent)*3600;
	if (isnan(dt2) || !isfinite(dt2) || dt2<0)
	{
		if (dTi==0)
			/* never more than an hour because of the occupancy schedule */
			return -(t1+(TIMESTAMP)(3600*TS_SECOND)); /* soft return */

		/* do not allow more than 1 degree/hour temperature change before solving again */
		dt2 = fabs(3600/dTi);
		if (dt2>3600)
			dt2 = 3600; /* never more than an hour because of the occupancy schedule */
		return -(t1+(TIMESTAMP)(dt2*TS_SECOND)); /* soft return */
	}
	if (dt2<TS_SECOND)
		return t1+1; /* need to do a second pass to get next state */
	else
		return t1+(TIMESTAMP)(dt2*TS_SECOND); /* return t2>t1 on success, t2=t1 for retry, t2<t1 on failure */
}