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 — 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—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 Grizzly (Version 2.3)
- 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 Hassayampa (Version 3.0)
- 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:

- 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. - The
*step by*syntax (using /) is not supported. - The special keywords (e.g., @hourly, @daily) are not supported.
- 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:valuekWh"; } object scaled-power { myshape "type: analog; schedule:schedule-name; power:valuekW"; } 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*←

*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:valuekW; stdeverrorW"; }

## 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:valuekWh; count:value; duration:values"; }

or

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

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*←

*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:valuekW; stdeverrorW"; }

## 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:valuekWh; count:value; period:values"; }

or

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

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*←

*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:valuekW; stdeverrorW"; }

## 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:valuekWh; count:value; duration:values; q_on:value; q_off:value"; }

or

class example { loadshape myshape; } object sample { myshape "type: pulsed; schedule:schedule-name; energy:valuekWh; count:value; power:valuekW; 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*←

*value*+

*stdev** Triangle[-3,3]

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

schedule-name; power:valuekW; stdeverrorW"; }

## Scheduled loadshapes

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

- The definition syntax of the schedule is simpler and more intuitive.
- 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<mean~stdev<max

If the min or the max are omitted, then 3 σ 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 whichloadis 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)

[math]energy = power \times dt[/math]

- 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)

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

[math]heatgain = power_{real} \times heatgain\_fraction[/math]

- 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 — 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 `residential_enduse::create()`

. In this case, the total power is calculated based on the load and the enduse fraction, e.g.,

*total _{r}* =

*load*(

*f*(

_{V}*f*+

_{V}Z_{f}*I*) +

_{f}*P*)

_{f}*total*= Sgn(

_{i}*pf*)

*total*(

_{r}*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 `load.shape`

null.

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

*total _{r}* =

*f*(

_{V}*f*+

_{V}Z_{r}*I*) +

_{r}*P*)

_{r}*total*=

_{i}*f*(

_{V}*f*+

_{V}Z_{i}*I*) +

_{i}*P*)

_{i}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

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

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