Creating a class
Creating a class - Procedure to create a new class in a GridLAB-D™ module
Synopsis
_module-name_ /_class-name_.h
_module-name_ /_class-name_.cpp
_module-name_ /Makefile.am
_module-name_ /_module-name_.vcproj
Header file
module-name /class-name.h
// $ Id$
// Copyright (c) <<<YEAR>>> <<<COMPANY>>>
// Template valid as of Hassayampa (Version 3.0)
#ifndef _<<<CLASS>>>_H
#define _<<<CLASS>>>_H
#include "gridlabd.h"
class <<<CLASS>>> : public gld_object {
public: // published variables
// **TODO** add public typedefs
// **TODO** declare published variables using GL_* macros
private: // unpublished variables
// **TODO** add private typedefs
// **TODO** add unpublished variables
public: // required functions
<<<CLASS>>>(module *module);
int create(void);
int init(OBJECT *parent);
// **TODO** add optional class functions
public: // optional/user-defined functions
// **TODO** add published class functions
private: // internal functions
// **TODO** add desired internal functions
public: // required members
static class *oclass;
static <<<CLASS>>> *defaults;
};
#endif // _<<<CLASS>>>_H
Source file
module-name /class-name.cpp
// $ Id$
// Copyright (c) <<<YEAR>>> <<<COMPANY>>>
// Template valid as of Hassayampa (Version 3.0)
#include "<<<CLASS>>>.h"
EXPORT_CREATE(<<<CLASS>>>);
EXPORT_INIT(<<<CLASS>>>);
// **TODO** add optional functions declarations
class <<<CLASS>>>::oclass = NULL;
<<<CLASS>>> *<<<CLASS>>>::defaults = NULL;
// **TODO** add declaration of class globals
<<<CLASS>>>::<<<CLASS>>>(module *module)
{
if ( oclass!=NULL )
exception("cannot register class more than once");
oclass = gld_class::create(module,"<<<CLASS>>>",sizeof(<<<CLASS>>>),<<<OPTIONS>>>);
if ( oclass==NULL )
exception("class registration failed");
oclass->trl = <<<TRL>>>;
if ( gl_publish_variable(oclass, <<<VARIABLESPECS>>>, NULL)<1 )
exception("unable to publish properties");
memset(defaults=this,0,sizeof(*this));
// **TODO** set defaults
}
<<<CLASS>>>::create(void)
{
memcpy(this,defaults,sizeof(*this));
// **TODO** set defaults
return SUCCESS; // return FAILED on create error
}
<<<CLASS>>>::init(OBJECT *parent)
{
// **TODO** initialize object
return SUCCESS; // return FAILED on create error
}
// **TODO** add implementations of optional class functions
Autoconf file
module-name /Makefile.am
pkglib_LTLIBRARIES = <<<MODULE>>>.la
<<<MODULE>>>_la_SOURCES = main.cpp \
<<<CLASS>>>.cpp <<<CLASS>>>.h \
# **TODO** add new classes before this line
<<<MODULE>>>_la_LDFLAGS = -module -no-undefined -avoid-version -version-info 1:0:0
uninstall-hook:
-rmdir $(DESTDIR)$(pkglibdir)
Project file
Additional include directories ..\core Debug information format Program Database (/Zi)
TODO: add other VS project options
Pass control
TODO:
Forced naming
The canonical name is the name automatically assigned to an object when name defined name is given by the modeler. The canonical name takes the form given by the object_format global variable.
object class {
name class: id;
}
Object may be assigned user-defined names using the naming variable. These names may be referred to be other objects using the object property.
object class {
name name ;
}
For example:
object house {
name house1;
...
};
In this way, other objects can refer to this object by name. For example, an appliance can be added to the house by referencing the house's name in the parent-child relationship:
object waterheater {
parent house1;
name waterheater1;
...
};
There are some restrictions on the characters that can be used in name. Here are the rules for creating a name:
- All printable ASCII characters are allowed with some exceptions that are reserved for special functions (reserved: ., (), {}, [], ^, $, *, +, =, |, :, and ;).
- Only alpha characters (A-Z,a-z) can be used at the beginning of the name. (This rule can be relaxed if
relax_naming_rulesis set to non-zero). - " " can be used to enclose the name. This is advisable when using spaces or other non-alphanumeric characters. NOTE: While "name" has a 1024 character limit, in practice, the limit is 64 characters. See ticket 980.
Automatic locking
TODO:
Observers
TODO:
Parent classes
TODO:
Abstract classes
TODO:
Parent overrides
TODO:
Built-in Types
GridLAB-D™ supports a range of built-in data types for use in defining classes.
To declare a char8 value in a class use the syntax
class my_class {
char8 my_string;
}
To define a string value in an object use the syntax
object my_class {
my_string "value";
}
where "value" is a string of 8 or fewer characters... Similarly for the remaining char data types. Note that an attempt to store a string longer than the maximum number of characters permitted results in truncation of the string to a length that fits.
Prior to Hassayampa (Version 3.0) there was no difference between the C and C++ implementations. Where noted, C++ specific information pertains only to versions as of Hassayampa (Version 3.0).
| Type | Description |
|---|---|
| bool | C This is a typedef to long C type. C++ This is the basic bool C++ type. |
| char8 | This is a typedef to char[9]. C++ This is a template based on charbuf<9>. |
| char32 | This is a typedef to char[33]. C++ This is a template based on charbuf<33>. |
| char256 | This is a typedef to char[33]. C++ This is a template based on charbuf<257>. |
| char1024 | This is a typedef to char[1025]. C++ This is a template based on charbuf<1025>. |
| complex | C This is a typedef to the struct s_complex. C++ This is the class Dev:complex. |
| complex_array | C This is a typedef to the struct s_doublearray. C++ This is the class double_array. |
| delegated | |
| double | This is the C/C++ basic double C type. |
| double_array | C This is a typedef to the struct s_doublearray. C++ This is the class double_array. |
| enduse | This is the s_enduse structure. |
| enumeration | This is a typedef to the long C type. |
| float | This is the basic float C type. |
| int8 | This is a typedef to the signed char C type. Integers requiring 8 bits are stored in an int8 built-in data type. An int8 value can represent values between -128 to 127. |
| int16 | This is a typedef to the signed short C type. Integers requiring 16 bits are stored in an int16 built-in data type. An int16 value can represent values between -32,768 to 32,767. |
| int32 | This is a typedef to the signed long C type. |
| int64 | This is a typedef to the signed longlong C type. Integers requiring 64 bits are stored in an int64 built-in data type. An int64 value can represent values between −9,223,372,036,854,775,808 to 9,223,372,036,854,775,807. |
| loadshape | This is the s_loadshape structure. |
| object | This is the s_objectlist structure. |
| object_array | |
| real | |
| set | This is a typedef to the int64 C type. |
| structure | |
| timestamp | This is a typedef to the int64 C type. |
| transform | This is the s_transform structure. |
| randomvar | This is the s_randomvar structure. |
| void | built-in data type is used to describe an data item that contains no information. |
Charbuf
template<size_t size> class charbuf {
private:
char buffer[size];
public:
charbuf<size>(void);
charbuf<size>(const char *s);
~charbuf<size>(void);
size_t get_size(void);
size_t get_length(void);
char *get_string(void);
char* erase(void);
char* copy_to(char *s);
char* copy_from(const char *s);
operator char*(void);
bool operator ==(const char *s);
bool operator <(const char *s);
bool operator <(const char *s);
bool operator <=(const char *s);
bool operator >=(const char *s);
char *find(const char c);
char *find(const char *s);
char *findrev(const char c);
char *token(char *from, const char *delim, char **context);
size_t format(char *fmt, ...);
size_t vformat(char *fmt, va_list ptr);
};
typedef charbuf<1025> char1024;
typedef charbuf<257> char256;
typedef charbuf<33> char32;
typedef charbuf<9> char8;
| Property | Description |
|---|---|
| copy_from | Copies data from a string into the buffer. |
| copy_to | Copies data to a string from the buffer. |
| erase | Erases the buffer. |
| find | Finds a character or string in the buffer. |
| findrev | Find a character in the buffer starting from the end. |
| format | Formats the buffer using printf syntax. |
| get_size | Gets the size of the buffer. |
| get_length | Gets the length of the string in the buffer. |
| get_string | Gets a pointer to the string in the buffer. |
| operator char* | Casts to a char* type, which effectively gets a pointer to the string in the buffer. |
| operator == | True is the string and the buffer are identical. |
| operator < | True if the buffer is alphabetically before the string. |
| operator > | True if the buffer is alphabetically after the string. |
| operator <= | True if the buffer is alphabetically before or the same as the string. |
| operator >= | True if the buffer is alphabetically after or the same as the string. |
| token | Find the first (or next) instance of a delimited string in the buffer. |
| vformat | Formats the string using vprintf syntax. |
Caveat
You cannot rely on C++ compiler to automatically convert from the charbuf class to a string when using variable argument functions. This is a very insidious problem for certain compilers. In the absence of a cast or function that returns a char*, the compiler will either copy the entire buffer onto the stack (which is not what is usually expected) or reject the statement as an error due to the lack of a copy constructor. Therefore instead of
printf("%s",~~member~~);
you should use either
printf("%s",(const char*)member);
or
printf("%s",member.get_string());
Double Array
The double_array is a built-in type type and implemented as a class in the C++ Module API. This class allows basic math operations on arrays.
#include "myclass.h"
myclass::myclass(MODULE *module)
{
// ...
if ( gl_publish(oclass,
PT_double_array,"name",get_name_offset(), // other options...
NULL)<1 )
exception("unable to publish module myclass properties");
// ...
}
int myclass::create(void)
{
// ...
name.set_name("name");
// ...
}
int myclass::init(OBJECT *parent)
{
// ...
set_name("'a'11 'a'12 ... 'a'1M ; 'a'21 'a'22 ... 'a'2M ; 'a'N1 'a'N2 ... 'a'NM");
}
Example
A good example of using double_array is the canonical control model:
int example::sync(timestamp t1)
{
TIMESTAMP ts = 10; // discrete-time model time step
Y = C*X + D*U;
X += A*X + B*U;
return (t1/ts+1)*ts;
}
The declarations for the arrays are
GL_STRUCT(double_array,A); ///< A matrix (state transition)
GL_STRUCT(double_array,B); ///< B matrix (control)
GL_STRUCT(double_array,C); ///< C matrix (output)
GL_STRUCT(double_array,D); ///< D matrix (feedfwd)
GL_STRUCT(double_array,X); ///< state vector X (state)
GL_STRUCT(double_array,U); ///< control vector U (input)
GL_STRUCT(double_array,Y); ///< output vector Y (output)
The user would then provide the input and output properties and the model definition in the GLM file. The following example is a SISO model (the extension to MIMO is intuitive).
module
class example {
double input;
double output;
}
object example {
input 0;
output 0;
A "0 1 ; -0.16 -1"; // dimensions must match state vector
B "1 ; 1"; // dimensions must match state vector and input
C "1 0"; // dimensions must match state vector and output
D "0"; // dimensions must match input input and output
U "input";
X "0 ; 0";
Y "output";
object player {
property input;
file input.csv;
};
object recorder {
property output;
file output.csv;
interval -1;
};
};
Enumeration
The enumeration built-in data type is used to describe a value that can have one of several discrete states. An enumeration is distinguished from a set by the fact that sets can have any combination of the states, whereas enumerations can have only one at a time.
class my_class {
enumeration {A=1, B=2, C=3, D=4} my_enum;
}
object my_class {
my_enum A;
}
Integral
The integral property automatically calculates the average integral of variable1 with respect to another variable2. If dt is used, the integral is with respect to the global clock. The value of the integral is updated whenever variable1 or variable2 changes. If dt is used, the value of the integral is updated after the object's clock is updated.
Both variables must have either double or complex underlying type. The multiplication is performed according to the usual mathematical rules for a product of double and complex values, as appropriate.
The units of the integral must be consistent the units obtained when multiplying variable1 by variable2. The conversion is performed automatically each time the integral value is updated. If either of the variables has no unit, then the integral cannot have a unit, and the values are simply multiplied.
Variables can be local to the object, in which case they are simply named. If a variable in another object is used, the name of the object must be prepended, as in object_name.variable.
Global variables may used provided the are of double or complex type. The syntax for a global variable is ::variable for core globals and module::variable for module globals.
Example
The following example maintains the integral of the random variable \(x\) with respect to time and names it \(Sx\).
class example {
random x[m];
integral Sx[m/s];
}
object example {
x "type:normal(0,1); refresh:1h";
Sx S(x)dt;
object recorder {
property "x,Sx";
interval -1;
file example.csv;
};
}
Loadshape
The basic principle underpinning loadshape is that the total energy \(E\) consumed by a series of \(N\) pulses
where
- \(P\) is the power of each pulse
- \(\Delta t\) is the duration of each pulse.
At least one of the variables E, N, P, \(\Delta t\) must remain unspecified by the user in order for Eq. 1 to not be overspecified. Thus
is used to determine the number of pulses,
is used to determine the pulse duration, and
is used to determine the power of each pulse.
In cases where the standard deviation \(\sigma\) is provided, the power of the pulse is adjusted so that
Analog loadshapes
When the analog loadshape power \(P\) is given, the loadshape amplitude \(y_t\) at the time \(t\) shall be computed from schedule value \(x_t\) as
When the analog loadshape energy \(E\) is given, the loadshape amplitude \(y_t\) at the time \(t\) shall be computed from the schedule values \(x_t\) in the interval \(t=(0,T)\) as
where
- \(\Delta t\) is the time interval over which the schedule value \(x_t\) is given.
When neither the energy nor the power are given, the loadshape amplitude \(y_t\) at the time \(t\) shall be the schedule value \(x_t\).
Pulsed shapes
When a pulse loadshape is specified, the power amplitude \(y_t\) at the time \(t\) based on the schedule value \(x_t\) is given by
When the energy is not given, the power amplitude may be computed from the power and time interval
When both the energy and power are given, the power amplitude may be computed as
Modulated shapes
TODO: describe how modulated shapes are generated from schedules
Queued shapes
TODO: describe how queued shapes are generated from schedules
Scheduled shaped
TODO: describe how pulse shapes are generated from schedules
Related Concepts:
- Guide to Programming GridLAB-D™
- Introduction
- Developer prerequisites
- Programming conventions
- Build/release process
- Documentation Guide
- Theory of operation
- Creating a module
- Module globals
- Module functions
- Enabling Transient Mode
- Import/export
- Check
- KML output
- Example 1
- Creating a class
- Class functions
- Class globals
- Publishing properties
- Publishing methods
- Notifications
- Load methods
- Example 2
- Special Topics
- Data types
- Multithreading
- Application links
- Realtime server
- Graphical user interfaces
- Troubleshooting messages
- Example 3
- Source documentation
- C/C++ Module API documentation (trunk)
- C/C++ Module API Guide
- Example 4
- Validation
- Example 5
- Debugging
- Debug option
- VS2005 (MS Windows)
- use_msvc
- gdb option (linux/mac)
- gdb_window
- Runtime Class Debugging
- compile_once
- Code templates