package uk.ac.imperial.simelec;
import cern.jet.random.Normal;
import cern.jet.random.Uniform;
/**
* Describes an electrical appliance.
*
* @author James Keirstead
*
*/
public class Appliance extends Load {
// Member fields
public int power;
String use_profile;
private double ownership_rate;
int standby_power;
private int mean_power;
private int rated_power;
double cycles_per_year;
private int cycle_length;
private int restart_delay;
double calibration;
private boolean owned = false;
private int cycle_time_left = 0;
int restart_delay_time_left = 0;
/**
* Creates a new appliance with specified attributes. The constructor uses
* this information to set a random start delay for restarting appliances
* and a random active power demand based on the specified mean.
*
*
* @param name
* a String giving the plain text name
* @param profile
* a String describing the use profile
* @param ownership
* a double giving the ownership probability
* @param standby
* an int giving the standby power demand in Watts
* @param mean
* an int giving the mean power demand in Watts
* @param cycles
* a double giving the average number of duty cycles per year
* @param length
* an int giving the length of an average cycle in minutes
* @param restart
* an int giving the delay between cycles in minutes
* @param calibration
* a double giving a calibration constant. See
* <link>http://dx.doi.org/10.1016/S0378-7788(02)00241-4</link>
*/
public Appliance(String name, String profile, double ownership,
int standby, int mean, double cycles, int length, int restart,
double calibration) {
this.id = name.toUpperCase();
this.use_profile = profile.toUpperCase();
this.ownership_rate = ownership;
// this.total_energy = energy;
this.standby_power = standby;
this.mean_power = mean;
this.cycles_per_year = cycles;
this.cycle_length = length;
this.restart_delay = restart;
this.calibration = calibration;
// Randomly delay the start of appliances that have a restart
// delay
this.setRestartDelay();
// Make the rated power variable over a normal distribution to
// provide some variation
this.setRatedPower();
}
/**
* Sets the rated power of this Appliance. Assumes that the true rated power
* is normally distributed about the stated mean power for the appliance
* type.
*/
private void setRatedPower() {
rated_power = (int) Normal
.staticNextDouble(mean_power, mean_power / 10);
}
/**
* Sets the restart delay for this Appliance. Assumes that the delay is
* uniformly distributed with an expected value equal to the stated restart
* delay.
*/
private void setRestartDelay() {
restart_delay_time_left = (int) Uniform.staticNextDouble()
* restart_delay * 2;
}
/**
* Starts this Appliance. Calling this method will calculate a new cycle
* length, reset the restart delay, and calculate the
*/
public void start() {
// Determine how long this appliance is going to be on for
cycle_time_left = calculateCycleLength();
// Determine if this appliance has a delay after the cycle before it
// can restart
restart_delay_time_left = this.restart_delay;
// Set the power
this.power = getPowerUsage(cycle_time_left);
// Decrement the cycle time left
cycle_time_left--;
}
/**
* A convenience function to test if a value is within specified limits.
*
* @param x
* an int giving the value to test
* @param lower
* an int giving the lower limit
* @param upper
* an int giving the upper limit
* @return a boolean <code>true</code> if x>=lower AND x<=upper
*/
private static boolean isBetween(int x, int lower, int upper) {
return lower <= x && x <= upper;
}
/**
* Gets the power consumed by this Appliance at a certain point in its cycle
*
* @param cycle_time_left
* an int giving the number of minutes left in the Appliance's
* cycle
* @return an int giving the power consumption in Watts at this time
*/
public int getPowerUsage(int cycle_time_left) {
// Set the working power to the rated power
int tmp_power = this.rated_power;
// Some appliances have a custom (variable) power profile depending
// on the time left
if (this.id.equals("WASHING_MACHINE")
|| this.id.equals("WASHER_DRYER")) {
int total_cycle_time = 0;
// Calculate the washing cycle time
if (this.id.equals("WASHING_MACHINE"))
total_cycle_time = 138;
if (this.id.equals("WASHER_DRYER"))
total_cycle_time = 198;
// This is an example power profile for an example washing
// machine. This simplistic model is based upon data from personal
// communication with a major washing machine manufacturer
int tmp = total_cycle_time - cycle_time_left + 1;
if (isBetween(tmp, 1, 8)) {
tmp_power = 73; // start-up and fill
} else if (isBetween(tmp, 9, 29)) {
tmp_power = 2056; // heating
} else if (isBetween(tmp, 30, 81)) {
tmp_power = 73; // Wash and drain
} else if (isBetween(tmp, 82, 92)) {
tmp_power = 73; // spin
} else if (isBetween(tmp, 93, 94)) {
tmp_power = 250; // rinse
} else if (isBetween(tmp, 95, 105)) {
tmp_power = 73; // Spin
} else if (isBetween(tmp, 106, 107)) {
tmp_power = 250; // rinse
} else if (isBetween(tmp, 108, 118)) {
tmp_power = 73; // Spin
} else if (isBetween(tmp, 119, 120)) {
tmp_power = 250; // rinse
} else if (isBetween(tmp, 121, 131)) {
tmp_power = 73; // Spin
} else if (isBetween(tmp, 132, 133)) {
tmp_power = 250; // rinse
} else if (isBetween(tmp, 134, 138)) {
tmp_power = 568; // fast spin
} else if (isBetween(tmp, 139, 198)) {
tmp_power = 2500; // Drying cycle
} else {
tmp_power = this.standby_power;
}
}
return (tmp_power);
}
/**
* Calculate the length of this Appliance's operating cycle. The duration
* (in minutes) is calculated as follows:
* <ul>
* <li>For televisions, an average viewing time of 73 minutes is assumed
* from Time Use Survey data with some random variation.</li>
* <li>For storage and electric space heaters, the cycle time is assumed to
* be normally distributed based on the specified cycle length</li>
* <li>For all other appliances, the specified cycle length is assumed with
* no random variation.</li>
* </ul>
*
* @return an int giving the operating cycle length in minutes
*/
private int calculateCycleLength() {
// Set the value to that provided in the configuration
int length = this.cycle_length;
// Use the TV watching length data approximation, derived from the
// TUS data
if ((this.id.equals("TV1")) || (this.id.equals("TV2"))
|| (this.id.equals("TV3"))) {
// The cycle length is approximated by the following function
// The average viewing time is approximately 73 minutes
length = (int) Math.round(70 * Math.pow(
(0 - Math.log10(1 - Uniform.staticNextDouble())), 1.1));
} else if ((this.id.equals("STORAGE_HEATER"))
|| (this.id.equals("ELEC_SPACE_HEATING"))) {
// Provide some variation on the cycle length of heating
// appliances
length = (int) Normal.staticNextDouble(this.cycle_length,
this.cycle_length / 10);
}
return length;
}
/**
* Assigns ownership of this Appliance. Draws a random number and if this is
* less than the average ownership rate specified in the constructor, then
* this Appliance is deemed to be owned.
*/
public void assignOwnership() {
double rnd = Uniform.staticNextDouble();
owned = (rnd < this.ownership_rate);
}
/**
* Is this Appliance owned?
*
* @return a boolean stating whether or not this appliance is owned.
*/
public boolean isOwned() {
return (this.owned);
}
/**
* Runs the appliance. In other words, sets the power demand for the current
* point in the cycle and step the timer.
*
*/
public void run() {
// Set the power
power = getPowerUsage(cycle_time_left);
// Decrement the cycle time left
cycle_time_left--;
}
/**
* Checks if this Appliance is off.
*
* @return a boolean <code>true</code> if the current cycle time remaining
* is less than or equal to zero.
*/
public boolean isOff() {
return (cycle_time_left <= 0);
}
/**
* Is this Appliance awaiting restart? In other words, is the value of the
* restart delay current greater than zero?
*
* @return a boolean <code>true</code> if appliance is still awaiting
* restart delay to finish
*/
public boolean awaitingRestart() {
return (restart_delay_time_left > 0);
}
}