Modules
What kind of general content do developers need to know when it comes to understanding and potentially implementing a GridLAB-D™ module?
TODO - Review - I'm assuming there is some but don't know about about the guts of GridLAB-D™ to know what kind of content needs to go here.
A great deal of the consternation with the core, when developing modules, is that there is no direct access to the core. There are instead a set of callbacks that are exported to modules through inclusion of gridlabd.h. The callback structure, defined in module.cpp, near line 500, and in object.h, near line 50, is a large group of function pointers that are populated when the core starts up, and is passed to each DLL as they are loaded. This structure is available within modules developed from the GridLAB-D™ basecode, and allows the functions below to be used transparently.
Modules are not like class definitions that you give as a directive because they are not compiled when GridLAB-D™ is run. Instead, developers package a collection of classes and distribute a dynamic link library (as a .dll file in Windows or .so file in Linux). Static modules must be loaded before the classes they define can be used or modified (i.e., use module name;)
The module loader will search the GLPATH environment variable to locate a file named name.dll (on linux the search will be for libname.so). If a particular version is desired, the version must be appended to the name using the format module name-major; or module name-major_minor; depending on whether you wish to specify on the major version, or both the major and minor version.
Module blocks may include additional information, such as assignments of the values for module globals and specification of the version number. To set a module global variable, simply include the name and value in the module block, such as
module MyModule {
MyStringGlobal "value";
MyEnumGlobal A;
MyDoubleGlobal 1.2 ft/s;
}
Class blocks are used to create, modify, or verify class definitions. If a class is already defined in a static module, then a class block either modifies or verifies the definition provided by the module. Consider the following example ` ``
module MyModule;
class MyClass {
char32 svalue;
enum {A=0,B=1,C=2} evalue;
double dvalue[W];
}
If the properties svalue, evalue, and dvalue are already defined as specified, the class block will load successfully. However, if there are any differences between the class block and the module's definition of the class, then the loader will attempt to address the discrepancy as follows:
-
If the class defines a property differently than the module, then the loader will fail.
-
If the class defines a property that the module does not define, then the loader will extend the module's definition of the class to include this new property.
-
If the class is not defined by any loaded module, then the class is defined as a new class, and the properties are added to that new class. In this case, you may also include C++ code for the behaviors that static modules normally provide. See below for more information on runtime classes.
XML files do not really support classes, at least not the way GLM files do. In GLM files, the class can be defined fully from nothing, but in an XML file, it can only be described in a limited fashion. This is because the definition is not easily transportable from one system to another, but the description is something that other systems often require in order to know how to interpret the model.
For this reason, XML files often use a description file (called an XSD file) to know how to interpret the XML file. The XSD contains all the class information, but XSD files omit many aspects that are specified in GLM files. GLM files provide instructions called directives on what GridLAB-D™ is supposed to do when it tries to update an object, which is something that is largely irrelevant to (or incompatible with) other tools that might look at a GridLAB-D™ XML file.
From here on, we will discuss modeling in the context of GLM files, and reserve the special consideration for XML files when they are discussed later in the documentation.
Introduction
A GridLAB-D™ module is a dynamically loaded library (.dll on Windows, .so on Linux) that packages one or more object classes together with their behaviors and properties. Modules are pre-compiled by developers and loaded at runtime using the module directive in a GLM file. The module loader resolves the library by searching the GLPATH environment variable. A module must be loaded before any of its classes can be instantiated in a model.
A module function is a C-callable exported function that the GridLAB-D™ core invokes to interact with a module. Required module functions include init() (called once on load to register classes and globals) and term(). Optional functions such as check(), import(), and export() extend module capabilities. Modules that support subsecond (deltamode) simulation must also implement deltamode_desired(), preupdate(), interupdate(), and postupdate(). See Module functions below for the full list.
A module global is a named variable defined and published by a module that is accessible through GridLAB-D™'s global variable mechanism. Module globals are registered during module initialization using gl_global_create() and can be set in the module block of a GLM file. They allow model authors to configure module-level behavior without modifying individual objects. See Module globals for details.
Module functions
Required export functions:
EXPORT CLASS *init(CALLBACKS *fntable, MODULE *module, int argc, char *argv[]);
EXPORT void term(void);
CDECL int do_kill();
Optional export functions:
EXPORT int check();
EXPORT int export(const char *file);
EXPORT int import(const char *file);
EXPORT int kmldump( int(*)(const char *file,...), OBJECT *obj);
EXPORT void test(int argc, const char *argv[]);
EXPORT size_t stream(void *ptr, size_t len, bool is_str=false, void *match==NULL);
Subsecond export functions
EXPORT unsigned long deltamode_desired(int *flags);
EXPORT unsigned long preupdate(MODULE *module, TIMESTAMP t0, unsigned int64 dt);
EXPORT SIMULATIONMODE interupdate(MODULE *module, TIMESTAMP t0, unsigned int64 delta_time, unsigned long dt, unsigned int iteration_count_val);
EXPORT STATUS postupdate(MODULE *module, TIMESTAMP t0, unsigned int64 dt);ed int64 _dt_);
Required functions
init
The init function is required for all GridLAB-D™ modules. It is called once when the module is loaded. The init should use this opportunity to register all classes and module globals. The template for this function is:
// module/main.cpp (init template)
#define DLMAIN
#include <stdlib.h>
#include "gridlabd.h"
EXPORT CLASS *init(CALLBACKS *fntable, MODULE *module, int argc, char *argv[])
{
if (set_callback(fntable)==NULL)
{
errno = EINVAL;
return NULL;
}
// TODO: add gl_global_create() calls here (see module globals for details)
// TODO: call new for each class here (see create class for details)
return NULL; // TODO: return oclass member of first new class
}
do_kill
The do_kill function is required for all GridLAB-D™ modules. It is called when GridLAB-D™ terminates. The do_kill function should be used only to cleanup temporary files and memory allocation used by the module.
// module/main.cpp (do_kill template)
#define DLMAIN
#include <stdlib.h>
#include "gridlabd.h"
CDECL int do_kill()
{
// TODO: perform cleanup actions if needed
return 0;
}
Optional functions
term
The term function is called when the simulation stop (normally or on error).
// module/main.cpp (do_kill template)
#define DLMAIN
#include <stdlib.h>
#include "gridlabd.h"
EXPORT void term(void)
{
// TODO: perform simulation end operations
}
check
The check function is used to allow user to perform module checks before running a simulation. These can be used to verify model consistency, disk space available, and other verification procedures that are not always needed, but can be helpful in diagnosing problems.
// module/main.cpp (do_kill template)
#define DLMAIN
#include <stdlib.h>
#include "gridlabd.h"
CDECL int check()
{
// TODO: perform check operations and report issues
return 0;
}
export
The export function allows a module to define a method for exporting a GridLAB-D™ model file to an arbitrary file format. If defined, the export routine is called after the simulation is completed.
module/main.cpp
EXPORT int (*export)(const char *file)
{
// your export code
return count; // count of entities exported
}
import
The import function is used to load a model from an arbitrary file format. The import GLM directive is used to initiative the import process.
EXPORT int import(const char *file)
{
// import processing code
return n; // n=0: failed; n<0: error after loading n entities; n>0: successfully loaded n entities
}
kmldump
The kmldump function is used to output KML (Google Earth) data.
typedef int (*KMLOUT)(const char *format, ...);
EXPORT int kmldump(KMLOUT kmlout, OBJECT *obj)
{
kmlout("kml data");
return 0; // return value is ignored
}
See Google KML Documentation for details on the KML format.
test
TODO - Update - The test function is relatively unused and was intended to support module tests.
stream
TODO - Update - The stream function will soon be required to support checkpoints.
Subsecond functions
deltamode_desired: indicate whether delta mode is desiredpreupdate: returnsdeltamode_timestepinterupdate: module-level call at each deltatimestep, including iterationspostupdate: returnsSUCCESS
See Time Management for more details.
Contingent functionalities
Modules may have runtime functionalities that are not always available, e.g., when an external application is not installed. In such cases, it is highly recommended that the module create a global flag variable only when the functionality is available. You can create a global variable in init, for example:
bool mytool_ok = false;
if ( load_mytool() )
{
mytool_ok = true;
gl_create_global("module-name",PT_bool, &mytool,NULL);
}
This will allow users to create GLM files that have contingent models depending on the presence of the external tools:
#ifdef module-name
class ...
object ...
#endif
Some examples of contingent functionalities are MATLAB and MYSQL.
VS2005 - Often these functionalities are only supported when the proper libraries are installed on the build machine. These modules usually have a special flag set, e.g., HAVE_MYSQL or HAVE_MATLAB so that Linux/Mac machine can automatically build the proper code based on what is installed. In Windows this is not possible. If the libraries are not available, we recommend you unload the project and not build it rather than changing the flag.
Module globals
How to publish a global variable from a module
Synopsis
C
gl_global_create("_#Module name|module-name_ ::_variable-name_ ",
PT__built-in_type,_variable-address _,_
PT_SIZE,_array-size_ ,
PT_UNITS,_units_ ,
PT_ACCESS,_access-control-flags_ ,
PT_DESCRIPTION,_brief-description_ ,
NULL);
C++
class gld_global {
gld_global(const char *name, PROPERTYTYPE t, void *p);
}
Description
The naming convention for module globals requires that the module name precede the global variable name separated by a double colon, as in module--name ::variable-name. This allows the core to associate the global with the module. If the variable name does not include the module name, it will be treated as a core global. There is nothing to prevent module programmers for doing this, and in some cases this may be useful.
Parameters
The follow parameter may be used to define a module or class global variable.
| Parameter | Description |
|---|---|
| Access control flags | This identifies how the global may be accessed by other modules and object. See PT_ACCESS for details. If omitted, the variable is assumed to be public. |
| Array size | Identifies the array size, which is optional. If omitted it is assumed to be 1. |
| Brief description | This identifies a string constant that provide a brief synopsis of the variable. This description is displayed by --modhelp and XML output. |
| Built-in type | This identifies what built-in type the variable is. This option is required. |
| Module name | This identifies the module name. The module name is optional, and when omitted causes the variable to become a core global. |
| Units | This identifies the units for double and complex variables. The units are optional and if omitted, the variable is considered unitless. |
| Variable name | This identifies the variable name and is required. |
Examples
Module globals
Module globals are created whenever a module is loaded. These must be placed in the module init function to ensure that they are always created when modules are loaded.
Note
Most module implementation is a callback function table to define gl_create_global. Therefore calls to this function cannot be completed until the callback table is built using the set_callback function. See source documentation on set_callback for details.
Example (main.cpp):
#include "gridlabd.h"
#include "my_class.h"
char256 my_data = "initial value";
EXPORT CLASS *init(CALLBACKS *fntable, MODULE *module, int argc, char *[])
{
if (set_callback(fntable)==NULL)
{
errno = EINVAL;
return NULL;
}
**if ( gl_global_create("my_module::my_data",PT_char256,my_data,**
**PT_ACCESS,PA_PUBLIC,**
**PT_DESCRIPTION,"my data example",NULL)==NULL )**
**throw "unable to create module global char256 my_module::my_data";**
new my_class(module);
return my_class::oclass;
}
Class globals
Class global are created whenever a class is referenced. These must be placed in the class constructor, which is called only when the class is first referenced.
Note
It is unusual to have class globals because they are only created when the class is referenced, but there are cases where this may be preferred to a module global. An example would be a situation in which all object of a given class must share a variable that may be altered by the user but would not be available to the user if the class when the class is not used.
Example (my_class.cpp):
class my_class {
public: my_class(MODULE *);
private: char256 my_variable;
};
static char256 my_class::my_variable = "initial value";
my_class::my_class(MODULE *module)
{
if ( oclass==NULL )
{
oclass = gld_class::create(module,"my_class",sizeof(my_class),PC_AUTOLOCK);
if ( oclass==NULL ) throw "unable to register my_class";
else oclass->trl = TRL_UNKNOWN;
defaults = this;
if ( gl_publish_variables( oclass,
NULL ) < 1 ) throw "unable to publish my_class properties";
}
else throw "invalid attempt to define class more than once";
**gld_global my_global("my_module::my_variable",PT_char256,my_variable);**
**if ( !my_global.isvalid() )**
**throw "unable to create class global char256 my_module::my_variable";**
memset(this,0,sizeof(my_class));
}
Globals
GridLAB-D™ supports dynamic definition of global variables. All of these are accessible through the core by any module at any time, and are exported to the output file as a group. These variables control output verbosity and output files, how many threads to use, how to output the results, the strictness of the global variable creation, various module states, etc.
Using Global Variables
Global variables are created and accessed through GL_* functions. There is no particular constraint on what can be done with the global variables, but it is recommended to declare them in the module init functions, and explicitly link them to a static variable.
The GLOBALVAR struct should be considered opaque. It is much simpler and much more reliable to only use the struct as a handle for getting and setting the value within the variable.
GLOBALVAR gl_global_create(char name, …)
Explicitly creates and defines a global variable. The first argument is the name of the variable, which must be unique. The subsequent arguments must specify a PT_type as a second argument, then any arguments for that type, such as keywords, key values, and access types.
Example:
gl_global_create("myglobalname",PT_double,&myglobalvar,PT_ACCESS,PA_REFERENCE,NULL)
This will create an entry named myglobalname that is treated as a double , and will point to myglobalvar. PA_REFERENCE declares that the value should only be read through the global implementation. The last argument must always be NULL , or the function will behave aberrantly – so don’t skip it.
STATUS gl_global_setvar(char *name, …)
This function uses a character string to set the value of a global variable, then returns 0 if the value could not be set as specified, 1 if it could. The arguments either end up as one string in the form (“ name =val ”), or (“ name ”, “ value ”). In both cases, the value is written as a string.
Example:
gl_global_setvar(“myglobal”, “4.360”);
This will set “myglobal” to 4.360.
Example:
gl_global_setvar(“myglobal=camera”);
This will fail, since the string input “camera” is nonsensical for a double value.
char gl_global_getvar(char name, char*value, int len)
This function will look for the most recently constructed variable published with name, and attempt to convert the contents into value (len chars long) with the value for the global variable. If value is null, a static buffer will be used. In either case, a pointer to the buffer holding the string representation of the global variable’s value will be returned on success, and NULL will be returned if the global variable could not be found, or if insufficient buffer space was available for the conversion.
GLOBALVAR gl_global_find(char name)
Looks for the global variable published as name and returns the first global variable with that name that was found, if any were.
Using Module Variables
Module-level variables use the global variable interface for construction and access. The significant difference is that the variables must be prefixed with the module name and two colons to associate them with a module within the code. Within model files, these variables can be set within the module property block. For example,
my_mod/init.cpp:
gl_global_create("my_mod::value1", PT_double, &value1, NULL);
my_mod_test.glm:
module my_mod{
value1 42.0;
}
These module variables will be grouped underneath their modules within model dump XML files.
| Global Variable Access Rights Variable name | Visible | Saved | Loaded | Access |
|---|---|---|---|---|
| version.major | √ | √ | REFERENCE | |
| version.minor | √ | √ | REFERENCE | |
| command_line | √ | √ | REFERENCE | |
| environment | √ | √ | √ | PUBLIC |
| quiet | √ | √ | √ | PUBLIC |
| warn | √ | √ | √ | PUBLIC |
| debugger | √ | √ | √ | PUBLIC |
| gdb | √ | √ | √ | PUBLIC |
| debug | √ | √ | √ | PUBLIC |
| test | √ | √ | √ | PUBLIC |
| verbose | √ | √ | √ | PUBLIC |
| iteration_limit | √ | √ | √ | PUBLIC |
| workdir | √ | √ | REFERENCE | |
| dumpfile | √ | √ | √ | PUBLIC |
| savefile | √ | √ | √ | PUBLIC |
| dumpall | √ | √ | √ | PUBLIC |
| runchecks | √ | √ | √ | PUBLIC |
| threadcount | √ | √ | √ | PUBLIC |
| profiler | √ | √ | √ | PUBLIC |
| pauseatexit | √ | √ | √ | PUBLIC |
| testoutputfile | √ | √ | √ | PUBLIC |
| xml_encoding | √ | √ | √ | PUBLIC |
| clock | √ | √ | √ | PUBLIC |
| starttime | √ | √ | √ | PUBLIC |
| stoptime | √ | √ | √ | PUBLIC |
| double_format | √ | √ | √ | PUBLIC |
| complex_format | √ | √ | √ | PUBLIC |
| object_format | √ | √ | √ | PUBLIC |
| object_scan | √ | √ | √ | PUBLIC |
| object_tree_balance | √ | √ | √ | PUBLIC |
| kmlfile | √ | √ | √ | PUBLIC |
| modelname | √ | √ | REFERENCE | |
| execdir | √ | √ | REFERENCE | |
| strictnames | √ | √ | √ | PUBLIC |
| website | √ | √ | √ | PUBLIC |
| urlbase | √ | √ | √ | PUBLIC |
| randomseed | √ | √ | √ | PUBLIC |
| include | √ | √ | REFERENCE | |
| trace | √ | √ | √ | PUBLIC |
| gdb_window | √ | √ | √ | PUBLIC |
| tmp | √ | √ | √ | PUBLIC |
| force_compile | √ | √ | √ | PUBLIC |
| nolocks | √ | √ | √ | PUBLIC |
| skipsafe | √ | √ | √ | PUBLIC |
| dateformat | √ | √ | √ | PUBLIC |
| minimum_timestep | √ | √ | √ | PUBLIC |
| platform | √ | √ | REFERENCE | |
| suppress_repeat_messages | √ | √ | √ | PUBLIC |
| maximum_synctime | √ | √ | √ | PUBLIC |
| run_realtime | √ | √ | √ | PUBLIC |
| no_deprecate | √ | √ | √ | PUBLIC |
| sync_dumpfile | √ | √ | √ | PUBLIC |
| streaming_io | √ | PROTECTED |
Synopsis
C
gl_global_create("_#Module name|module-name_ ::_variable-name_ ",
PT__[built-in_type],_variable-address _,_
PT_SIZE,_array-size_ ,
PT_UNITS,_[units]_ ,
PT_ACCESS,_access-control-flags_ ,
PT_DESCRIPTION,_brief-description_ ,
NULL);
C++
class gld_global {
gld_global(const char *name, PROPERTYTYPE t, void *p);
}
Description
The naming convention for module globals requires that the module name precede the global variable name separated by a double colon, as in module--name ::variable-name. This allows the core to associate the global with the module. If the variable name does not include the module name, it will be treated as a core global. There is nothing to prevent module programmers for doing this, and in some cases this may be useful.
Parameters
The follow parameter may be used to define a module or class global variable.
Access control flags
This identifies how the global may be accessed by other modules and object. See PT_ACCESS for details. If omitted, the variable is assumed to be public.
Array size
Identifies the array size, which is optional. If omitted it is assumed to be 1.
Brief description
This identifies a string constant that provide a brief synopsis of the variable. This description is displayed by --modhelp and XML output.
Built-in type
This identifies what built-in type the variable is. This option is required.
Module name
This identifies the module name. The module name is optional, and when omitted causes the variable to become a core global.
Units
This identifies the units for double and complex variables. The units are optional and if omitted, the variable is considered unitless.
Variable name
This identifies the variable name and is required.
Examples
Module globals
Module globals are created whenever a module is loaded. These must be placed in the module init function to ensure that they are always created when modules are loaded.
Note Most module implementation is a callback function table to define gl_create_global. Therefore calls to this function cannot be completed until the callback table is built using the set_callback function. See source documentationTODO* - link - insert link for source doc on set_callback** for details.
Example (main.cpp)
#include "gridlabd.h"
#include "my_class.h"
char256 my_data = "initial value";
EXPORT CLASS *init(CALLBACKS *fntable, MODULE *module, int argc, char *[])
{
if (set_callback(fntable)==NULL)
{
errno = EINVAL;
return NULL;
}
**if ( gl_global_create("my_module::my_data",PT_char256,my_data,**
**PT_ACCESS,PA_PUBLIC,**
**PT_DESCRIPTION,"my data example",NULL)==NULL )**
**throw "unable to create module global char256 my_module::my_data";**
new my_class(module);
return my_class::oclass;
}
Class globals
Class global are created whenever a class is referenced. These must be placed in the class constructor, which is called only when the class is first referenced.
Note It is unusual to have class globals because they are only created when the class is referenced, but there are cases where this may be preferred to a module global. An example would be a situation in which all object of a given class must share a variable that may be altered by the user but would not be available to the user if the class when the class is not used.
Example (my_class.cpp)
class my_class {
public: my_class(MODULE *);
private: char256 my_variable;
};
static char256 my_class::my_variable = "initial value";
my_class::my_class(MODULE *module)
{
if ( oclass==NULL )
{
oclass = gld_class::create(module,"my_class",sizeof(my_class),PC_AUTOLOCK);
if ( oclass==NULL ) throw "unable to register my_class";
else oclass->trl = TRL_UNKNOWN;
defaults = this;
if ( gl_publish_variables( oclass,
NULL ) < 1 ) throw "unable to publish my_class properties";
}
else throw "invalid attempt to define class more than once";
**gld_global my_global("my_module::my_variable",PT_char256,my_variable);**
**if ( !my_global.isvalid() )**
**throw "unable to create class global char256 my_module::my_variable";**
memset(this,0,sizeof(my_class));
}
}