Built in schedules and loadshapes

Modelers can create schedules for built-in loadshapes using the schedule directive. Each schedule item creates a reusable named schedule that can be referenced later by built-in loadshapes. A schedule can either be a binary or analog series of values that vary according to time. The format for schedules is highly analogous to the crontab format used to schedule jobs in Unix and Linux (see Wikipedia for cron).

= Schedules =

The schedule directive can contain either a simple schedule, such as

schedule officehours { * 8-17 * * 1-5 # M-F 8a to 5p }

or a complex schedule with multiple blocks, such as

schedule officehours { weekdays { * 8-16 * * 1-5 # Monday through Friday, 8am to 5pm }   weekends { * 9-11,13-15 * * 6 # Saturdays, 9am-noon and 1pm to 4pm } }

If you want to provide values for each time interval, they can be listed after the time specification, such as

schedule tou_price { * 21-8 * * 1-5 35 # weekdays 9pm-9am, $35 * 9-20 * * 1-5 135 # weekdays 9am-9pm, $135 * * * * 6-0 35 # weekends, $35 }

Omitted values on schedule items take on the default value of 1. Omitted times in the schedule take on the default value of 0.


 * Limitations : There may be no more than 4 blocks, and each block may not contain more than 63 distinct non-zero values. Although you may have more than 64 schedule entries in a block, the number of distinct values cannot exceed 64, and zero is always a value.  If you have defined more than 63 distinct values, you can either limit the resolution over the dynamic range, or you can use a orphaned player (i.e., having no parent) as a source instead.

Normalization
Some schedules need to be normalized before they are used, depending on the application (e.g., loadshapes). When normalization takes place it is done separately over each block. The (optionally weighted) sum of the values given within the block is the normalization coefficient &mdash; each value in the block is divided by the sum of all the values in the block. Some applications may need the signed sum and others may use the sum of the absolute values.

When weighting is used it is based on the fraction of minutes over which the value applies with respect to the total minutes over which the block applies. Only minutes that are explicitly listed in the block are count&mdash;omitted times (which are associated with the value 0.0) are ignored. If you wish to have the value 0 counted in the weighting, you must include the times for which it applies as well.

Normalization is controlled using the following options


 * normal : enables normalization so that all values in each block are divided by the sum of the values in the block.
 * absolute : normalization sums uses absolute values instead signed values. Its inclusion implies normal.
 * weighted : normalization sums uses time-weighted values instead of simple values. Its inclusion implies normal.

Normalization options should be provided in-line, such as

schedule demand { weighted; * 21-8 * * 1-5 1.2 # weekdays 9pm-9am, weeknights * 9-20 * * 1-5 1.5 # weekdays 9am-9pm, weekdays * * * * 6-0   0.8 # weekends, weekends }

which enables normalization using time-weighted values.

Schedule transforms
Any double property can be driven from a schedule using a schedule transform. Schedule transforms are linear functions that convert a schedule-driven value to a property of an object. A schedule transform can be define by simply describing the function when the property is set, as in

schedule occupancy_schedule { * 8-16 * * 1-5 }  object small_building { n_occupants 10*occupancy_schedule; } object large_building { n_occupancy 100*occupancy_schedule+1; }

The value of the property is updated before the presync pass, immediately after the schedules are updated.


 * As of : Any property with an underlying numeric value may be driven by a transform.  This includes double, complex, bool, int16, int32, int64, set, enumeration, timestamp, loadshape, and enduse.  In cases where the underlying data type is compatible, the value will be cast according to the C/C++ type-casting rules.


 * As of : A transform may also link a variable to an external function defined using the extern directive.

Caveats
There are some notable differences from the cron syntax:


 * 1) The alternate use of day and weekday is not supported.  If both day and weekday are not *, they are considered as day AND weekday rather than OR.
 * 2) The step by syntax (using /) is not supported.
 * 3) The special keywords (e.g., @hourly, @daily) are not supported.
 * 4) The weekday 7 refers to holidays, which can occur any day of the week.  Holidays are not supported yet, but will be someday.

= Load shapes =

The loadshape property is a finite-state machine that takes on the value of a complex power when synchronized. A loadshape is associated with a schedule, and requires a number of parameters to define its behavior. There are 4 types of loadshape, and each has a difference set of parameters that define it.

Analog loadshapes


Analog loadshapes directly compute the power from the values in the schedule. An analog loadshape is defined using the following terms:

class example { loadshape myshape; } object fixed-energy { myshape "type: analog; schedule: schedule-name; energy: value kWh"; } object scaled-power { myshape "type: analog; schedule: schedule-name; power: value kW"; } object unscaled { myshape "type: analog; schedule: schedule-name"; }

The schedule parameter specifies which schedule is to be used. When the energy is given, the schedule is used to create a shape that consumes the specified energy in each schedule block. The power required is based on the fraction of energy allocated to each time interval.

When the power scale is given, the scheduled value is multiplied by the power.

When neither energy nor power is given, the schedule value is used directly as the power.

A standard deviation on the energy or power value can be given, in which case each instance of the loadshape that is generated will use an error drawn from the triangle distribution from -3 to +3, such that

value &larr; value + stdev * Triangle[-3,3]

The stdev term can be given units and it will be scaled accordingly, e.g.,

object stdev-power { myshape "type: analog; schedule: schedule-name; power: value kW; stdev error W"; }

Pulsed loadshapes


Pulsed loadshapes emit 1 or more pulsed at random times such that the total energy is accumulated over the period of the loadshape. A pulsed loadshape is defined using the following terms:

class example { loadshape myshape; } object sample { myshape "type: pulsed; schedule: schedule-name; energy: value kWh; count: value; duration: value s"; } or

class example { loadshape myshape; } object sample { myshape "type: pulsed; schedule: schedule-name; energy: value kWh; count: value; power: value kW"; }

The first form define a series of pulses with constant duration, and the second form defines a series of pulses with constant power. When the duration is constant, the power will vary in response to changes in voltage such that the amount of energy used during a loadshape block is as specified. When the power is constant, the duration will vary in response to changes in voltage such that the amount of energy used is constant. Only one of the two may be specified, and at least one must be specified.

The count parameter determine how many pulses will be generated during a loadshape block. The value is optional and the default value is 1.0.

A standard deviation on the duration or power value can be given, in which case each instance of the loadshape that is generated will use an error drawn from the triangle distribution from -3 to +3, such that

value &larr; value + stdev * Triangle[-3,3]

The stdev term can be given units and it will be scaled accordingly, e.g.,

object stdev-power { myshape "type: analog; schedule: schedule-name; power: value kW; stdev error W"; }

Modulated loadshapes


Modulated loadshapes emit a continuous sequence of modulated pulses with either constant period and duty-cycle(amplitude), or constant power and off-time (pulsewidth), or constant power and on-time (frequency). A modulated loadshape is defined using the following terms:

class example { loadshape myshape; } object sample { myshape "type: modulated; modulation: modulation; schedule: schedule-name; energy: value kWh; count: value; period: value s"; }

or

class example { loadshape myshape; } object sample { myshape "type: modulated; modulation: modulation; schedule: schedule-name; energy: value kWh; count: value; power: value kW"; }

A standard deviation on the duration or power value can be given, in which case each instance of the loadshape that is generated will use an error drawn from the triangle distribution from -3 to +3, such that

value &larr; value + stdev * Triangle[-3,3]

The stdev term can be given units and it will be scaled accordingly, e.g.,

object stdev-power { myshape "type: analog; schedule: schedule-name; power: value kW; stdev error W"; }

Queued loadshapes


Queued loadshapes emit random pulses whenever a queue accrued from the loadshape reaches an on threshold and continues emitting pulses until the queue reaches an off threshold. A queued loadshape is defined using the following terms:

class example { loadshape myshape; } object sample { myshape "type: pulsed; schedule: schedule-name; energy: value kWh; count: value; duration: value s; q_on: value; q_off: value"; }

or

class example { loadshape myshape; } object sample { myshape "type: pulsed; schedule: schedule-name; energy: value kWh; count: value; power: value kW; q_on: value; q_off: value"; }

The values of q_on and q_off are in the same units as the integrals of the normalized loadshape and q_on must be greater than q_off.

A standard deviation on the duration or power value can be given, in which case each instance of the loadshape that is generated will use an error drawn from the triangle distribution from -3 to +3, such that

value &larr; value + stdev * Triangle[-3,3]

The stdev term can be given units and it will be scaled accordingly, e.g.,

object stdev-power { myshape "type: analog; schedule: schedule-name; power: value kW; stdev error W"; }

Scheduled loadshapes
A schedule-based loadshape syntax available from Version 2.1 on has a number of important advantages.


 * 1) The definition syntax of the schedule is simpler and more intuitive.
 * 2) The definition incorporates diversity characteristics that are common in populations of buildings.

For example

class example { loadshape myshape; } object sample { myshape "type: scheduled; weekdays: MTWRF; on-time: 6<8~1<10; off-time: 15<16~1<18; on-ramp: 0.5<1~0.5<1.5; off-ramp: 1<2~1<3; low: 1<2~1<3high: 10<15~2<20 kW;  }

will generate a randomized ramped 8-hour pulse at roughly 10 kW Monday through Friday. Weekdays are defined as
 * U=sunday,
 * M=monday,
 * T=tuesday,
 * W=wednesday,
 * R=thursday,
 * F=friday,
 * S=saturday, and
 * H=holiday.

Values are provided in the format

min&lt;mean~stdev&lt;max

If the min or the max are omitted, then 3 &sigma; is used. If the stdev is omitted, then 0 is used (meaning the value is invariant).

The syntax for varying values (mean~stdev) allows the same definition to be used for multiple objects, e.g.,

#define SCHEDULE_1="weekdays: MTWRF; on-time: 8~1; off-time: 16~1; on-ramp: 1~0.5; off-ramp: 2~1;" object sample { myshape "type: scheduled; SCHEDULE_1; power: 15~2 kW;  }

= Reading loads =

Loadshapes publish their characteristics when output through a string (say to XML, GLM, or a stream) so to read the actual load at any given time it is necessary to use an alias to access the end-use load. Currently, aliases are not supported in runtime classes, but a module may publish the load alias using the following form

...,  PT_loadshape, "plugshape", &plugs, PT_double, "plugload[kW]", &(plugs.load), ...

= Enduse data =

Objects should use load data as a part of enduse properties. The enduse property encapsulates information about how a load is converted to a power, accumulated to an energy and heat gain. The structure is

typedef struct s_enduse { loadshape *shape; /* reference to the loadshape from which load is read set config; /* enduse configuration (i.e., IS220) */ complex total; /* total power (sum of        complex admittance; /* constant impedance portion of load in kW) */ complex current; /* constant current portion of load in kW) */       complex power; /* value of power in kW */        complex energy; /* accumulated energy in kWh */        complex demand; /* peak power observed (can be periodically reset) */        double impedance_fraction; /* fraction of load that is constant impedance */        double current_fraction; /* fraction of load that is constant current */        double power_fraction; /* fraction of load that is constant power */        double power_factor; /* power factor of load */        complex voltage_factor; /* voltage in pu */        double heatgain; /* internal heat from load */        double heatgain_fraction; /* fraction of power that goes to internal heat */ } enduse;

End-use power fractions are update differently depending on whether the end-use is driven from a loadshape or driven externally.

When driven from a loadshape, the power, current, and impedance fraction are updated first, along with the total power and they are used to calculate the constant power, current, and admittance portion of the total power.

When driven externally, the constant power, current, and admittance portion of the total power are update first, and the fractions and total power are calculated from them.

When enduse_recalc is called, the following calculations are made, depending on the update requested. When a presync update is requested the energy is calculated as a function of the power and time given, i.e.,

enduse_recalc(int PC_PRESYNC, double dt)

$$energy = power \times dt$$


 * Important : The presync update is done automatically by the main executive and should not be called explicitly by the object's presync implementation, if any.

When a sync update is requested the power is calculated as a function load, and the heat is updated as a function of power, i.e.,

enduse_recalc(int PC_SYNC)

$$power = set\_power\_factor(shape_{load},power\_factor) \left  ( power\_fraction + \frac{current\_fraction}{voltage} + \frac{impedance\_fraction}{voltage^2} \right ) $$

$$heatgain = power_{real} \times heatgain\_fraction$$


 * Important : The enduse sync update is not performed automatically and must be called by the sync operation of the object that implements the enduse. This is done using the gl_enduse_sync call:

TIMESTAMP myclass::sync(TIMESTAMP t0, TIMESTAMP t1) {    TIMESTAMP t2 = TS_NEVER; /* ... my calcs before enduse sync */ /* synchronize my enduse property */ TIMESTAMP t3 = gl_enduse_sync(myenduse,t1); if (t3<t2) t2=t3; /* power and heatgain are now set based on loadshape */ /* continue with calcs ... */    return t2; }

The limitation is for performance reasons &mdash; there is at present no efficient way to determine which enduse blocks belong to which objects and consequently the sync operation in the core cannot know when each block needs to be updated. Furthermore, depending on the implementation of the object's sync function, the synchronization of the enduse block may need to occur in the middle of the object sync. For these reasons, the enduse sync must be called explicitly during the object sync at the appropriate time.

= Module integration =

There are two ways to use schedules, loadshapes, and enduses in modules. The first is to directly drive the enduse from the loadshape. This is done by including the statement

load.shape = &shape;

which in the case of residential enduses is done in. In this case, the total power is calculated based on the load and the enduse fraction, e.g.,

totalr = load ( fV ( fV Zf + If ) + Pf )  totali = Sgn(pf) totalr (pf-2 -1)1/2

The second way separates the loadshape from the enduse so that a model of how the load converts to power can be explicitly given. In this case, the above statement is omitted, leaving  null.

Now you must provide the ZIP components as actual real and reactive quantities and the total power is computed, e.g.,

totalr = fV ( fV Zr + Ir ) + Pr )  totali = fV ( fV Zi + Ii ) +  Pi )

In addition, the enduse load is named using a statement like,

load.name = oclass->name;

so that the convention of naming the enduse the same as the class that implements it is observed.

= Caveats =

Modules that implement defaults in the style commonly used in Version 1 of GridLAB-D will find that this is no longer possible. The following changes are required to use loadshapes and enduses


 * 1) You can rely on object_create_single to both clear memory and properly create and initialize both loadshape properties and enduse properties.


 * 1) All other object property defaults must be done in create.