This tutorial shows how to use ModelicaRes to load and analyze Modelica results.
Before we get started, we'll load some standard modules and establish settings for this IPython notebook.
import numpy as np
import matplotlib.pyplot as plt
import pandas; pandas.set_option('display.max_rows', 5)
%matplotlib inline
%precision 4
First, we'll load a simulation result of Modelica.Electrical.Analog.Examples.ChuaCircuit:
from modelicares import SimRes
sim = SimRes('ChuaCircuit.mat')
Now we'll explore the simulation results using the methods in SimRes. To get the start and stop times of the simulation, we can look up the initial and final values of time:
sim['Time'].IV()
sim['Time'].FV()
There are other many other methods. For example,
Li = sim['L.i']
dict(description=Li.description,
maximum=Li.max(),
mean=Li.mean(),
minimum=Li.min(),
rms=Li.RMS(),
unit=Li.unit)
Notice, however, that description
and unit
are not methods but fields. To retrieve the values of a variable, use values():
sim['L.v'].values(t=(0,20))
Here we've only included the first 20 seconds, but all of the values are returned by default. To return an array with columns for times and values, use array():
sim['L.v'].array(t=(0,20))
These values have been extracted directly, but it's also possible to interpolate:
sim['L.v'].array(t=[2.5, 7.5, 12.5, 17.5])
or to return only every nth sample (here, n = 100):
sim['L.v'].array(t=(None,None,100))
We can perform calculations on the values while retrieving them. For example, to calculate the rate of heat generated by the resistor Ro
in the ChuaCircuit at 100 s:
R = sim['Ro.R'].value()
square = lambda x: x**2
Isquared = sim['Ro.i'].values(f=square, t=100)
Isquared*R
As expected, this matches the value recorded in the simulation:
sim['Ro.LossPower'].values(t=100)
To check if a variable is included in the results, we can use Python's in operator.
'L.v' in sim
To see how many variables are in a simulation, use Python's len() function:
len(sim)
To search for variable names, use names() with wildcards:
sim.names('L*v')
Regular expressions can also be used:
voltages = sim.names('^[^.]*.v$', re=True)
voltages
We can access information about all these variables at once:
sim(voltages).IV()
Notice that we used parentheses; sim[]
only accepts one variable and would have produced an error.
We can plot these voltages using plot():
ax, __ = sim.plot(voltages)
The title, axis labels, and legend entries were generated automatically, but they can be customized using other plot arguments or functions from matplotlib.pyplot. The abscissa can be any variable, not just time. Here, we use current:
ax, __ = sim.plot(ynames1='L.v', ylabel1="Voltage", xname='L.i', xlabel="Current",
title="Chua circuit\n"
"Current through and voltage across the inductor")
# Mark the start and stop points.
def mark(time, text):
i = sim['L.i'].values(time)
v = sim['L.v'].values(time)
plt.plot(i, v, 'bo')
ax.annotate(text, xy=(i, v), xytext=(0, -4), ha='center', va='top',
textcoords='offset points')
mark(0, "Start")
mark(2500, "Stop")
ax, __ = sim.plot('L.v', ylabel1="Voltage",
f1={"abs(L.v)": lambda x: np.abs(x[0])},
title="Chua circuit\nVoltage across the inductor")
# Show the max, mean, and rectified mean.
from modelicares import add_hlines
Lv = sim['L.v']
add_hlines(ax, [Lv.max(), Lv.mean(), Lv.mean_rectified()],
["max", "mean", "rectified mean"], color='k', linestyle=":")
We can add traces that are a function of other variables in the plot using the f1 and f2 plot arguments. Here, we will plot the absolute value of the voltage:
We can create a pandas data frame of selected data:
sim.to_pandas(voltages)
We'll load a linearization result of Modelica.Blocks.Continuous.PID:
from modelicares import LinRes
lin = LinRes('PID.mat')
We can explore it and create diagrams using the methods in LinRes. An instance of LinRes has a sys
attribute, which is an instance of the StateSpace class from the control package. The state names are available in sys.state_names
:
lin.sys.state_names
The input and output names are also available:
lin.sys.input_names
lin.sys.output_names
The system matrixes are available in sys.A
, sys.B
, sys.C
, and sys.D
, e.g.,
lin.sys.A
ax1, ax2 = lin.bode(omega=2*np.pi*np.logspace(-2, 3))
Nyquist plots are available via nyquist():
ax = lin.nyquist(omega=2*np.pi*np.logspace(0, 3, 61), labelFreq=20)
We can also use ModelicaRes to analyze and plot data from multiple simulation and linearization results at once. First, we'll import some additional classes:
from modelicares import SimResList, LinResList
SimResList or LinResList are Python lists with additional methods that operate on the contained simulation and linearization results as a group. Each entry is an instance of SimRes or LinRes (correspondingly).
We'll load all simulations from a nested folder:
sims = SimResList('ChuaCircuit/*/*.mat')
print(sims)
We can query the list for an attribute of a variable across all of the simulations:
sims['L.v'].RMS()
We can also see which initial values are different among the simulations:
unique_IVs = sims.unique_IVs()
unique_IVs
It's easy to plot variables across all of the simulations:
inductance_label = lambda L: 'w/ L.L = {L} H'.format(L=L)
ax, __ = sims.plot('L.v', title="Chua circuit\nwith varying inductance", ylabel1='Voltage',
suffixes=map(inductance_label, unique_IVs['L.L']))
Similarly, we can load two linearizations:
results = {'PID/1/dslin.mat': "Td = 0.1 s",
'PID/2/dslin.mat': "Td = 1 s"}
lins = LinResList(*list(results))
It's necessary to provide the differential time constants because they're not recorded in the files. (However, if each result is accompanied with a "dsin"-style parameter file, we could use read_params(); see the example in LinResList.bode().)
We can create a Bode plot of both linearizations at once:
ax1, ax2 = lins.bode(title="Bode plot of PID\nwith varying differential time constant",
omega=2*np.pi*np.logspace(-2, 3), labels=results.values(),
leg_kwargs=dict(loc='lower right'))
or a Nyquist plot:
ax = lins.nyquist(title="Nyquist plot of PID\nwith varying differential time constant",
omega=2*np.pi*np.logspace(-1, 2, 61), labels=results.values(), labelFreq=20)
For advanced topics in ModelicaRes, see the the next IPython notebook as a file or a static page. For full documentation of ModelicaRes, see the main webpage.