Device Modeling
TODO - Consider for PNNL report or tutorial or tutorial video or some other in-depth treatment.
This page is for those that are implementing a new device model. Definitionally, since this requires editing source code in GridLAB-D™, this is a developer task. But, this documentation recognizes that there may be modelers that are sneaking over into this developer section and thus those that are reading this may be more comfortable as device or power system domain experts than as programmers or traditional developers. Thus, this section needs to be as approachable as possible for those that are coming into the GridLAB-D™ code base without a lot of skill or experience in developing GridLAB-D™.
Device Model Parameter Declarations
Parameritization Design
Device models all have parameters that allow modelers to create custom instances of a device to include in their model. Often these parameters relate to the physical characteristics of the device but, for more complex devices, these parameters may be device control parameters. Correctly parameterizing a model is essential to allowing it to be flexible enough for it to be used in a wide variety of analysis efforts. Similarly, designing the parameters to be as independent as possible and clearly documenting when they are not is essential to allow modelers to use the model correctly.
Because GridLAB-D™ has a flat parameter list, it is not unusual for some device model parameters to only be used by the model if other parameters are set to certain values. For example, in the "house_e" model used to model single-zone structures, there are parameters for the heating_setpoint and cooling_setpoint. As you might be able to guess, when the HVAC system is in heating mode, changing the value of the cooling_setpoint has no impact on the devices simulated behavior. As much as possible, avoid this kind of dependent parameterization.
Similarly, as much as possible avoid a parameritization where some parameters effectively override others, either universally or based on parameter values. This creates confusion as the modeler thinks they are specifying the model in a particular way but the underlying model code negates some of the specified values. When doing so is unavoidable, document this parameter complexity and write code to evaluate when parameters are being overriden and post warning messages so the modeler can be made aware of the inconsistency.
Parameter Validation
A suprisingly large amount of model code should be devoted to parameter value verification. In many cases, parameter values have physically limited ranges that can be checked and if the value provided by the modeler is out of range, the value may be coerced to a non-invalid one and/or a warning or error message should be posted to allow the user to correct the parameter value.
For exammple, one of the parameters in the "house_e" model is the square footage of the house. This value must be greater than zero for much of the modeling of the thermodynamics of the house to work out mathematically. Furthermore, very small (e.g. 10) or very large values (e.g. 1,000,000,000) for this parameter are almost certainly not what the modeler intends. In the former case posting an error message and halting execution is probably appropriate while in the later case an error or warning message would be appropriate. In both cases, the posted warning or error should be clear about what parameter is causing the problem and what the expected or reasonable (i.e. non-message producing) range for the parameter value should be.
Parameter types
Access to variables and properties may be restricted using these access control flags. The underlying access privileges are
- PT_N: Only the creating module (or core) can access the property.
- PT_R: The property may be read.
- PT_W: The property may be modified.
- PT_S: The property may be saved to output.
- PT_L: The property may be loaded from input.
- PT_H: The property is hidden (not searchable).
PA_PUBLIC
Public properties can be read, modified, saved, and loaded.
PA_REFERENCE
Reference properties can be read, saved, and loaded.
PA_PROTECTED
Protected properties can be read.
PA_PRIVATE
Private properties can be saved and loaded.
PA_HIDDEN
Hidden property are public but will not be found by searches.
Device Modeling Methods
Refer to the "GridLAB-D™ Object Synchronization Process" page heavily here. Add details about using the synchronization methods as necessary.
Generic Device Model Class Template
I would propose we pull this from here and give this its own page that has inline comments that briefly summarize each method and refers developers back to these documentation pages for additional details.
class _class-name_ {
public _GLM-type_ _property-name_Units;
protected _GLM-type_ _reference-name_Units;
private _C-type_ _private-name_ ;
intrinsic create (object parent) {
// ...
return SUCCESS; // or FAILED
};
intrinsic init (object parent) {
// ...
return SUCCESS; // or FAILED
};
intrinsic isa (char* _class-name_) {
// ...
return true; // or false
};
intrinsic precommit (void) {
// ...
};
intrinsic presync (TIMESTAMP from , TIMESTAMP to) {
// ...
};
intrinsic sync (TIMESTAMP from , TIMESTAMP to) {
// ...
};
intrinsic postsync (TIMESTAMP from , TIMESTAMP to) {
// ...
};
intrinsic plc (TIMESTAMP from , TIMESTAMP to) {
// ...
};
intrinsic commit (TIMESTAMP from , TIMESTAMP to) {
// ...
};
intrinsic notify (char* _property-name_ , int event , void* value) {
// ...
};
function _function-name_ (_call arguments..._) {
// code
};
}
commit
Prior to Hassayampa (Version 3.0)
intrinsic commit (void) {...}
The commit function is run after all synchronization passes have been completed. The commit function must return SUCCESS or FAILED to indicate the result. The simulation will stop if the return values is not SUCCESS.
As of Hassayampa (Version 3.0)
intrinsic commit (TIMESTAMP from , TIMESTAMP to) {...}
The commit function is run after all synchronization passes have been completed. The commit function must return a TIMESTAMP later than to (or TS_NEVER) to indicate the success. The simulation will stop if the return values is not later than to.
create
intrinsic create (object parent) {...}
The create function is run before user-defined values are loaded and init is performed. This event allows you to set up each object prior to user values being set by the GLM file. This is the time to set default values and values used to detect whether the user has defined required values (e.g., negative or zero values that are not valid). The create function must return SUCCESS or FAILED to indicate the result. The simulation will stop if the return values is not SUCCESS.
init
TODO:
isa
TODO:
notify
TODO:
plc
TODO:
postsync
TODO:
precommit
TODO:
presync
TODO:
sync
TODO:
Caveat
You should not define classes that are already defined in modules. There's nothing to prevent users from doing this, but the behavior of GridLAB-D™ under such conditions is not defined.
Windows users must have MinGW installed on their system for the class directive to load properly for anything other than adding public properties.
Examples
Verifying class structures
To verify the structure of an existing class use the syntax:
module climate;
class climate {
double temperature[degF$]$;
}
If the variable temperature is defined but is not a double with units degF, then the load of the GLM file will fail. This allows modelers to be certain that the variable and units remain as expected when a module is loaded.
Altering class structures
To alter the structure of an existing class by adding new variables, use the syntax:
module climate;
class climate {
double elevation[ft];
}
If the variable elevation is not defined, then it will be added with the units specified.
Creating new classes
To create a new runtime class with a double called elevation measured in [ft] use the syntax:
class my_class {
double elevation[ft];
}
TODO: Add an example of a full-fledged class with runtime components.
Linking to Parameters Between Objects
Synopsis
gl_publish_variable(CLASS *_oclass, PROPERTYTYPE_ type _, const char *'name_ , size_t offset , ..., NULL);
Declarations
#include "gridlabd.h"
GL_ATOMIC(type ,name);
GL_STRUCT(type ,name);
GL_STRING(type ,name);
GL_ARRAY(type ,name , size);
GL_BITFLAGS(type ,name);
General accessors
size_t get_name _offset();
gld_property get_name _property();
Atomic accessors
type get_name();
type get_name(gld_rlock& rlock);
type get_name(gld_wlock& wlock);
void set_name(type value);
void set_name(type value , gld_wlock& wlock);
Struct accessors
type get_name();
type get_name(gld_rlock& rlock);
type get_name(gld_wlock& wlock);
void set_name(type value);
void set_name(type value , gld_wlock& wlock);
String accessors
char* get_name();
char* get_name(gld_rlock& rlock);
char* get_name(gld_wlock& wlock);
void set_name(char* value);
void set_name(char* value , gld_wlock& wlock);
char get_name(size_t n);
char get_name(size_t n , gld_rlock& rlock);
void set_name(char value , size_t n);
void set_name(char value , size_t n , gld_wlock& wlock);
Array accessors
type * get_name();
type * get_name(gld_rlock& rlock);
type * get_name(gld_wlock& wlock);
void set_name(type * value);
void set_name(type * value , gld_wlock& wlock);
type get_name(size_t n);
type get_name(size_t n , gld_rlock& rlock);
void set_name(type value , size_t n);
void set_name(type value , size_t n , gld_wlock& wlock);
Bitflag accessors
type get_name(type mask=-1);
type get_name(gld_rlock& rlock);
type get_name(gld_wlock& wlock);
void set_name(type value);
void set_name _bits(type value);
void clr_name(type value);
void set_name(type value , gld_wlock& wlock);
Description
The gl_publish_variable function is a variable argument list call used to publish the publicly accessible properties of a GridLAB-D™ class. The argument list must contain a least one property type, property name and property offset tuple. The property type must be one of the members of PROPERTYTYPE. The property name must be a const char * and the property offset must be size_t. The property list must be NULL terminated.
Additional options may be added after each property definition tuple. These options include
-
PT_INHERIT: This will include the properties of the parent class (if any) in searches of this class's properties.
-
PT_ACCESS:, access This allows you to set special access rights (see PROPERTYACCESS for details).
-
PT_FLAGS:, flags This allows you to set special property flags (see PROPERTYFLAGS for details).
-
PT_UNIT:, "definition " This allows you to set the units for double and complex properties.
-
PT_SIZE:, size This allows you to define an array of properties.
-
PT_EXTEND: This allows you to enlarge the class by the size of the property.
-
PT_EXTENDBY:, bytes This allows you to enlarge the class by the number of bytes given.
-
PT_DESCRIPTION:, "description " This allows you to provide a text description of the property for use in given users help (see --modhelp for details)
-
PT_KEYWORD:, "name ", value This allows you to define one or more keyword values to associate with the property (see set and enumeration for details). Note that you must cast constants to appropriate built-in types to avoid argument alignment problems.
Declarations
-
GL_ATOMIC: GL_ATOMIC types are used strictly for data types that can set as an atomic operation on all platforms.
-
GL_STRUCT: If the data type cannot be set as an atomic operation, you must use the GL_STRUCT declaration.
-
GL_STRING: Any data type that is a character array can be declared as GL_STRING to enable string accessors.
-
GL_ARRAY: Data types that are general arrays can be declared as GL_ARRAY to enable array accessors.
-
GL_BITFLAGS: Bitmap and bitflags integers can be declared as GL_BITFLAGS to enable bit set and clear accessors.
Example
The following example is taken from the assert module:
Header file (assert.h)
class g_assert : public gld_object {
public:
typedef enum {AS_INIT=0, AS_TRUE=1, AS_FALSE=2, AS_NONE=3} ASSERTSTATUS;
GL_ATOMIC(ASSERTSTATUS,status)
GL_STRING(char1024,target);
GL_STRING(char32,part);
GL_ATOMIC(PROPERTYCOMPAREOP,relation);
GL_STRING(char1024,value);
GL_STRING(char1024,value2);
// ...
};
Implementation file (assert.cpp)
g_assert::g_assert(MODULE *module)
{
// ...
if (gl_publish_variable(oclass,
PT_enumeration,"status",get_status_offset(),PT_DESCRIPTION,"desired outcome of assert test",
PT_KEYWORD,"TRUE",(enumeration)AS_TRUE,
PT_KEYWORD,"FALSE",(enumeration)AS_FALSE,
PT_KEYWORD,"NONE",(enumeration)AS_NONE,
PT_char1024, "target", get_target_offset(),PT_DESCRIPTION,"the target property to test",
PT_char32, "part", get_part_offset(),PT_DESCRIPTION,"the target property part to test",
PT_enumeration,"relation",get_relation_offset(),PT_DESCRIPTION,"the relation to use for the test",
PT_KEYWORD,"==",(enumeration)TCOP_EQ,
PT_KEYWORD,"<",(enumeration)TCOP_LT,
PT_KEYWORD,"<=",(enumeration)TCOP_LE,
PT_KEYWORD,">",(enumeration)TCOP_GT,
PT_KEYWORD,">=",(enumeration)TCOP_GE,
PT_KEYWORD,"!=",(enumeration)TCOP_NE,
PT_KEYWORD,"inside",(enumeration)TCOP_IN,
PT_KEYWORD,"outside",(enumeration)TCOP_NI,
PT_char1024, "value", get_value_offset(),PT_DESCRIPTION,"the value to compare with for binary tests",
PT_char1024, "within", get_value2_offset(),PT_DESCRIPTION,"the bounds within which the value must bed compared",
PT_char1024, "lower", get_value_offset(),PT_DESCRIPTION,"the lower bound to compare with for interval tests",
PT_char1024, "upper", get_value2_offset(),PT_DESCRIPTION,"the upper bound to compare with for interval tests",
NULL)<1)
throw "assert property publish failed";
// ...
}
Exposing Methods to Other Objects
Synopsis
FUNCTIONADDR (*call)(object *object);
gl_publish_function(class *oclass , const char *name , FUNCTIONADDR call);
FUNCTIONADDR gl_get_function(object *obj , const char *name);
Remarks
Classes may expose methods to other classes using the gl_publish_function API call. The function address can then be looked up and called as needed. The argument list is not specified and thus cannot be verified when the method is called.
Example
Target class
#include <stdarg.h>
#include "gridlabd.h"
EXPORT void* my_function(OBJECT *obj , ...)
{
va_list ptr;
va_start(ptr,obj);
// **TODO** implement your function here
va_end(ptr);
return NULL; // **TODO** return data pointer
}
Calling class
#include "gridlabd.h"
gld_object *target = get_object("target_object_name ");
FUNCTIONADDR my_function = target->get_function("my_function ");
void *result = my_function(target->my()); // **TODO** add arguments to call