Commit 84b4a698 authored by Georg Martin Reinke's avatar Georg Martin Reinke
Browse files

support loading components from CIM in Python; remove unneeded command-line args

parent 8e767e65
......@@ -7,6 +7,7 @@
#include <thread>
#include "CIMReader.h"
#include "PyComponent.h"
#include "PySimulation.h"
#include "Simulation.h"
#include "ShmemInterface.h"
......@@ -16,15 +17,11 @@ using namespace DPsim;
void usage() {
std::cerr << "usage: DPsolver [OPTIONS] CIM_FILE..." << std::endl
<< "Possible options:" << std::endl
<< " -d/--duration DURATION: simulation duration in seconds (default: 0.3)" << std::endl
<< " -f/--frequency FREQUENCY: system frequency in Hz (default: 50)" << std::endl
<< " -h/--help: show this help and exit" << std::endl
<< " -i/--interface OBJ_NAME: prefix for the names of the shmem objects used for communication (default: /dpsim)" << std::endl
<< " -n/--node NODE_ID: RDF id of the node where the interfacing voltage/current source should be placed" << std::endl
<< " -r/--realtime: enable realtime simulation " << std::endl
<< " -p/--python: provide an interactive python shell for simulation control" << std::endl
<< " -s/--split INDEX: index of this instance for distributed simulation (0 or 1)" << std::endl
<< " -t/--timestep TIMESTEP: simulation timestep in seconds (default: 1e-3)" << std::endl;
<< " -s/--split INDEX: index of this instance for distributed simulation (0 or 1)" << std::endl;
}
bool parseFloat(const char *s, double *d) {
......@@ -39,14 +36,21 @@ bool parseInt(const char *s, int *i) {
return (end != s && !*end);
}
static PyMethodDef pyModuleMethods[] = {
{"load_cim", pyLoadCim, METH_VARARGS, "Load a network from CIM file(s)."},
{0}
};
static PyModuleDef dpsimModule = {
PyModuleDef_HEAD_INIT, "dpsim", NULL, -1, NULL,
PyModuleDef_HEAD_INIT, "dpsim", NULL, -1, pyModuleMethods,
NULL, NULL, NULL, NULL
};
static PyObject* PyInit_dpsim(void) {
PyObject* m;
if (PyType_Ready(&PyComponentType) < 0)
return nullptr;
if (PyType_Ready(&PySimulationType) < 0)
return nullptr;
......@@ -56,6 +60,8 @@ static PyObject* PyInit_dpsim(void) {
Py_INCREF(&PySimulationType);
PyModule_AddObject(m, "Simulation", (PyObject*) &PySimulationType);
Py_INCREF(&PyComponentType);
PyModule_AddObject(m, "Component", (PyObject*) &PyComponentType);
return m;
}
......@@ -73,9 +79,8 @@ void doPythonLoop() {
// TODO: that many platform-dependent ifdefs inside main are kind of ugly
int cimMain(int argc, const char* argv[]) {
bool rt = false, python = false;
bool rt = false;
int i, split = -1;
Real frequency = 2*PI*50, timestep = 0.001, duration = 0.3;
std::string interfaceBase = "/dpsim";
std::string splitNode = "";
std::string outName, inName, logName("log.txt"), llogName("lvector.csv"), rlogName("rvector.csv");
......@@ -85,26 +90,7 @@ int cimMain(int argc, const char* argv[]) {
// Parse arguments
for (i = 1; i < argc; i++) {
if (!strcmp(argv[i], "-d") || !strcmp(argv[i], "--duration")) {
if (i == argc-1) {
std::cerr << "Missing argument for -d/--duration; see 'DPsim --help' for usage" << std::endl;
return 1;
}
if (!parseFloat(argv[++i], &duration) || duration <= 0) {
std::cerr << "Invalid setting " << argv[i] << " for the duration" << std::endl;
return 1;
}
} else if (!strcmp(argv[i], "-f") || !strcmp(argv[i], "--frequency")) {
if (i == argc-1) {
std::cerr << "Missing argument for -f/--frequency; see 'DPsim --help' for usage" << std::endl;
return 1;
}
if (!parseFloat(argv[++i], &frequency) || frequency <= 0) {
std::cerr << "Invalid setting " << argv[i] << " for system frequency" << std::endl;
return 1;
}
frequency *= 2*PI;
} else if (!strcmp(argv[i], "-h") || !strcmp(argv[i], "--help")) {
if (!strcmp(argv[i], "-h") || !strcmp(argv[i], "--help")) {
usage();
return 0;
} else if (!strcmp(argv[i], "-i") || !strcmp(argv[i], "--interface")) {
......@@ -123,8 +109,6 @@ int cimMain(int argc, const char* argv[]) {
return 1;
}
splitNode = std::string(argv[++i]);
} else if (!strcmp(argv[i], "-p") || !strcmp(argv[i], "--python")) {
python = true;
} else if (!strcmp(argv[i], "-r") || !strcmp(argv[i], "--realtime")) {
rt = true;
} else if (!strcmp(argv[i], "-s") || !strcmp(argv[i], "--split")) {
......@@ -136,15 +120,6 @@ int cimMain(int argc, const char* argv[]) {
std::cerr << "Invalid setting " << argv[i] << " for the split index" << std::endl;
return 1;
}
} else if (!strcmp(argv[i], "-t") || !strcmp(argv[i], "--timestep")) {
if (i == argc-1) {
std::cerr << "Missing argument for -t/--timestep; see 'DPsim --help' for usage" << std::endl;
return 1;
}
if (!parseFloat(argv[++i], &timestep) || timestep <= 0) {
std::cerr << "Invalied setting " << argv[i] << " for the timestep" << std::endl;
return 1;
}
} else if (argv[i][0] == '-') {
std::cerr << "Unknown option " << argv[i] << " ; see 'DPsim --help' for usage" << std::endl;
return 1;
......@@ -153,10 +128,7 @@ int cimMain(int argc, const char* argv[]) {
break;
}
}
if (i == argc) {
std::cerr << "No input files given (see DPsim --help for usage)" << std::endl;
return 1;
}
// TODO: treat remaining arguments as initial python scripts
#ifndef __linux__
if (split >= 0 || splitNode.length() != 0) {
std::cerr << "Distributed simulation not supported on this platform" << std::endl;
......@@ -166,22 +138,13 @@ int cimMain(int argc, const char* argv[]) {
return 1;
}
#endif
if (python && (split >= 0 || splitNode.length() != 0 || rt)) {
if (split >= 0 || splitNode.length() != 0 || rt) {
std::cerr << "Realtime and distributed simulation currently not supported in combination with Python" << std::endl;
return 1;
}
// Parse CIM files
CIMReader reader(frequency);
for (; i < argc; i++) {
if (!reader.addFile(argv[i])) {
std::cerr << "Failed to read file " << argv[i] << std::endl;
return 1;
}
}
reader.parseFiles();
components = reader.getComponents();
// TODO: RT / shmem interface with python
/*
#ifdef __linux__
// TODO: this is a simple, pretty much fixed setup. Make this more flexible / configurable
if (split >= 0) {
......@@ -216,14 +179,11 @@ int cimMain(int argc, const char* argv[]) {
}
}
#endif
// Do the actual simulation
Logger log(logName), llog(llogName), rlog(rlogName);
Simulation sim(components, frequency, timestep, duration, log);
*/
if (python) {
doPythonLoop();
} else {
doPythonLoop();
/*
#ifdef __linux__
if (intf)
sim.addExternalInterface(intf);
......@@ -237,11 +197,8 @@ int cimMain(int argc, const char* argv[]) {
if (intf)
delete intf;
#else
while (sim.step(log, llog, rlog))
sim.increaseByTimeStep();
#endif
}
*/
return 0;
}
......
#include "PyComponent.h"
#include "CIMReader.h"
using namespace DPsim;
PyTypeObject DPsim::PyComponentType = {
PyVarObject_HEAD_INIT(NULL, 0)
"dpsim.Component", /* tp_name */
sizeof(PyComponent), /* tp_basicsize */
0, /* tp_itemsize */
(destructor)PyComponent::dealloc, /* tp_dealloc */
0, /* tp_print */
0, /* tp_getattr */
0, /* tp_setattr */
0, /* tp_reserved */
0, /* tp_repr */
0, /* tp_as_number */
0, /* tp_as_sequence */
0, /* tp_as_mapping */
0, /* tp_hash */
0, /* tp_call */
0, /* tp_str */
0, /* tp_getattro */
0, /* tp_setattro */
0, /* tp_as_buffer */
Py_TPFLAGS_DEFAULT |
Py_TPFLAGS_BASETYPE, /* tp_flags */
"A component in a simulation.", /* tp_doc */
0, /* tp_traverse */
0, /* tp_clear */
0, /* tp_richcompare */
0, /* tp_weaklistoffset */
0, /* tp_iter */
0, /* tp_iternext */
0, /* tp_methods */
0, /* tp_members */
0, /* tp_getset */
0, /* tp_base */
0, /* tp_dict */
0, /* tp_descr_get */
0, /* tp_descr_set */
0, /* tp_dictoffset */
0, /* tp_init */
0, /* tp_alloc */
PyComponent::newfunc, /* tp_new */
};
PyObject* PyComponent::newfunc(PyTypeObject* type, PyObject *args, PyObject *kwds) {
PyComponent* self = (PyComponent*) type->tp_alloc(type, 0);
if (self)
self->comp = nullptr;
return (PyObject*) self;
}
void PyComponent::dealloc(PyComponent* self) {
if (self->comp)
delete self->comp;
Py_TYPE(self)->tp_free((PyObject*)self);
}
bool DPsim::compsFromPython(PyObject* list, std::vector<BaseComponent*>& comps) {
if (!PyList_Check(list))
return false;
for (int i = 0; i < PyList_Size(list); i++) {
PyObject* obj = PyList_GetItem(list, i);
if (!PyObject_TypeCheck(obj, &PyComponentType)) {
comps.clear();
return false;
}
PyComponent* pyComp = (PyComponent*) obj;
comps.push_back(pyComp->comp);
}
return true;
}
PyObject* DPsim::pyLoadCim(PyObject* self, PyObject* args) {
double frequency = 50;
PyObject *list;
PyBytesObject *filename;
CIMReader *reader;
if (PyArg_ParseTuple(args, "O&|d", PyUnicode_FSConverter, &filename, &frequency)) {
reader = new CIMReader(2*PI*frequency);
reader->addFile(PyBytes_AsString((PyObject*) filename));
Py_DECREF(filename);
} else if (PyArg_ParseTuple(args, "O|d", &list, &frequency)) {
PyErr_Clear();
if (!PyList_Check(list)) {
PyErr_SetString(PyExc_TypeError, "First argument must be filename or list of filenames");
return nullptr;
}
reader = new CIMReader(2*PI*frequency);
for (int i = 0; i < PyList_Size(list); i++) {
if (!PyUnicode_FSConverter(PyList_GetItem(list, i), &filename)) {
delete reader;
PyErr_SetString(PyExc_TypeError, "First argument must be filename or list of filenames");
return nullptr;
}
reader->addFile(PyBytes_AsString((PyObject*) filename));
Py_DECREF(filename);
}
} else {
PyErr_SetString(PyExc_TypeError, "First argument must be filename or list of filenames");
return nullptr;
}
reader->parseFiles();
std::vector<BaseComponent*> comps = reader->getComponents();
list = PyList_New(comps.size());
for (int i = 0; i < comps.size(); i++) {
// corresponds to "pyComp = dpsim.Component()" in Python
PyObject *emptyTuple = PyTuple_New(0);
PyComponent* pyComp = (PyComponent*) PyObject_CallObject((PyObject*) &PyComponentType, emptyTuple);
Py_DECREF(emptyTuple);
if (!pyComp) {
Py_DECREF(list);
delete reader;
return nullptr;
}
/*
PyComponent* pyComp = PyObject_New(PyComponent, &PyComponentType);
PyObject_Init((PyObject*) pyComp, &PyComponentType);
*/
pyComp->comp = comps[i];
PyList_SET_ITEM(list, i, (PyObject*) pyComp);
}
delete reader;
return list;
}
#ifndef PYCOMPONENT_H
#define PYCOMPONENT_H
#include <Python.h>
#include <vector>
#include "Components/BaseComponent.h"
namespace DPsim {
struct PyComponent {
PyObject_HEAD
BaseComponent* comp;
static PyObject* newfunc(PyTypeObject* type, PyObject *args, PyObject *kwds);
static void dealloc(PyComponent*);
};
extern PyTypeObject PyComponentType;
bool compsFromPython(PyObject* list, std::vector<BaseComponent*>& comps);
PyObject* pyLoadCim(PyObject* self, PyObject* args);
};
#endif
#include "PySimulation.h"
#include "PyComponent.h"
#include <cfloat>
#include <iostream>
using namespace DPsim;
std::vector<BaseComponent*> DPsim::components;
static PyMethodDef PySimulation_methods[] = {
{"lvector", PySimulation::lvector, METH_NOARGS, "Returns the left-side vector from the last step."},
{"start", PySimulation::start, METH_NOARGS, "Start the simulation, or resume if it is paused."},
{"step", PySimulation::step, METH_NOARGS, "Perform a single simulation step."},
{"stop", PySimulation::stop, METH_NOARGS, "Cancel the running simulation."},
{"pause", PySimulation::pause, METH_NOARGS, "Pause the already running simulation."},
{"wait", PySimulation::wait, METH_NOARGS, "Wait for the simulation to finish."},
{NULL, NULL, 0, NULL}
};
PyTypeObject DPsim::PySimulationType = {
PyVarObject_HEAD_INIT(NULL, 0)
"dpsim.Simulation", /* tp_name */
sizeof(PySimulation), /* tp_basicsize */
0, /* tp_itemsize */
(destructor)PySimulation::dealloc, /* tp_dealloc */
0, /* tp_print */
0, /* tp_getattr */
0, /* tp_setattr */
0, /* tp_reserved */
0, /* tp_repr */
0, /* tp_as_number */
0, /* tp_as_sequence */
0, /* tp_as_mapping */
0, /* tp_hash */
0, /* tp_call */
0, /* tp_str */
0, /* tp_getattro */
0, /* tp_setattro */
0, /* tp_as_buffer */
Py_TPFLAGS_DEFAULT |
Py_TPFLAGS_BASETYPE, /* tp_flags */
"A single simulation.", /* tp_doc */
0, /* tp_traverse */
0, /* tp_clear */
0, /* tp_richcompare */
0, /* tp_weaklistoffset */
0, /* tp_iter */
0, /* tp_iternext */
PySimulation_methods, /* tp_methods */
0, /* tp_members */
0, /* tp_getset */
0, /* tp_base */
0, /* tp_dict */
0, /* tp_descr_get */
0, /* tp_descr_set */
0, /* tp_dictoffset */
(initproc)PySimulation::init, /* tp_init */
0, /* tp_alloc */
PySimulation::newfunc, /* tp_new */
};
void PySimulation::simThreadFunction(PySimulation* pySim) {
bool notDone = true;
......@@ -46,18 +97,23 @@ PyObject* PySimulation::newfunc(PyTypeObject* type, PyObject *args, PyObject *kw
}
int PySimulation::init(PySimulation* self, PyObject *args, PyObject *kwds) {
static char *kwlist[] = {"frequency", "timestep", "duration", "log", NULL};
static char *kwlist[] = {"components", "frequency", "timestep", "duration", "log", NULL};
double frequency = 50, timestep = 1e-3, duration = DBL_MAX;
const char *log = nullptr;
if (!PyArg_ParseTupleAndKeywords(args, kwds, "|ddds", kwlist,
&frequency, &timestep, &duration, &log))
if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|ddds", kwlist,
&self->pyComps, &frequency, &timestep, &duration, &log))
return -1;
if (!compsFromPython(self->pyComps, self->comps)) {
PyErr_SetString(PyExc_TypeError, "Invalid components argument (must by list of dpsim.Component)");
return -1;
}
Py_INCREF(self->pyComps);
if (log)
self->log = new Logger(log);
else
self->log = new Logger();
self->sim = new Simulation(components, 2*PI*frequency, timestep, duration, *self->log);
self->sim = new Simulation(self->comps, 2*PI*frequency, timestep, duration, *self->log);
return 0;
};
......@@ -69,12 +125,20 @@ void PySimulation::dealloc(PySimulation* self) {
self->simThread->join();
delete self->simThread;
}
if (self->sim)
delete self->sim;
if (self->log)
delete self->log;
delete self->mut;
delete self->cond;
// Since this is not a C++ destructor which would automatically call the
// destructor of its members, we have to manually call the destructor of
// the component vector here to free the associated memory.
self->comps.~vector<BaseComponent*>();
Py_XDECREF(self->pyComps);
Py_TYPE(self)->tp_free((PyObject*)self);
}
......
......@@ -32,6 +32,9 @@ namespace DPsim {
std::atomic_int sigPause, numStep;
std::thread *simThread;
SimState state;
PyObject* pyComps; // Components as a (Python) list of PyComponents
std::vector<BaseComponent*> comps;
// Function executed by the simulation thread
static void simThreadFunction(PySimulation* pySim);
......@@ -53,58 +56,6 @@ namespace DPsim {
static PyObject* wait(PyObject *self, PyObject *args);
};
static PyMethodDef PySimulation_methods[] = {
{"lvector", PySimulation::lvector, METH_NOARGS, "Returns the left-side vector from the last step."},
{"start", PySimulation::start, METH_NOARGS, "Start the simulation, or resume if it is paused."},
{"step", PySimulation::step, METH_NOARGS, "Perform a single simulation step."},
{"stop", PySimulation::stop, METH_NOARGS, "Cancel the running simulation."},
{"pause", PySimulation::pause, METH_NOARGS, "Pause the already running simulation."},
{"wait", PySimulation::wait, METH_NOARGS, "Wait for the simulation to finish."},
{NULL, NULL, 0, NULL}
};
static PyTypeObject PySimulationType = {
PyVarObject_HEAD_INIT(NULL, 0)
"dpsim.Simulation", /* tp_name */
sizeof(PySimulation), /* tp_basicsize */
0, /* tp_itemsize */
(destructor)PySimulation::dealloc, /* tp_dealloc */
0, /* tp_print */
0, /* tp_getattr */
0, /* tp_setattr */
0, /* tp_reserved */
0, /* tp_repr */
0, /* tp_as_number */
0, /* tp_as_sequence */
0, /* tp_as_mapping */
0, /* tp_hash */
0, /* tp_call */
0, /* tp_str */
0, /* tp_getattro */
0, /* tp_setattro */
0, /* tp_as_buffer */
Py_TPFLAGS_DEFAULT |
Py_TPFLAGS_BASETYPE, /* tp_flags */
"A single simulation.", /* tp_doc */
0, /* tp_traverse */
0, /* tp_clear */
0, /* tp_richcompare */
0, /* tp_weaklistoffset */
0, /* tp_iter */
0, /* tp_iternext */
PySimulation_methods, /* tp_methods */
0, /* tp_members */
0, /* tp_getset */
0, /* tp_base */
0, /* tp_dict */
0, /* tp_descr_get */
0, /* tp_descr_set */
0, /* tp_dictoffset */
(initproc)PySimulation::init, /* tp_init */
0, /* tp_alloc */
PySimulation::newfunc, /* tp_new */
};
extern std::vector<BaseComponent*> components;
extern PyTypeObject PySimulationType;
};
#endif
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment