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;
}
Exemple #3
0
/* 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;
}
Exemple #4
0
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;
}
Exemple #5
0
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
}
Exemple #6
0
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);
	}
}
Exemple #7
0
// 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__);
		}
}
Exemple #8
0
//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
}
Exemple #9
0
/* 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 */
    }
}
Exemple #10
0
//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;
}
Exemple #11
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);
}
Exemple #12
0
/**  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__);
	}
}
Exemple #13
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);
}
// 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);
}
Exemple #18
0
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;
}
Exemple #20
0
/**
* 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;
}
Exemple #22
0
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 */
	}
}
Exemple #24
0
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
}
Exemple #26
0
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 */	
}
Exemple #27
0
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));
	}
}