Skip to content
GitLab
Menu
Projects
Groups
Snippets
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Sign in
Toggle navigation
Menu
Open sidebar
ACS
Public
Power System Simulation and Optimization
DPsim
DPsim
Commits
2c7053e9
Commit
2c7053e9
authored
Sep 13, 2017
by
Georg Martin Reinke
Browse files
encapsulate simulation state / communication in a Python class
parent
50c7da4e
Changes
3
Hide whitespace changes
Inline
Side-by-side
Source/DPsimMain.cpp
View file @
2c7053e9
...
...
@@ -7,6 +7,7 @@
#include
<thread>
#include
"CIMReader.h"
#include
"PySimulation.h"
#include
"Simulation.h"
#include
"ShmemInterface.h"
...
...
@@ -38,153 +39,27 @@ bool parseInt(const char *s, int *i) {
return
(
end
!=
s
&&
!*
end
);
}
std
::
vector
<
BaseComponent
*>
components
;
enum
SimState
{
StateStopped
=
0
,
StateRunning
,
StatePaused
,
StateDone
};
static
std
::
thread
*
simThread
;
struct
SimContext
{
Simulation
&
sim
;
Logger
&
log
,
&
llog
,
&
rlog
;
std
::
condition_variable
cond
;
std
::
mutex
mut
;
std
::
atomic_int
stop
,
numStep
;
SimState
state
;
SimContext
(
Simulation
&
,
Logger
&
,
Logger
&
,
Logger
&
);
static
PyModuleDef
dpsimModule
=
{
PyModuleDef_HEAD_INIT
,
"dpsim"
,
NULL
,
-
1
,
NULL
,
NULL
,
NULL
,
NULL
,
NULL
};
SimContext
::
SimContext
(
Simulation
&
sim
,
Logger
&
log
,
Logger
&
llog
,
Logger
&
rlog
)
:
sim
(
sim
),
log
(
log
),
llog
(
llog
),
rlog
(
rlog
),
mut
(),
cond
()
{
this
->
stop
=
0
;
this
->
numStep
=
0
;
this
->
state
=
StateStopped
;
}
static
SimContext
*
globalCtx
;
static
void
simThreadFunction
(
SimContext
*
ctx
)
{
bool
running
=
true
;
std
::
unique_lock
<
std
::
mutex
>
lk
(
ctx
->
mut
,
std
::
defer_lock
);
ctx
->
numStep
=
0
;
while
(
running
)
{
running
=
ctx
->
sim
.
step
(
ctx
->
log
,
ctx
->
llog
,
ctx
->
rlog
);
ctx
->
numStep
++
;
ctx
->
sim
.
increaseByTimeStep
();
if
(
ctx
->
stop
)
{
lk
.
lock
();
ctx
->
state
=
StatePaused
;
ctx
->
cond
.
notify_one
();
ctx
->
cond
.
wait
(
lk
);
ctx
->
state
=
StateRunning
;
lk
.
unlock
();
}
}
lk
.
lock
();
ctx
->
state
=
StateDone
;
ctx
->
cond
.
notify_one
();
}
static
PyObject
*
pythonStart
(
PyObject
*
self
,
PyObject
*
args
)
{
std
::
unique_lock
<
std
::
mutex
>
lk
(
globalCtx
->
mut
);
if
(
globalCtx
->
state
==
StateRunning
)
{
PyErr_SetString
(
PyExc_SystemError
,
"Simulation already started"
);
return
nullptr
;
}
else
if
(
globalCtx
->
state
==
StateDone
)
{
PyErr_SetString
(
PyExc_SystemError
,
"Simulation already finished"
);
return
nullptr
;
}
else
if
(
globalCtx
->
state
==
StatePaused
)
{
globalCtx
->
stop
=
0
;
globalCtx
->
cond
.
notify_one
();
}
else
{
globalCtx
->
stop
=
0
;
globalCtx
->
state
=
StateRunning
;
simThread
=
new
std
::
thread
(
simThreadFunction
,
globalCtx
);
}
Py_INCREF
(
Py_None
);
return
Py_None
;
}
static
PyObject
*
PyInit_dpsim
(
void
)
{
PyObject
*
m
;
static
PyObject
*
pythonStep
(
PyObject
*
self
,
PyObject
*
args
)
{
std
::
unique_lock
<
std
::
mutex
>
lk
(
globalCtx
->
mut
);
int
oldStep
=
globalCtx
->
numStep
;
if
(
globalCtx
->
state
==
StateStopped
)
{
globalCtx
->
state
=
StateRunning
;
globalCtx
->
stop
=
1
;
simThread
=
new
std
::
thread
(
simThreadFunction
,
globalCtx
);
}
else
if
(
globalCtx
->
state
==
StatePaused
)
{
globalCtx
->
stop
=
1
;
globalCtx
->
cond
.
notify_one
();
}
else
if
(
globalCtx
->
state
==
StateDone
)
{
PyErr_SetString
(
PyExc_SystemError
,
"Simulation already finished"
);
return
nullptr
;
}
else
{
PyErr_SetString
(
PyExc_SystemError
,
"Simulation currently running"
);
if
(
PyType_Ready
(
&
PySimulationType
)
<
0
)
return
nullptr
;
}
while
(
globalCtx
->
numStep
==
oldStep
)
globalCtx
->
cond
.
wait
(
lk
);
Py_INCREF
(
Py_None
);
return
Py_None
;
}
static
PyObject
*
pythonPause
(
PyObject
*
self
,
PyObject
*
args
)
{
std
::
unique_lock
<
std
::
mutex
>
lk
(
globalCtx
->
mut
);
if
(
globalCtx
->
state
!=
StateRunning
)
{
PyErr_SetString
(
PyExc_SystemError
,
"Simulation not currently running"
);
m
=
PyModule_Create
(
&
dpsimModule
);
if
(
!
m
)
return
nullptr
;
}
globalCtx
->
stop
=
1
;
globalCtx
->
cond
.
notify_one
();
while
(
globalCtx
->
state
==
StateRunning
)
globalCtx
->
cond
.
wait
(
lk
);
Py_INCREF
(
Py_None
);
return
Py_None
;
}
static
PyObject
*
pythonWait
(
PyObject
*
self
,
PyObject
*
args
)
{
std
::
unique_lock
<
std
::
mutex
>
lk
(
globalCtx
->
mut
);
if
(
globalCtx
->
state
==
StateDone
)
{
Py_INCREF
(
Py_None
);
return
Py_None
;
}
else
if
(
globalCtx
->
state
==
StateStopped
)
{
PyErr_SetString
(
PyExc_SystemError
,
"Simulation not currently running"
);
return
nullptr
;
}
else
if
(
globalCtx
->
state
==
StatePaused
)
{
PyErr_SetString
(
PyExc_SystemError
,
"Simulation currently paused"
);
return
nullptr
;
}
while
(
globalCtx
->
state
==
StateRunning
)
globalCtx
->
cond
.
wait
(
lk
);
Py_INCREF
(
Py_None
);
return
Py_None
;
}
static
PyMethodDef
pythonMethods
[]
=
{
{
"start"
,
pythonStart
,
METH_VARARGS
,
"Start the simulation, or resume if it is paused."
},
{
"step"
,
pythonStep
,
METH_VARARGS
,
"Perform a single simulation step."
},
{
"pause"
,
pythonPause
,
METH_VARARGS
,
"Pause the already running simulation."
},
{
"wait"
,
pythonWait
,
METH_VARARGS
,
"Wait for the simulation to finish."
},
{
NULL
,
NULL
,
0
,
NULL
}
};
static
PyModuleDef
dpsimModule
=
{
PyModuleDef_HEAD_INIT
,
"dpsim"
,
NULL
,
-
1
,
pythonMethods
,
NULL
,
NULL
,
NULL
,
NULL
};
static
PyObject
*
PyInit_dpsim
(
void
)
{
return
PyModule_Create
(
&
dpsimModule
);
Py_INCREF
(
&
PySimulationType
);
PyModule_AddObject
(
m
,
"Simulation"
,
(
PyObject
*
)
&
PySimulationType
);
return
m
;
}
int
doPythonLoop
(
SimContext
*
sim
)
{
void
doPythonLoop
()
{
PyImport_AppendInittab
(
"dpsim"
,
&
PyInit_dpsim
);
Py_Initialize
();
...
...
@@ -347,8 +222,7 @@ int cimMain(int argc, const char* argv[]) {
Simulation
sim
(
components
,
frequency
,
timestep
,
duration
,
log
);
if
(
python
)
{
globalCtx
=
new
SimContext
(
sim
,
log
,
llog
,
rlog
);
doPythonLoop
(
globalCtx
);
doPythonLoop
();
}
else
{
#ifdef __linux__
if
(
intf
)
...
...
Source/PySimulation.cpp
0 → 100644
View file @
2c7053e9
#include
"PySimulation.h"
using
namespace
DPsim
;
void
PySimulation
::
simThreadFunction
(
PySimulation
*
pySim
)
{
bool
notDone
=
true
;
std
::
unique_lock
<
std
::
mutex
>
lk
(
*
pySim
->
mut
,
std
::
defer_lock
);
pySim
->
numStep
=
0
;
while
(
pySim
->
running
&&
notDone
)
{
notDone
=
pySim
->
sim
->
step
(
*
pySim
->
log
);
pySim
->
numStep
++
;
pySim
->
sim
->
increaseByTimeStep
();
if
(
pySim
->
sigPause
)
{
lk
.
lock
();
pySim
->
state
=
StatePaused
;
pySim
->
cond
->
notify_one
();
pySim
->
cond
->
wait
(
lk
);
pySim
->
state
=
StateRunning
;
lk
.
unlock
();
}
}
lk
.
lock
();
pySim
->
state
=
StateDone
;
pySim
->
cond
->
notify_one
();
}
PyObject
*
PySimulation
::
newfunc
(
PyTypeObject
*
type
,
PyObject
*
args
,
PyObject
*
kwds
)
{
PySimulation
*
self
;
self
=
(
PySimulation
*
)
type
->
tp_alloc
(
type
,
0
);
if
(
self
)
{
// since mutex, thread etc. have no copy-constructor, but we can't use
// our own C++ constructor that could be called from python, we need to
// implement them as pointers
self
->
cond
=
new
std
::
condition_variable
();
self
->
mut
=
new
std
::
mutex
();
}
return
(
PyObject
*
)
self
;
}
int
PySimulation
::
init
(
PySimulation
*
self
,
PyObject
*
args
,
PyObject
*
kwds
)
{
// TODO: actually parse arguments (frequency, timestep etc.)
self
->
log
=
new
Logger
(
"log.txt"
);
self
->
sim
=
new
Simulation
(
components
,
2
*
PI
*
50
,
1e-3
,
0.3
,
*
self
->
log
);
return
0
;
};
void
PySimulation
::
dealloc
(
PySimulation
*
self
)
{
if
(
self
->
simThread
)
{
// We have to cancel the running thread here, because otherwise self can't
// be freed.
PySimulation
::
stop
((
PyObject
*
)
self
,
NULL
);
delete
self
->
simThread
;
}
if
(
self
->
sim
)
delete
self
->
sim
;
delete
self
->
mut
;
delete
self
->
cond
;
Py_TYPE
(
self
)
->
tp_free
((
PyObject
*
)
self
);
}
PyObject
*
PySimulation
::
start
(
PyObject
*
self
,
PyObject
*
args
)
{
PySimulation
*
pySim
=
(
PySimulation
*
)
self
;
std
::
unique_lock
<
std
::
mutex
>
lk
(
*
pySim
->
mut
);
if
(
pySim
->
state
==
StateRunning
)
{
PyErr_SetString
(
PyExc_SystemError
,
"Simulation already started"
);
return
nullptr
;
}
else
if
(
pySim
->
state
==
StateDone
)
{
PyErr_SetString
(
PyExc_SystemError
,
"Simulation already finished"
);
return
nullptr
;
}
else
if
(
pySim
->
state
==
StatePaused
)
{
pySim
->
sigPause
=
0
;
pySim
->
cond
->
notify_one
();
}
else
{
pySim
->
sigPause
=
0
;
pySim
->
state
=
StateRunning
;
pySim
->
running
=
true
;
pySim
->
simThread
=
new
std
::
thread
(
simThreadFunction
,
pySim
);
}
Py_INCREF
(
Py_None
);
return
Py_None
;
}
PyObject
*
PySimulation
::
step
(
PyObject
*
self
,
PyObject
*
args
)
{
PySimulation
*
pySim
=
(
PySimulation
*
)
self
;
std
::
unique_lock
<
std
::
mutex
>
lk
(
*
pySim
->
mut
);
int
oldStep
=
pySim
->
numStep
;
if
(
pySim
->
state
==
StateStopped
)
{
pySim
->
state
=
StateRunning
;
pySim
->
sigPause
=
1
;
pySim
->
running
=
true
;
pySim
->
simThread
=
new
std
::
thread
(
simThreadFunction
,
pySim
);
}
else
if
(
pySim
->
state
==
StatePaused
)
{
pySim
->
sigPause
=
1
;
pySim
->
cond
->
notify_one
();
}
else
if
(
pySim
->
state
==
StateDone
)
{
PyErr_SetString
(
PyExc_SystemError
,
"Simulation already finished"
);
return
nullptr
;
}
else
{
PyErr_SetString
(
PyExc_SystemError
,
"Simulation currently running"
);
return
nullptr
;
}
while
(
pySim
->
numStep
==
oldStep
)
pySim
->
cond
->
wait
(
lk
);
Py_INCREF
(
Py_None
);
return
Py_None
;
}
PyObject
*
PySimulation
::
stop
(
PyObject
*
self
,
PyObject
*
args
)
{
PySimulation
*
pySim
=
(
PySimulation
*
)
self
;
std
::
unique_lock
<
std
::
mutex
>
lk
(
*
pySim
->
mut
);
pySim
->
running
=
false
;
while
(
pySim
->
state
==
StateRunning
)
pySim
->
cond
->
wait
(
lk
);
Py_INCREF
(
Py_None
);
return
Py_None
;
}
PyObject
*
PySimulation
::
pause
(
PyObject
*
self
,
PyObject
*
args
)
{
PySimulation
*
pySim
=
(
PySimulation
*
)
self
;
std
::
unique_lock
<
std
::
mutex
>
lk
(
*
pySim
->
mut
);
if
(
pySim
->
state
!=
StateRunning
)
{
PyErr_SetString
(
PyExc_SystemError
,
"Simulation not currently running"
);
return
nullptr
;
}
pySim
->
sigPause
=
1
;
pySim
->
cond
->
notify_one
();
while
(
pySim
->
state
==
StateRunning
)
pySim
->
cond
->
wait
(
lk
);
Py_INCREF
(
Py_None
);
return
Py_None
;
}
PyObject
*
PySimulation
::
wait
(
PyObject
*
self
,
PyObject
*
args
)
{
PySimulation
*
pySim
=
(
PySimulation
*
)
self
;
std
::
unique_lock
<
std
::
mutex
>
lk
(
*
pySim
->
mut
);
if
(
pySim
->
state
==
StateDone
)
{
Py_INCREF
(
Py_None
);
return
Py_None
;
}
else
if
(
pySim
->
state
==
StateStopped
)
{
PyErr_SetString
(
PyExc_SystemError
,
"Simulation not currently running"
);
return
nullptr
;
}
else
if
(
pySim
->
state
==
StatePaused
)
{
PyErr_SetString
(
PyExc_SystemError
,
"Simulation currently paused"
);
return
nullptr
;
}
while
(
pySim
->
state
==
StateRunning
)
pySim
->
cond
->
wait
(
lk
);
Py_INCREF
(
Py_None
);
return
Py_None
;
}
Source/PySimulation.h
0 → 100644
View file @
2c7053e9
#ifndef PYSIMULATION_H
#define PYSIMULATION_H
#include
<Python.h>
#include
<atomic>
#include
<condition_variable>
#include
<mutex>
#include
<thread>
#include
<vector>
#include
"Components/BaseComponent.h"
#include
"Simulation.h"
namespace
DPsim
{
enum
SimState
{
StateStopped
=
0
,
StateRunning
,
StatePaused
,
StateDone
};
struct
PySimulation
{
PyObject_HEAD
Simulation
*
sim
;
Logger
*
log
;
// TODO other loggers
std
::
condition_variable
*
cond
;
std
::
mutex
*
mut
;
std
::
atomic_bool
running
;
std
::
atomic_int
sigPause
,
numStep
;
std
::
thread
*
simThread
;
SimState
state
;
// Function executed by the simulation thread
static
void
simThreadFunction
(
PySimulation
*
pySim
);
// The Python API has no notion of C++ classes and methods, so the methods
// that can be called from Python are static.
//
// Helper methods for memory management / initialization etc.
static
PyObject
*
newfunc
(
PyTypeObject
*
type
,
PyObject
*
args
,
PyObject
*
kwds
);
static
int
init
(
PySimulation
*
self
,
PyObject
*
args
,
PyObject
*
kwds
);
static
void
dealloc
(
PySimulation
*
);
// Methods that are actually available from Python
static
PyObject
*
start
(
PyObject
*
self
,
PyObject
*
args
);
static
PyObject
*
step
(
PyObject
*
self
,
PyObject
*
args
);
static
PyObject
*
stop
(
PyObject
*
self
,
PyObject
*
args
);
static
PyObject
*
pause
(
PyObject
*
self
,
PyObject
*
args
);
static
PyObject
*
wait
(
PyObject
*
self
,
PyObject
*
args
);
};
static
PyMethodDef
PySimulation_methods
[]
=
{
{
"start"
,
PySimulation
::
start
,
METH_VARARGS
,
"Start the simulation, or resume if it is paused."
},
{
"step"
,
PySimulation
::
step
,
METH_VARARGS
,
"Perform a single simulation step."
},
{
"stop"
,
PySimulation
::
stop
,
METH_VARARGS
,
"Cancel the running simulation."
},
{
"pause"
,
PySimulation
::
pause
,
METH_VARARGS
,
"Pause the already running simulation."
},
{
"wait"
,
PySimulation
::
wait
,
METH_VARARGS
,
"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 */
};
static
std
::
vector
<
BaseComponent
*>
components
;
};
#endif
Write
Preview
Supports
Markdown
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment