Commit a96e6215 authored by Markus Mirz's avatar Markus Mirz
Browse files

Merge branch 'fix-feature-notebooks' into 'master'

Fix feature notebooks

Closes #107

See merge request acs/public/simulation/dpsim!83
parents 84b0b808 55e152f4
Subproject commit adac52c0b39412b0e300e3e6e9638cd35646149d
Subproject commit 2f2f246aee96a2f213a1ccceec3d392a3f0a06a0
%% Cell type:markdown id: tags:
# Asynchronous execution
%% Cell type:markdown id: tags:
DPsim integrates well with event loop implementation like [asyncio](https://docs.python.org/3/library/asyncio.html).
This allows the user to run simulations as a coroutine asynchronously in the background.
%% Cell type:markdown id: tags:
## Example 1
%% Cell type:markdown id: tags:
We start by defining a very simple simulation:
%% Cell type:code id: tags:
``` python
import time
import asyncio
import dpsim
from dpsim.EventChannel import Event
# Nodes
gnd = dpsim.dp.Node.GND()
n1 = dpsim.dp.Node("n1")
# Components
v1 = dpsim.dp.ph1.VoltageSource("v_1", [gnd, n1], V_ref=complex(345,0))
sys = dpsim.SystemTopology(50, [gnd, n1], [v1])
```
%% Cell type:markdown id: tags:
The <code>dpsim.Simulation</code> class has a function called `coro simulate()` which returns a coroutine.
this co-routine can be started in the background via:
%% Cell type:code id: tags:
``` python
sims = []
for i in range(1, 4):
sim = dpsim.RealTimeSimulation("async_demo_%d" % i, sys, timestep = i * 1e-3, duration=3*i, pbar=True)
sim = dpsim.RealTimeSimulation("async_demo_%d" % i, sys, timestep=i*1e-3, duration=3*i+5, pbar=True)
sim.start()
sims += [sim]
for i in range(1, 6):
print("Doing something different: %d" % i)
await asyncio.sleep(1)
_ = await asyncio.wait([ s.wait(Event.done) for s in sims ])
```
%% Output
Doing something different: 1
Doing something different: 2
Doing something different: 3
Doing something different: 4
Doing something different: 5
......
This diff is collapsed.
%% Cell type:markdown id: tags:
# Progressbars
%% Cell type:markdown id: tags:
During long running or real-time simulations it is useful to have a feedback from the running simulation about its current state.
DPsim can show its current progress
%% Cell type:markdown id: tags:
## Example 1
%% Cell type:code id: tags:
``` python
import dpsim
# Nodes
gnd = dpsim.dp.Node.GND()
n1 = dpsim.dp.Node("n1")
n2 = dpsim.dp.Node("n2")
n3 = dpsim.dp.Node("n3")
n4 = dpsim.dp.Node("n4")
# Components
v1 = dpsim.dp.ph1.VoltageSource("v_1", [gnd, n1], V_ref=complex(345,0))
r1 = dpsim.dp.ph1.Resistor("r1", [n1, n2], R=5)
c1 = dpsim.dp.ph1.Capacitor("c_1", [n2, gnd], C=0.002)
rL1 = dpsim.dp.ph1.Resistor("r_load1", [n2, n4], R=6.4)
l1 = dpsim.dp.ph1.Inductor("l_1", [n4, n3], L=0.186)
c2 = dpsim.dp.ph1.Capacitor("c_2", [n3, gnd], C=0.002)
rL2 = dpsim.dp.ph1.Resistor("r_load2", [n3, gnd], R=150)
sys = dpsim.SystemTopology(50, [gnd, n1, n2, n3, n4], [v1, r1, c1, rL1, l1, c2, rL2])
sim = dpsim.Simulation("progress_demo1", sys, duration=20, timestep=0.0005)
```
%% Cell type:code id: tags:
``` python
sim.show_progressbar()
```
%% Output
%% Cell type:code id: tags:
``` python
await sim.simulate()
```
%% Cell type:markdown id: tags:
## Example 2
%% Cell type:markdown id: tags:
Progressbars are also supported for simulations which are executed as a co-routine
%% Cell type:code id: tags:
``` python
import asyncio
async def dummy():
for i in range(1,10):
await asyncio.sleep(1)
print('Doing something different:', i)
sim2 = dpsim.Simulation("progress_demo2", sys, duration=20, timestep=0.00005)
sim2.show_progressbar()
await asyncio.gather(sim2.simulate(), dummy())
```
%% Output
Doing something different: 1
Doing something different: 2
Doing something different: 3
Doing something different: 4
Doing something different: 5
Doing something different: 6
Doing something different: 7
Doing something different: 8
Doing something different: 9
[None, None]
%% Cell type:code id: tags:
``` python
```
......
%% Cell type:markdown id: tags:
# Widgets
This example uses slider widgets to adjust parameters
In this example we will use the [iPyWidgets Jupyter](https://ipywidgets.readthedocs.io) extension to interactively tune simulation or model parameters.
The user will see immediatly the impact of parameter changes as the simulation results are continously updated.
This example uses slider widgets to adjust parameters:
%% Cell type:code id: tags:
``` python
import dpsim
import math, cmath
import time
import os
import matplotlib as plt
import villas.dataprocessing.plottools as pt
import villas.dataprocessing.readtools as rt
%matplotlib inline
%config InlineBackend.figure_format = 'svg'
```
%% Cell type:markdown id: tags:
We start by defining our system topology:
%% Cell type:code id: tags:
``` python
gnd = dpsim.dp.Node.GND()
n1 = dpsim.dp.Node("n1")
n2 = dpsim.dp.Node("n2")
cs = dpsim.dp.ph1.CurrentSource("cs", [gnd, n1])
r1 = dpsim.dp.ph1.Resistor("r_1", [n1, gnd])
c1 = dpsim.dp.ph1.Capacitor("c_1", [n1, n2])
l1 = dpsim.dp.ph1.Inductor("l_1", [n2, gnd])
r2 = dpsim.dp.ph1.Resistor("r_2", [n2, gnd])
sys = dpsim.SystemTopology(50, [gnd, n1, n2], [cs, r1, c1, l1, r2])
sim = dpsim.Simulation("Widgets", sys, duration=0.1)
logger = dpsim.Logger("Widgets")
logger.log_attribute(n1, "v") # v1
logger.log_attribute(n2, "v") # v2
logger.log_attribute(cs, "i_intf") # i12
logger.log_attribute(c1, "i_intf") # i34
sim.add_logger(logger)
```
%% Cell type:markdown id: tags:
Next, we define the simulation callback. This function will be executed on every parameter change.
It will re-run the simulation and plot the updated results.
%% Cell type:code id: tags:
``` python
def simulate(dt, dur, pha, mag, r1, r2, l, c):
gnd = dpsim.dp.Node.GND()
n1 = dpsim.dp.Node("n1")
n2 = dpsim.dp.Node("n2")
cs = dpsim.dp.ph1.CurrentSource("cs", [gnd, n1], I_ref=cmath.rect(mag, pha))
r1 = dpsim.dp.ph1.Resistor("r_1", [n1, gnd], R=r1)
c1 = dpsim.dp.ph1.Capacitor("c_1", [n1, n2], C=c)
l1 = dpsim.dp.ph1.Inductor("l_1", [n2, gnd], L=l*1e-3)
r2 = dpsim.dp.ph1.Resistor("r_2", [n2, gnd], R=r2*1e-3)
sys = dpsim.SystemTopology(50, [gnd, n1, n2], [cs, r1, c1, l1, r2])
sim = dpsim.Simulation("Widgets", sys, duration=dur, timestep=dt)
logger = dpsim.Logger("Widgets")
logger.log_attribute(n1, "v") # v1
logger.log_attribute(n2, "v") # v2
logger.log_attribute(cs, "i_intf") # i12
logger.log_attribute(c1, "i_intf") # i34
sim.add_logger(logger)
def simulate(dt, dur, pha, mag, r1_val, r2_val, l_val, c_val):
sim.reset()
sim.timestep = dt
sim.final_time = dur
cs.I_ref = cmath.rect(mag, pha)
r1.R = r1_val
c1.C = c_val
l1.L = l_val * 1e-3
r2.R = r2_val * 1e-3
sim.start()
while sim.state != 9:
time.sleep(0.01)
os.system('head -n-1 Logs/Widgets.csv > Logs/Widgets_fixed.csv')
time.sleep(0.001)
results = rt.read_timeseries_dpsim('Logs/Widgets_fixed.csv')
results = rt.read_timeseries_dpsim('logs/Widgets.csv')
for l in [ 'n1.v', 'n2.v', 'cs.i_intf', 'c_1.i_intf' ]:
emt = results[l].frequency_shift(l + '_emt', 50)
emt = results[l].frequency_shift(50)
pt.plot_timeseries(1, emt)
```
%% Cell type:markdown id: tags:
Set the `cu` variable to `True` for a continous redrawing of the plot while dragging the sliders.
%% Cell type:code id: tags:
``` python
cu = False
```
%% Cell type:code id: tags:
``` python
from ipywidgets import interact, interactive, fixed, interact_manual
import ipywidgets as widgets
output = interactive(simulate,
dt = widgets.FloatText(description="Timestep [s]", value=1e-3, min=1e-3, max=1),
dur = widgets.FloatText(description="Duration [s]", value=0.1, min=0, max=10),
pha = widgets.FloatSlider(description="Phase [rad]", min=-math.pi, max=math.pi),
mag = widgets.FloatSlider(description="Magnitude [V]", value=10, min=0, max=100),
r1 = widgets.FloatSlider(description="Resistance [Ohm]", value=1, min=0.1, max=10),
r2 = widgets.FloatSlider(description="Resistance [Ohm]", value=1, min=0.1, max=10),
l = widgets.FloatSlider(description="Inductance [H]", value=1, min=1, max=10),
c = widgets.FloatSlider(description="Capactance [F]", value=1, min=1, max=10),
continuous_update=False
dt = widgets.FloatText(description="Timestep [s]", value=1e-3, min=1e-3, max=1),
dur = widgets.FloatText(description="Duration [s]", value=0.5, min=0.001, max=4),
pha = widgets.FloatSlider(description="Phase [rad]", min=-math.pi, max=math.pi, continuous_update=cu),
mag = widgets.FloatSlider(description="Magnitude [V]", value=10, min=0, max=100, continuous_update=cu),
r1_val = widgets.FloatSlider(description="Resistance [Ohm]", value=1, min=0.1, max=10, continuous_update=cu),
r2_val = widgets.FloatSlider(description="Resistance [Ohm]", value=1, min=0.1, max=10, continuous_update=cu),
l_val = widgets.FloatSlider(description="Inductance [H]", value=1, min=1, max=10, continuous_update=cu),
c_val = widgets.FloatSlider(description="Capactance [F]", value=1, min=1, max=10, continuous_update=cu)
)
last = output.children[-1]
last.layout.height = '400px'
output
```
%% Output
%% Cell type:code id: tags:
``` python
```
......
This source diff could not be displayed because it is too large. You can view the blob instead.
......@@ -24,6 +24,8 @@
#include <map>
#include <iostream>
#include <fstream>
#include <experimental/filesystem>
namespace fs = std::experimental::filesystem;
#include <dpsim/Definitions.h>
#include <dpsim/Scheduler.h>
......@@ -41,6 +43,7 @@ namespace DPsim {
String mName;
Bool mEnabled;
UInt mDownsampling;
fs::path mFilename;
std::map<String, CPS::AttributeBase::Ptr> mAttributes;
......@@ -54,9 +57,13 @@ namespace DPsim {
DataLogger(Bool enabled = true);
DataLogger(String name, Bool enabled = true, UInt downsampling = 1);
~DataLogger();
void open();
void close();
void reopen() {
close();
open();
}
void logPhasorNodeValues(Real time, const Matrix& data);
void logEMTNodeValues(Real time, const Matrix& data);
......
......@@ -107,10 +107,14 @@ namespace Python {
static PyObject* start(Simulation *self, PyObject *args);
static PyObject* step(Simulation *self, PyObject *args);
static PyObject* stop(Simulation *self, PyObject *args);
static PyObject* reset(Simulation *self, PyObject *args);
static PyObject* addEventFD(Simulation *self, PyObject *args);
static PyObject* removeEventFD(Simulation *self, PyObject *args);
static PyObject* setScheduler(Simulation *self, PyObject *args, PyObject *kwargs);
// Setters
static int setFinalTime(Simulation *self, PyObject *val, void *ctx);
// Getters
static PyObject* getState(Simulation *self, void *ctx);
static PyObject* name(Simulation *self, void *ctx);
......@@ -123,6 +127,7 @@ namespace Python {
static const char *docStart;
static const char *docPause;
static const char *docStop;
static const char *docReset;
static const char *docStep;
static const char *docAddInterface;
static const char *docAddEvent;
......
......@@ -191,6 +191,8 @@ namespace DPsim {
void sync();
/// Create the schedule for the independent tasks
void schedule();
/// Reset internal state of simulation
void reset();
///
template <typename VarType>
......
......@@ -35,6 +35,12 @@ RUN dnf -y install /tmp/*.rpm
ADD requirements.txt .
RUN pip3 install -r requirements.txt
# Activate Jupyter extensions
RUN dnf -y --refresh install npm
RUN jupyter nbextension enable --py widgetsnbextension
RUN jupyter labextension install @jupyter-widgets/jupyterlab-manager
# Copy Example materials
RUN mkdir dpsim
COPY --from=builder /dpsim/Examples /dpsim/
RUN find /dpsim \
......
......@@ -19,8 +19,6 @@
*********************************************************************************/
#include <iomanip>
#include <experimental/filesystem>
namespace fs = std::experimental::filesystem;
#include <dpsim/DataLogger.h>
#include <cps/Logger.h>
......@@ -41,26 +39,23 @@ DataLogger::DataLogger(String name, Bool enabled, UInt downsampling) :
if (!mEnabled)
return;
String filename = CPS::Logger::logDir() + "/" + name + ".csv";
mFilename = CPS::Logger::logDir() + "/" + name + ".csv";
fs::path p = filename;