diff --git a/Makefile b/Makefile index 2f6e27731..f3c682cce 100644 --- a/Makefile +++ b/Makefile @@ -104,10 +104,10 @@ LIB_LDLIBS += $(shell pkg-config --libs ${PKGS}) ######## Targets ######## -.PHONY: all clean install release docker doc +.PHONY: all clean install release docker doc models # Default target: build everything -all: $(LIBS) $(TARGETS) +all: $(LIBS) $(TARGETS) models # Dependencies for individual binaries fpga: LDLIBS += -lpci -lxil @@ -157,5 +157,8 @@ docker: doc: doxygen +models: + $(MAKE) -C lib/cbmodels + # Include auto-generated dependencies -include $(wildcard *.d) diff --git a/include/villas/nodes/cbuilder.h b/include/villas/nodes/cbuilder.h new file mode 100644 index 000000000..8fdf3296c --- /dev/null +++ b/include/villas/nodes/cbuilder.h @@ -0,0 +1,55 @@ +/** Node type: Wrapper around RSCAD CBuilder model + * + * @file + * @author Steffen Vogel + * @copyright 2015-2016, Steffen Vogel + * This file is part of VILLASnode. All Rights Reserved. Proprietary and confidential. + * Unauthorized copying of this file, via any medium is strictly prohibited. + **********************************************************************************/ + +#ifndef _CBUILDER_H_ +#define _CBUILDER_H_ + +#include + +#include "list.h" + +/* Helper macros for registering new models */ +#define REGISTER_CBMODEL(cb) \ +__attribute__((constructor)) static void __register() { \ + list_push(&cbmodels, cb); \ +} + +extern struct list cbmodels; /**< Table of existing CBuilder models */ + +struct cbuilder { + unsigned long step, read; + double timestep; + + struct cbmodel *model; + + float *params; + int paramlen; + + /* This mutex and cv are used to protect model parameters, input & outputs + * + * The cbuilder_read() function will wait for the completion of a simulation step + * before returning. + * The simulation step is triggerd by a call to cbuilder_write(). + */ + pthread_mutex_t mtx; + pthread_cond_t cv; +}; + +struct cbmodel { + char *name; + + void (*code)(); + void (*ram)(); + + int (*init)(struct cbuilder *cb); + int (*read)(float inputs[], int len); + int (*write)(float outputs[], int len); +}; + +#endif /* _CBUILDER_H_ */ \ No newline at end of file diff --git a/lib/cbmodels/Makefile b/lib/cbmodels/Makefile new file mode 100644 index 000000000..7e81258c7 --- /dev/null +++ b/lib/cbmodels/Makefile @@ -0,0 +1,8 @@ +MODELS = simple_circuit + +CFLAGS = -fPIC -DVILLAS -I../../include/villas + +all: $(addsuffix .so,${MODELS}) + +%.so: %.o + $(CC) -shared $^ -o $@ \ No newline at end of file diff --git a/lib/cbmodels/Subsystem.c b/lib/cbmodels/Subsystem.c new file mode 100644 index 000000000..9e86834b0 --- /dev/null +++ b/lib/cbmodels/Subsystem.c @@ -0,0 +1,32 @@ +VERSION: +3.001 + +#include "Subsystem.h" + +STATIC: + +#define SECTION STATIC + #include "model.c" +#undef SECTION + +RAM_FUNCTIONS: + +#define SECTION RAM_FUNCTIONS + #include "model.c" +#undef SECTION + +RAM: + +#define SECTION RAM + #include "model.c" +#undef SECTION + +model_ram(); + +CODE: + +#define SECTION CODE + #include "model.c" +#undef SECTION + +model_code(); \ No newline at end of file diff --git a/lib/cbmodels/Subsystem.h b/lib/cbmodels/Subsystem.h new file mode 100644 index 000000000..b072b8a81 --- /dev/null +++ b/lib/cbmodels/Subsystem.h @@ -0,0 +1,24 @@ +MODEL_TYPE: CTL + +#define SECTION CONSTANTS + #include "model.c" +#undef SECTION + +INPUTS: + +#define SECTION INPUTS + #include "model.c" +#undef SECTION + +OUTPUTS: + +#define SECTION OUTPUTS + #include "model.c" +#undef SECTION + +PARAMETERS: + +#define SECTION PARAMETERS + #include "model.c" +#undef SECTION + diff --git a/lib/cbmodels/constants.h b/lib/cbmodels/constants.h new file mode 100644 index 000000000..f551ef0e8 --- /dev/null +++ b/lib/cbmodels/constants.h @@ -0,0 +1,9 @@ +/* These constants are defined by RTDS in the component header file */ + +#define PI 3.1415926535897932384626433832795 // definition of PI +#define TWOPI 6.283185307179586476925286766559 // definition of 2.0*PI +#define E 2.71828182845904523536028747135266 // definition of E +#define EINV 0.36787944117144232159552377016147 // definition of E Inverse (1/E) +#define RT2 1.4142135623730950488016887242097 // definition of square root 2.0 +#define RT3 1.7320508075688772935274463415059 // definition of square root 3.0 +#define INV_ROOT2 0.70710678118654752440084436210485 \ No newline at end of file diff --git a/lib/cbmodels/model.c b/lib/cbmodels/model.c new file mode 100644 index 000000000..088c31a70 --- /dev/null +++ b/lib/cbmodels/model.c @@ -0,0 +1,163 @@ +// This is c-code for CBuilder component for Subsystem 2 +// Solver used as in RTDS: Resistive companion (Dommel's algo) +// Subsystem 1 is modelled in RSCAD +// +//% Circuit topology +// % +//% *** Subsystem 1 (SS1) *** % *** Subsystem 2 (SS2) *** +//% % +//% |---------| |---------| % +//% |---------| R1 |-------| L1 |----%-------|---------| +//% | |---------| |---------| % | | +//% | % | | +//% ----- % ----- ----- +//% | + | % | | | | +//% | E | % |C2 | | R2| +//% | - | % | | | | +//% ----- % ----- ----- +//% | % | | +//% |------------------------------------------%------------------ +// % +// % + +/* These constants are defined by RTDS in the component header file */ +#if defined(VILLAS) || SECTION == CONSTANTS + #define PI 3.1415926535897932384626433832795 // definition of PI + #define TWOPI 6.283185307179586476925286766559 // definition of 2.0*PI + #define E 2.71828182845904523536028747135266 // definition of E + #define EINV 0.36787944117144232159552377016147 // definition of E Inverse (1/E) + #define RT2 1.4142135623730950488016887242097 // definition of square root 2.0 + #define RT3 1.7320508075688772935274463415059 // definition of square root 3.0 + #define INV_ROOT2 0.70710678118654752440084436210485 +#endif + +// ----------------------------------------------- +// Variables declared here may be used as parameters +// inputs or outputs +// The have to match with whats in Subsystem.h +// ----------------------------------------------- +#if defined(VILLAS) || SECTION == INPUTS +double IntfIn; +#endif + +#if defined(VILLAS) || SECTION == OUTPUTS +double IntfOut; +#endif + +#if defined(VILLAS) || SECTION == PARAMETERS +double R2; // Resistor [Ohm] in SS2 +double C2; // Capacitance [F] in SS2 +#endif + +// ----------------------------------------------- +// Variables declared here may be used in both the +// RAM: and CODE: sections below. +// ----------------------------------------------- +#if defined(VILLAS) || SECTION == STATIC +double dt; +double GR2, GC2; //Inductances of components +double GnInv; //Inversion of conductance matrix (here only scalar) +double vC2Hist, iC2Hist, AC2; // history meas. and current of dynamic elements +double Jn; //source vector in equation Gn*e=Jn +double eSS2; //node voltage solution +#endif + +// ----------------------------------------------- +// This section should contain any 'c' functions +// to be called from the RAM section (either +// RAM_PASS1 or RAM_PASS2). Example: +// +// static double myFunction(double v1, double v2) +// { +// return(v1*v2); +// } +// ----------------------------------------------- +#if defined(VILLAS) || SECTION == RAM_FUNCTIONS +/* Nothing here */ +#endif + +// ----------------------------------------------- +// Place C code here which computes constants +// required for the CODE: section below. The C +// code here is executed once, prior to the start +// of the simulation case. +// ----------------------------------------------- +#if defined(VILLAS) || SECTION == RAM + +void ram() { + GR2 = 1/R2; + GC2 = 2*C2/dt; //trapezoidal rule + GnInv = 1/(GR2+GC2); //eq. conductance (inverted) + + vC2Hist = 0.0; //Voltage over C2 in previous time step + iC2Hist = 0.0; //Current through C2 in previous time step +} + +#endif + +// ----------------------------------------------- +// Place C code here which runs on the RTDS. The +// code below is entered once each simulation +// step. +// ----------------------------------------------- +#if defined(VILLAS) || SECTION == CODE + +void code() { + //Update source vector + AC2 = iC2Hist+vC2Hist*GC2; + Jn = IntfIn+AC2; + //Solution of the equation Gn*e=Jn; + eSS2 = GnInv*Jn; + //Post step -> calculate the voltage and current for C2 for next step and set interface output + vC2Hist= eSS2; + iC2Hist = vC2Hist*GC2-AC2; + IntfOut = eSS2; +} + +#endif + +// ----------------------------------------------- +// The following code portion is VILLASnode specific +// ----------------------------------------------- +#if defined(VILLAS) + +#include "nodes/cbuilder.h" + +// ----------------------------------------------- +// Place C code here which intializes parameters +// ----------------------------------------------- +void init(struct cbuilder *cb) +{ + R2 = cb->params[0]; + C2 = cb->params[1]; + + dt = cb->timestep; +} + +// ----------------------------------------------- +// Place C code here reads model outputs +// ----------------------------------------------- +int read(float outputs[], int len) +{ + outputs[0] = IntfOut; +} + +// ----------------------------------------------- +// Place C code here which updates model inputs +// ----------------------------------------------- +int write(float inputs[], int len) +{ + IntfIn = inputs[0]; +} + +static struct cbmodel cb = { + .name = "simple_circuit", + .code = code, + .init = init, + .read = read, + .write = write, +}; + +REGISTER_CBMODEL(&cb); + +#endif \ No newline at end of file diff --git a/lib/cbmodels/simple_circuit.c b/lib/cbmodels/simple_circuit.c new file mode 100644 index 000000000..3ab7934b8 --- /dev/null +++ b/lib/cbmodels/simple_circuit.c @@ -0,0 +1,175 @@ +// This is c-code for CBuilder component for Subsystem 2 +// Solver used as in RTDS: Resistive companion (Dommel's algo) +// Subsystem 1 is modelled in RSCAD +// +//% Circuit topology +// % +//% *** Subsystem 1 (SS1) *** % *** Subsystem 2 (SS2) *** +//% % +//% |---------| |---------| % +//% |---------| R1 |-------| L1 |----%-------|---------| +//% | |---------| |---------| % | | +//% | % | | +//% ----- % ----- ----- +//% | + | % | | | | +//% | E | % |C2 | | R2| +//% | - | % | | | | +//% ----- % ----- ----- +//% | % | | +//% |------------------------------------------%------------------ +// % +// % + +// ----------------------------------------------- +// Variables declared here may be used as parameters +// inputs or outputs +// The have to match with whats in Subsystem.h +// ----------------------------------------------- +#if defined(VILLAS) || SECTION == INPUTS +double IntfIn; +#endif + +#if defined(VILLAS) || SECTION == OUTPUTS +double IntfOut; +#endif + +#if defined(VILLAS) || SECTION == PARAMS +double R2; // Resistor [Ohm] in SS2 +double C2; // Capacitance [F] in SS2 +#endif + +// ----------------------------------------------- +// Variables declared here may be used in both the +// RAM: and CODE: sections below. +// ----------------------------------------------- +#if defined(VILLAS) || SECTION == STATIC +double dt; +double GR2, GC2; //Inductances of components +double GnInv; //Inversion of conductance matrix (here only scalar) +double vC2Hist, iC2Hist, AC2; // history meas. and current of dynamic elements +double Jn; //source vector in equation Gn*e=Jn +double eSS2; //node voltage solution +#endif + +// ----------------------------------------------- +// This section should contain any 'c' functions +// to be called from the RAM section (either +// RAM_PASS1 or RAM_PASS2). Example: +// +// static double myFunction(double v1, double v2) +// { +// return(v1*v2); +// } +// ----------------------------------------------- +#if defined(VILLAS) || SECTION == RAM_FUNCTIONS +/* Nothing here */ +#endif + +// ----------------------------------------------- +// Place C code here which computes constants +// required for the CODE: section below. The C +// code here is executed once, prior to the start +// of the simulation case. +// ----------------------------------------------- +#if defined(VILLAS) || SECTION == RAM + +void simple_circuit_ram() { + GR2 = 1/R2; + GC2 = 2*C2/dt; //trapezoidal rule + GnInv = 1/(GR2+GC2); //eq. conductance (inverted) + + vC2Hist = 0.0; //Voltage over C2 in previous time step + iC2Hist = 0.0; //Current through C2 in previous time step +} + +#endif + +// ----------------------------------------------- +// Place C code here which runs on the RTDS. The +// code below is entered once each simulation +// step. +// ----------------------------------------------- +#if defined(VILLAS) || SECTION == CODE + +void simple_circuit_code() { + //Update source vector + AC2 = iC2Hist+vC2Hist*GC2; + Jn = IntfIn+AC2; + //Solution of the equation Gn*e=Jn; + eSS2 = GnInv*Jn; + //Post step -> calculate the voltage and current for C2 for next step and set interface output + vC2Hist= eSS2; + iC2Hist = vC2Hist*GC2-AC2; + IntfOut = eSS2; +} + +#endif + + +// ----------------------------------------------- +// Interface to VILLASnode +// ----------------------------------------------- +#if defined(VILLAS) + +#include "nodes/cbuilder.h" + +double getTimeStep() +{ + return dt; +} + +// ----------------------------------------------- +// Place C code here which intializes parameters +// ----------------------------------------------- +int simple_circuit_init(struct cbuilder *cb) +{ + if (cb->paramlen < 2) + return -1; /* not enough parameters given */ + + R2 = cb->params[0]; + C2 = cb->params[1]; + + /* 'dt' is a special parameter */ + dt = cb->timestep; + + return 0; /* success */ +} + +// ----------------------------------------------- +// Place C code here reads model outputs +// ----------------------------------------------- +int simple_circuit_read(float outputs[], int len) +{ + if (len < 1) + return -1; /* not enough space */ + + outputs[0] = IntfOut; + + return 1; /* 1 value per sample */ +} + +// ----------------------------------------------- +// Place C code here which updates model inputs +// ----------------------------------------------- +int simple_circuit_write(float inputs[], int len) +{ + if (len < 1) + return -1; /* not enough values */ + + IntfIn = inputs[0]; + + return 0; +} + +static struct cbmodel cb = { + .name = "simple_circuit", + .code = simple_circuit_code, + .init = simple_circuit_init, + .read = simple_circuit_read, + .write = simple_circuit_write, + .ram = simple_circuit_ram +}; + +REGISTER_CBMODEL(&cb); + +#endif \ No newline at end of file diff --git a/lib/nodes/cbuilder.c b/lib/nodes/cbuilder.c index e1ef24086..765e119f4 100644 --- a/lib/nodes/cbuilder.c +++ b/lib/nodes/cbuilder.c @@ -7,64 +7,109 @@ **********************************************************************************/ #include "node.h" - -/* Constants from RSCAD */ -#define PI 3.1415926535897932384626433832795 // definition of PI -#define TWOPI 6.283185307179586476925286766559 // definition of 2.0*PI -#define E 2.71828182845904523536028747135266 // definition of E -#define EINV 0.36787944117144232159552377016147 // definition of E Inverse (1/E) -#define RT2 1.4142135623730950488016887242097 // definition of square root 2.0 -#define RT3 1.7320508075688772935274463415059 // definition of square root 3.0 -#define INV_ROOT2 0.70710678118654752440084436210485 +#include "log.h" +#include "nodes/cbuilder.h" -double TimeStep; +struct list cbmodels; /**< Table of existing CBuilder models */ -/* Add your inputs and outputs here */ -double IntfIn; -double IntfOut; +int cbuilder_parse(struct node *n, config_setting_t *cfg) +{ + struct cbuilder *cb = n->_vd; + config_setting_t *cfg_params; -/* Add your parameters here */ -double R2; // Resistor [Ohm] in SS2 -double C2; // Capacitance [F] in SS2 - -#include "cbuilder/static.h" -#include "cbuilder/ram_functions.h" + const char *model; -static double getTimeStep() { - return TimeStep; + if (!config_setting_lookup_float(cfg, "timestep", &cb->timestep)) + cerror(cfg, "CBuilder model requires 'timestep' setting"); + + if (!config_setting_lookup_string(cfg, "model", &model)) + cerror(cfg, "CBuilder model requires 'model' setting"); + + cb->model = list_lookup(&cbmodels, model); + if (!cb->model) + cerror(cfg, "Unknown model '%s'", model); + + cfg_params = config_setting_get_member(cfg, "parameters"); + if (cfg_params) { + if (!config_setting_is_array(cfg_params)) + cerror(cfg_params, "Model parameters must be an array of numbers!"); + + cb->paramlen = config_setting_length(cfg_params); + cb->params = alloc(cb->paramlen * sizeof(double)); + + for (int i = 0; i < cb->paramlen; i++) + cb->params[i] = config_setting_get_float_elem(cfg_params, i); + } + + return 0; } int cbuilder_open(struct node *n) { - /* Initialize parameters */ - R2 = 1; /**< R2 = 1 Ohm */ - C2 = 0.001; /**< C2 = 1000 uF */ + int ret; + struct cbuilder *cb = n->_vd; - TimeStep = 50e-6; + /* Initialize mutex and cv */ + pthread_mutex_init(&cb->mtx, NULL); + pthread_cond_init(&cb->cv, NULL); - #include "cbuilder/ram.h" + /* Currently only a single timestep per model / instance is supported */ + cb->step = 0; + cb->read = 0; + + ret = cb->model->init(cb); + if (ret) + error("Failed to intialize CBuilder model %s", node_name(n)); + + cb->model->ram(); + + return 0; +} + +int cbuilder_close(struct node *n) +{ + struct cbuilder *cb = n->_vd; + + pthread_mutex_destroy(&cb->mtx); + pthread_cond_destroy(&cb->cv); return 0; } int cbuilder_read(struct node *n, struct sample *smps[], unsigned cnt) { + struct cbuilder *cb = n->_vd; struct sample *smp = smps[0]; - smp->values[0].f = IntfOut; + /* Wait for completion of step */ + pthread_mutex_lock(&cb->mtx); + while (cb->read >= cb->step) + pthread_cond_wait(&cb->cv, &cb->mtx); + + smp->length = cb->model->read(&smp->values[0].f, 16); + smp->sequence = cb->step; + + cb->read = cb->step; + + pthread_mutex_unlock(&cb->mtx); return 1; } int cbuilder_write(struct node *n, struct sample *smps[], unsigned cnt) { + struct cbuilder *cb = n->_vd; struct sample *smp = smps[0]; + + pthread_mutex_lock(&cb->mtx); - /* Update inputs */ - IntfIn = smp->values[0].f; + cb->model->write(&smp->values[0].f, smp->length); + cb->model->code(); - /* Start calculation of 1 step */ - #include "cbuilder/code.h" + cb->step++; + + pthread_cond_signal(&cb->cv); + pthread_mutex_unlock(&cb->mtx); return 1; } @@ -73,8 +118,10 @@ static struct node_type vt = { .name = "cbuilder", .description = "RTDS CBuilder model", .vectorize = 1, - .size = 0, + .size = sizeof(struct cbuilder), + .parse = cbuilder_parse, .open = cbuilder_open, + .close = cbuilder_close, .read = cbuilder_read, .write = cbuilder_write, };