<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>DPsim – Documentation</title><link>https://sogno.energy/dpsim/docs/</link><description>Recent content in Documentation on DPsim</description><generator>Hugo -- gohugo.io</generator><atom:link href="https://sogno.energy/dpsim/docs/index.xml" rel="self" type="application/rss+xml"/><item><title>Docs: Your First Simulation</title><link>https://sogno.energy/dpsim/docs/tutorials/python/first-simulation/</link><pubDate>Fri, 31 Jul 2026 00:00:00 +0000</pubDate><guid>https://sogno.energy/dpsim/docs/tutorials/python/first-simulation/</guid><description>
&lt;p>This page goes from nothing to a plotted result. It assumes DPsim is installed and importable; if
it is not, start with &lt;a href="https://sogno.energy/dpsim/docs/user-guide/install/">install&lt;/a> or &lt;a href="https://sogno.energy/dpsim/docs/developer-guide/architecture-and-conventions/build/">build&lt;/a>.&lt;/p>
&lt;p>The circuit is deliberately trivial, a voltage source feeding a resistor, so that nothing in it
distracts from the shape of the script. Every real simulation has the same five parts in the same
order.&lt;/p>
&lt;p>&lt;img src="circuit.svg" alt="A voltage source feeding a resistor.">&lt;/p>
&lt;h2 id="the-whole-script">The whole script&lt;/h2>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-python" data-lang="python">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#204a87;font-weight:bold">import&lt;/span> &lt;span style="color:#000">dpsimpy&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#204a87;font-weight:bold">import&lt;/span> &lt;span style="color:#000">villas.dataprocessing.readtools&lt;/span> &lt;span style="color:#204a87;font-weight:bold">as&lt;/span> &lt;span style="color:#000">rt&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#204a87;font-weight:bold">from&lt;/span> &lt;span style="color:#000">villas.dataprocessing.timeseries&lt;/span> &lt;span style="color:#204a87;font-weight:bold">import&lt;/span> &lt;span style="color:#000">TimeSeries&lt;/span> &lt;span style="color:#204a87;font-weight:bold">as&lt;/span> &lt;span style="color:#000">ts&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#000">name&lt;/span> &lt;span style="color:#ce5c00;font-weight:bold">=&lt;/span> &lt;span style="color:#4e9a06">&amp;#34;first_simulation&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#8f5902;font-style:italic"># 1. Nodes&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#000">gnd&lt;/span> &lt;span style="color:#ce5c00;font-weight:bold">=&lt;/span> &lt;span style="color:#000">dpsimpy&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">dp&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">SimNode&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">gnd&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#000">n1&lt;/span> &lt;span style="color:#ce5c00;font-weight:bold">=&lt;/span> &lt;span style="color:#000">dpsimpy&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">dp&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">SimNode&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#4e9a06">&amp;#34;n1&amp;#34;&lt;/span>&lt;span style="color:#000;font-weight:bold">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#8f5902;font-style:italic"># 2. Components&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#000">src&lt;/span> &lt;span style="color:#ce5c00;font-weight:bold">=&lt;/span> &lt;span style="color:#000">dpsimpy&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">dp&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">ph1&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">VoltageSource&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#4e9a06">&amp;#34;src&amp;#34;&lt;/span>&lt;span style="color:#000;font-weight:bold">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#000">src&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">V_ref&lt;/span> &lt;span style="color:#ce5c00;font-weight:bold">=&lt;/span> &lt;span style="color:#204a87">complex&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#0000cf;font-weight:bold">100&lt;/span>&lt;span style="color:#000;font-weight:bold">,&lt;/span> &lt;span style="color:#0000cf;font-weight:bold">0&lt;/span>&lt;span style="color:#000;font-weight:bold">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#000">load&lt;/span> &lt;span style="color:#ce5c00;font-weight:bold">=&lt;/span> &lt;span style="color:#000">dpsimpy&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">dp&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">ph1&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">Resistor&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#4e9a06">&amp;#34;load&amp;#34;&lt;/span>&lt;span style="color:#000;font-weight:bold">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#000">load&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">R&lt;/span> &lt;span style="color:#ce5c00;font-weight:bold">=&lt;/span> &lt;span style="color:#0000cf;font-weight:bold">10.0&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#8f5902;font-style:italic"># 3. Connections&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#000">src&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">connect&lt;/span>&lt;span style="color:#000;font-weight:bold">([&lt;/span>&lt;span style="color:#000">gnd&lt;/span>&lt;span style="color:#000;font-weight:bold">,&lt;/span> &lt;span style="color:#000">n1&lt;/span>&lt;span style="color:#000;font-weight:bold">])&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#000">load&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">connect&lt;/span>&lt;span style="color:#000;font-weight:bold">([&lt;/span>&lt;span style="color:#000">n1&lt;/span>&lt;span style="color:#000;font-weight:bold">,&lt;/span> &lt;span style="color:#000">gnd&lt;/span>&lt;span style="color:#000;font-weight:bold">])&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#8f5902;font-style:italic"># 4. Topology&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#000">system&lt;/span> &lt;span style="color:#ce5c00;font-weight:bold">=&lt;/span> &lt;span style="color:#000">dpsimpy&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">SystemTopology&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#0000cf;font-weight:bold">50&lt;/span>&lt;span style="color:#000;font-weight:bold">,&lt;/span> &lt;span style="color:#000;font-weight:bold">[&lt;/span>&lt;span style="color:#000">gnd&lt;/span>&lt;span style="color:#000;font-weight:bold">,&lt;/span> &lt;span style="color:#000">n1&lt;/span>&lt;span style="color:#000;font-weight:bold">],&lt;/span> &lt;span style="color:#000;font-weight:bold">[&lt;/span>&lt;span style="color:#000">src&lt;/span>&lt;span style="color:#000;font-weight:bold">,&lt;/span> &lt;span style="color:#000">load&lt;/span>&lt;span style="color:#000;font-weight:bold">])&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#8f5902;font-style:italic"># 5. Logging, then run&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#000">logger&lt;/span> &lt;span style="color:#ce5c00;font-weight:bold">=&lt;/span> &lt;span style="color:#000">dpsimpy&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">Logger&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#000">name&lt;/span>&lt;span style="color:#000;font-weight:bold">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#000">logger&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">log_attribute&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#4e9a06">&amp;#34;n1.v&amp;#34;&lt;/span>&lt;span style="color:#000;font-weight:bold">,&lt;/span> &lt;span style="color:#4e9a06">&amp;#34;v&amp;#34;&lt;/span>&lt;span style="color:#000;font-weight:bold">,&lt;/span> &lt;span style="color:#000">n1&lt;/span>&lt;span style="color:#000;font-weight:bold">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#000">logger&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">log_attribute&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#4e9a06">&amp;#34;load.i_intf&amp;#34;&lt;/span>&lt;span style="color:#000;font-weight:bold">,&lt;/span> &lt;span style="color:#4e9a06">&amp;#34;i_intf&amp;#34;&lt;/span>&lt;span style="color:#000;font-weight:bold">,&lt;/span> &lt;span style="color:#000">load&lt;/span>&lt;span style="color:#000;font-weight:bold">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#000">sim&lt;/span> &lt;span style="color:#ce5c00;font-weight:bold">=&lt;/span> &lt;span style="color:#000">dpsimpy&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">Simulation&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#000">name&lt;/span>&lt;span style="color:#000;font-weight:bold">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#000">sim&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">set_domain&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#000">dpsimpy&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">Domain&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">DP&lt;/span>&lt;span style="color:#000;font-weight:bold">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#000">sim&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">set_system&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#000">system&lt;/span>&lt;span style="color:#000;font-weight:bold">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#000">sim&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">set_time_step&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#0000cf;font-weight:bold">1e-3&lt;/span>&lt;span style="color:#000;font-weight:bold">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#000">sim&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">set_final_time&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#0000cf;font-weight:bold">0.1&lt;/span>&lt;span style="color:#000;font-weight:bold">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#000">sim&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">add_logger&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#000">logger&lt;/span>&lt;span style="color:#000;font-weight:bold">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#000">sim&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">run&lt;/span>&lt;span style="color:#000;font-weight:bold">()&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Running it prints solver progress and writes &lt;code>logs/first_simulation.csv&lt;/code>.&lt;/p>
&lt;h2 id="what-each-part-is-doing">What each part is doing&lt;/h2>
&lt;p>&lt;strong>Nodes come first&lt;/strong> because components connect to them, not to each other. &lt;code>SimNode.gnd&lt;/code> is the
reference node and is shared; every network needs it. Nodes are chosen from a domain namespace,
&lt;code>dpsimpy.dp&lt;/code> here, and a node from one domain cannot be connected to a component from another.&lt;/p>
&lt;p>&lt;strong>Components are created, then configured through their attributes.&lt;/strong> &lt;code>src.V_ref = complex(100, 0)&lt;/code>
sets the source reference as a complex phasor, because this is the dynamic phasor domain and a
voltage is an envelope rather than an instantaneous value. In EMT the same field would carry a
different meaning; see &lt;a href="https://sogno.energy/dpsim/docs/concepts/dyn-phasors/">dynamic phasors&lt;/a>.&lt;/p>
&lt;div class="alert alert-warning" role="alert">
&lt;h4 class="alert-heading">Watch out: connection order sets the sign&lt;/h4>
&lt;strong>Connection order defines polarity.&lt;/strong> &lt;code>src.connect([gnd, n1])&lt;/code> means terminal 0 at ground and
terminal 1 at &lt;code>n1&lt;/code>, so a positive current flows from terminal 0 to terminal 1 inside the component.
Reversing the list reverses the sign of everything that component reports. Nothing checks this for
you, and a sign error here produces a simulation that runs and is wrong.
&lt;/div>
&lt;div class="alert alert-warning" role="alert">
&lt;h4 class="alert-heading">Watch out: a component left out of the topology is ignored&lt;/h4>
&lt;strong>The topology takes the system frequency first&lt;/strong>, then the nodes, then the components. Anything not
in those two lists is not simulated, even if it was created and connected. This is the most common
reason a component appears to have no effect.
&lt;/div>
&lt;p>&lt;strong>Logging is opt-in, and takes three steps in order.&lt;/strong> Nothing is recorded unless a logger asks for
it.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-python" data-lang="python">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#000">logger&lt;/span> &lt;span style="color:#ce5c00;font-weight:bold">=&lt;/span> &lt;span style="color:#000">dpsimpy&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">Logger&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#000">name&lt;/span>&lt;span style="color:#000;font-weight:bold">)&lt;/span> &lt;span style="color:#8f5902;font-style:italic"># 1. create it; the name becomes the file name&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#000">logger&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">log_attribute&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#4e9a06">&amp;#34;n1.v&amp;#34;&lt;/span>&lt;span style="color:#000;font-weight:bold">,&lt;/span> &lt;span style="color:#4e9a06">&amp;#34;v&amp;#34;&lt;/span>&lt;span style="color:#000;font-weight:bold">,&lt;/span> &lt;span style="color:#000">n1&lt;/span>&lt;span style="color:#000;font-weight:bold">)&lt;/span> &lt;span style="color:#8f5902;font-style:italic"># 2. register each attribute you want&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#000">logger&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">log_attribute&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#4e9a06">&amp;#34;load.i_intf&amp;#34;&lt;/span>&lt;span style="color:#000;font-weight:bold">,&lt;/span> &lt;span style="color:#4e9a06">&amp;#34;i_intf&amp;#34;&lt;/span>&lt;span style="color:#000;font-weight:bold">,&lt;/span> &lt;span style="color:#000">load&lt;/span>&lt;span style="color:#000;font-weight:bold">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#000">sim&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">add_logger&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#000">logger&lt;/span>&lt;span style="color:#000;font-weight:bold">)&lt;/span> &lt;span style="color:#8f5902;font-style:italic"># 3. attach it to the simulation, before run()&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;code>log_attribute&lt;/code> takes the column name you want, the name of the attribute on the object, and the
object itself. &lt;code>&amp;quot;v&amp;quot;&lt;/code> on a node is its voltage; &lt;code>&amp;quot;i_intf&amp;quot;&lt;/code> on a component is the current through it.
The first argument is yours to choose and is the key you will use when reading the file back; the
second must be an attribute the object actually publishes, and &lt;code>print_attribute_list()&lt;/code> on the
object shows what that is.&lt;/p>
&lt;div class="alert alert-warning" role="alert">
&lt;h4 class="alert-heading">Watch out: only attributes can be logged&lt;/h4>
A value a component computes internally but does not publish as an attribute cannot be recorded by
any logger option. &lt;code>print_attribute_list()&lt;/code> on an object shows what it publishes.
&lt;/div>
&lt;p>The order is what makes it work. A logger registers attributes before it is attached, and it must be
attached before &lt;code>run()&lt;/code>, because the column header is written from whatever is registered when the
first row is written. A logger created but never passed to &lt;code>add_logger&lt;/code> produces no file at all,
which is the usual reason for a run that appears to have logged nothing.&lt;/p>
&lt;h2 id="reading-the-results">Reading the results&lt;/h2>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-python" data-lang="python">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#000">results&lt;/span> &lt;span style="color:#ce5c00;font-weight:bold">=&lt;/span> &lt;span style="color:#000">rt&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">read_timeseries_dpsim&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#4e9a06">&amp;#34;logs/&amp;#34;&lt;/span> &lt;span style="color:#ce5c00;font-weight:bold">+&lt;/span> &lt;span style="color:#000">name&lt;/span> &lt;span style="color:#ce5c00;font-weight:bold">+&lt;/span> &lt;span style="color:#4e9a06">&amp;#34;.csv&amp;#34;&lt;/span>&lt;span style="color:#000;font-weight:bold">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#204a87">print&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#204a87">sorted&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#000">results&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">keys&lt;/span>&lt;span style="color:#000;font-weight:bold">()))&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#8f5902;font-style:italic"># [&amp;#39;load.i_intf&amp;#39;, &amp;#39;n1.v&amp;#39;]&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#000">v&lt;/span> &lt;span style="color:#ce5c00;font-weight:bold">=&lt;/span> &lt;span style="color:#000">results&lt;/span>&lt;span style="color:#000;font-weight:bold">[&lt;/span>&lt;span style="color:#4e9a06">&amp;#34;n1.v&amp;#34;&lt;/span>&lt;span style="color:#000;font-weight:bold">]&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#204a87">print&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#000">v&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">time&lt;/span>&lt;span style="color:#000;font-weight:bold">[&lt;/span>&lt;span style="color:#0000cf;font-weight:bold">1&lt;/span>&lt;span style="color:#000;font-weight:bold">],&lt;/span> &lt;span style="color:#204a87">abs&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#000">v&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">values&lt;/span>&lt;span style="color:#000;font-weight:bold">[&lt;/span>&lt;span style="color:#0000cf;font-weight:bold">1&lt;/span>&lt;span style="color:#000;font-weight:bold">]))&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#8f5902;font-style:italic"># 0.001 100.0&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>The keys are the column names given to &lt;code>log_attribute&lt;/code>. Each value is a time series with &lt;code>time&lt;/code> and
&lt;code>values&lt;/code> arrays; in a dynamic phasor simulation the values are complex, and &lt;code>abs()&lt;/code> gives the
envelope magnitude.&lt;/p>
&lt;p>Note that the first sample is zero. The log is written before the first solve, so row zero is the
state the simulation started from rather than a result. From &lt;code>t = 0.001&lt;/code> onwards this circuit sits
at exactly 100 V and 10 A, which is what a 100 V source across 10 Ω should give.&lt;/p>
&lt;h2 id="plotting">Plotting&lt;/h2>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-python" data-lang="python">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#204a87;font-weight:bold">import&lt;/span> &lt;span style="color:#000">villas.dataprocessing.plottools&lt;/span> &lt;span style="color:#204a87;font-weight:bold">as&lt;/span> &lt;span style="color:#000">pt&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#000">pt&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">plot_timeseries&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#0000cf;font-weight:bold">1&lt;/span>&lt;span style="color:#000;font-weight:bold">,&lt;/span> &lt;span style="color:#000">results&lt;/span>&lt;span style="color:#000;font-weight:bold">[&lt;/span>&lt;span style="color:#4e9a06">&amp;#34;n1.v&amp;#34;&lt;/span>&lt;span style="color:#000;font-weight:bold">]&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">abs&lt;/span>&lt;span style="color:#000;font-weight:bold">())&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#000">pt&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">plot_timeseries&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#0000cf;font-weight:bold">2&lt;/span>&lt;span style="color:#000;font-weight:bold">,&lt;/span> &lt;span style="color:#000">results&lt;/span>&lt;span style="color:#000;font-weight:bold">[&lt;/span>&lt;span style="color:#4e9a06">&amp;#34;load.i_intf&amp;#34;&lt;/span>&lt;span style="color:#000;font-weight:bold">]&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">abs&lt;/span>&lt;span style="color:#000;font-weight:bold">())&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>The first argument is a figure number, so repeated calls with the same number overlay curves on one
axis. &lt;code>.abs()&lt;/code> is needed for complex results; plotting a complex series directly is not meaningful.&lt;/p>
&lt;h2 id="recovering-the-waveform-from-a-dynamic-phasor-result">Recovering the waveform from a dynamic phasor result&lt;/h2>
&lt;p>A dynamic phasor result is an envelope, not a waveform. To compare it against an instantaneous
result, shift it back onto the carrier:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-python" data-lang="python">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#000">emt&lt;/span> &lt;span style="color:#ce5c00;font-weight:bold">=&lt;/span> &lt;span style="color:#000">ts&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">frequency_shift_list&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#000">results&lt;/span>&lt;span style="color:#000;font-weight:bold">,&lt;/span> &lt;span style="color:#0000cf;font-weight:bold">50&lt;/span>&lt;span style="color:#000;font-weight:bold">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#204a87">print&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#204a87">sorted&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#000">emt&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">keys&lt;/span>&lt;span style="color:#000;font-weight:bold">()))&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#8f5902;font-style:italic"># [&amp;#39;load.i_intf_shift&amp;#39;, &amp;#39;n1.v_shift&amp;#39;]&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>
&lt;div class="alert alert-warning" role="alert">
&lt;h4 class="alert-heading">Watch out: every key gains a _shift suffix&lt;/h4>
Every key gains a &lt;code>_shift&lt;/code> suffix, which is easy to miss and produces a &lt;code>KeyError&lt;/code> that reads as
though the quantity were never logged. The result is a real waveform at the given carrier frequency,
and can be plotted or compared against an EMT run directly.
&lt;/div>
&lt;h2 id="the-script">The script&lt;/h2>
&lt;p>The complete script for this page is &lt;a href="https://github.com/sogno-platform/dpsim/blob/master/examples/Python/Tutorials/01_first_simulation.py">&lt;code>01_first_simulation.py&lt;/code>&lt;/a> under &lt;code>examples/Python/Tutorials&lt;/code>. The numbers quoted above are the numbers it prints, so if the two ever disagree the page is the one that is wrong.&lt;/p>
&lt;h2 id="next">Next&lt;/h2>
&lt;p>This circuit has no dynamics at all: it is a source and a resistor, so it reaches its final value in
one step and stays there. The next step is to add an element that stores energy, which is where the
choice of time step starts to matter and where a result becomes worth plotting.&lt;/p>
&lt;p>The &lt;a href="https://sogno.energy/dpsim/docs/user-guide/examples/">examples&lt;/a> work through larger networks, and the models used here are
derived under &lt;a href="https://sogno.energy/dpsim/docs/concepts/models/sources/">sources&lt;/a> and
&lt;a href="https://sogno.energy/dpsim/docs/concepts/models/rlc-elements/">RLC elements&lt;/a>.&lt;/p></description></item><item><title>Docs: Install</title><link>https://sogno.energy/dpsim/docs/user-guide/install/</link><pubDate>Fri, 31 Jul 2026 00:00:00 +0000</pubDate><guid>https://sogno.energy/dpsim/docs/user-guide/install/</guid><description>
&lt;p>DPsim is a Python module and C++ library for dynamic power system simulation.&lt;/p>
&lt;p>The quickest route to a result is the Python module: install it, then work through the
&lt;a href="https://sogno.energy/dpsim/docs/tutorials/">tutorials&lt;/a>, which build up one idea at a time from a source and a
resistor. If you would rather read a finished study than build one, the
&lt;a href="https://sogno.energy/dpsim/docs/user-guide/examples/">example notebooks&lt;/a> run complete scenarios and plot them.&lt;/p>
&lt;p>Building from source is only needed for a platform without a published wheel, or to work on DPsim
itself; see &lt;a href="https://sogno.energy/dpsim/docs/developer-guide/architecture-and-conventions/build/">build&lt;/a>.&lt;/p>
&lt;h2 id="try-it-without-installing">Try it without installing&lt;/h2>
&lt;p>The example notebooks run in the browser with no local installation:&lt;/p>
&lt;p>&lt;a href="https://2i2c.mybinder.org/v2/gh/sogno-platform/dpsim/HEAD?urlpath=%2Fdoc%2Ftree%2Fexamples%2FIndex.ipynb">&lt;img src="https://2i2c.mybinder.org/badge_logo.svg" alt="Binder">&lt;/a>&lt;/p>
&lt;h2 id="python-package">Python package&lt;/h2>
&lt;p>DPsim is published on PyPI and installs like any other Python package:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-shell" data-lang="shell">&lt;span style="display:flex;">&lt;span>python3 -m venv venv
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#204a87">source&lt;/span> venv/bin/activate
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>pip install dpsim
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>
&lt;div class="alert alert-info" role="alert">
&lt;h4 class="alert-heading">Requires Linux for the published wheels&lt;/h4>
Two limitations are worth knowing before you start.
Only Linux wheels are currently published, for CPython 3.9 through 3.13, so on Windows and macOS
you have to build from source for now.
The package also contains only the simulation core; the example notebooks additionally need
plotting and data handling packages, which are listed in the import section of each notebook.
&lt;/div>
&lt;p>If you prefer conda, the equivalent is:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-shell" data-lang="shell">&lt;span style="display:flex;">&lt;span>conda create -n dpsim &lt;span style="color:#000">python&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">=&lt;/span>3.13
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>conda activate dpsim
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>pip install dpsim
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="docker">Docker&lt;/h2>
&lt;p>You need &lt;a href="https://docs.docker.com/install/">Docker&lt;/a> installed first. The prepared image on
Docker Hub bundles the module together with a JupyterLab session:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-shell" data-lang="shell">&lt;span style="display:flex;">&lt;span>docker run -p 8888:8888 sogno/dpsim
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Then open &lt;a href="http://localhost:8888/lab?token=3adaa57df44cea75e60c0169e1b2a98ae8f7de130481b5bc">http://localhost:8888/lab?token=3adaa57df44cea75e60c0169e1b2a98ae8f7de130481b5bc&lt;/a>.&lt;/p>
&lt;p>Note that the image pins that access token in its startup command, so it is the same for
everyone who runs the image. Publish the port on localhost only, as above, and do not expose
it to an untrusted network.&lt;/p>
&lt;p>To build the image yourself rather than pulling it, see
&lt;a href="https://sogno.energy/dpsim/docs/developer-guide/architecture-and-conventions/build/">build&lt;/a>.&lt;/p></description></item><item><title>Docs: The MNA Solver</title><link>https://sogno.energy/dpsim/docs/developer-guide/solvers/mna-solver/</link><pubDate>Fri, 31 Jul 2026 00:00:00 +0000</pubDate><guid>https://sogno.energy/dpsim/docs/developer-guide/solvers/mna-solver/</guid><description>
&lt;p>&lt;code>MnaSolver&amp;lt;VarType&amp;gt;&lt;/code> is the solver almost every simulation uses. The method it implements is derived
under &lt;a href="https://sogno.energy/dpsim/docs/concepts/nodal-analysis/">nodal analysis&lt;/a>; what a component must
provide to take part is under
&lt;a href="https://sogno.energy/dpsim/docs/developer-guide/writing-a-model/mnainterface/">interfacing with the MNA solver&lt;/a>. This page is
the solver itself.&lt;/p>
&lt;p>Note the spelling: the class is &lt;code>MnaSolver&lt;/code> even though the file is &lt;code>MNASolver.h&lt;/code>.&lt;/p>
&lt;h2 id="setting-up">Setting up&lt;/h2>
&lt;p>&lt;code>initialize&lt;/code> runs the sequence described under
&lt;a href="https://sogno.energy/dpsim/docs/developer-guide/writing-a-model/initialization/">component and solver initialization&lt;/a>:
identify the topology objects, create sub-components, collect virtual nodes,
&lt;code>assignMatrixNodeIndices&lt;/code>, size the matrices, initialize the components, then assemble.&lt;/p>
&lt;p>Which assembly function runs depends on the network:&lt;/p>
&lt;ul>
&lt;li>&lt;code>initializeSystemWithPrecomputedMatrices&lt;/code> when the switch combinations are few enough to
enumerate. Every combination gets its own factorised matrix up front, so a switching event
becomes a lookup rather than a refactorisation.&lt;/li>
&lt;li>&lt;code>initializeSystemWithVariableMatrix&lt;/code> when a component changes its own stamp continuously and
enumeration is impossible.&lt;/li>
&lt;li>&lt;code>initializeSystemWithParallelFrequencies&lt;/code> for a harmonic study, where several frequencies are
solved side by side.&lt;/li>
&lt;/ul>
&lt;p>&lt;code>resolveSystemMatrixRecomputationMode&lt;/code> chooses between them when the mode is &lt;code>Auto&lt;/code>;
&lt;code>SystemMatrixRecomputationMode::Enabled&lt;/code> and &lt;code>Disabled&lt;/code> force it either way.&lt;/p>
&lt;h2 id="stepping">Stepping&lt;/h2>
&lt;p>&lt;code>solve&lt;/code> does the same four things every step.&lt;/p>
&lt;p>It zeroes the right-hand side and sums the stamps the components&amp;rsquo; pre-step tasks produced, which is
why a component that fails to declare its dependencies can find its contribution missing rather
than wrong. It calls &lt;code>updateSwitchStatus&lt;/code>, which produces an index into the precomputed matrices.
It solves through the linear solver for that index. Then it hands the solution to the components'
post-step tasks.&lt;/p>
&lt;p>The switch index is the point of the precomputed strategy: with the factorisations already built,
a switching event costs a different lookup rather than new numerical work. That is what makes a
network with frequent switching affordable, and it is why the number of switches is bounded in
practice, since the enumeration grows as two to the power of that number.&lt;/p>
&lt;p>&lt;code>solveWithSystemMatrixRecomputation&lt;/code> is the other path. It asks &lt;code>hasVariableComponentChanged&lt;/code> each
step and rebuilds and refactorises only when something reports a change, which is the expensive but
general case used by variable components such as the SSN models.&lt;/p>
&lt;h2 id="iterative-components">Iterative components&lt;/h2>
&lt;p>After the solve, the solver checks whether any synchronous generator reports &lt;code>requiresIteration&lt;/code>.
If so it repeats the solve step until none does, which is how the predictor-corrector and two-stage
machine models reach the implicit solution rather than its explicit approximation. Models that do
not request iteration cost nothing here.&lt;/p>
&lt;p>This loop is the reason a machine model can be iterative without the whole solver being iterative.&lt;/p>
&lt;h2 id="linear-backends">Linear backends&lt;/h2>
&lt;p>The solver does not implement its own factorisation; it selects an adapter through
&lt;code>MnaSolverFactory&lt;/code>. The choices and their tuning are described under
&lt;a href="https://sogno.energy/dpsim/docs/developer-guide/solvers/alternative-solvers/">alternative solvers&lt;/a>, which also covers the ordering and
partial-refactorisation options that matter most when the matrix changes every step.&lt;/p>
&lt;h2 id="instrumentation">Instrumentation&lt;/h2>
&lt;p>&lt;code>Solver::mLogSolveTimes&lt;/code> records the wall-clock duration of each solve into &lt;code>mSolveTimes&lt;/code>, which is
the measurement to use when comparing backends or step sizes rather than timing the whole run.&lt;/p>
&lt;h2 id="source">Source&lt;/h2>
&lt;p>&lt;code>dpsim/src/MNASolver.cpp&lt;/code>, &lt;code>dpsim/src/MNASolverDirect.cpp&lt;/code>, and
&lt;code>dpsim/include/dpsim/MNASolverFactory.h&lt;/code>.&lt;/p></description></item><item><title>Docs: Reduced Order Machine Models</title><link>https://sogno.energy/dpsim/docs/concepts/models/synchronous-generator/reduced-order/</link><pubDate>Fri, 31 Jul 2026 00:00:00 +0000</pubDate><guid>https://sogno.energy/dpsim/docs/concepts/models/synchronous-generator/reduced-order/</guid><description>
&lt;p>The full dq0 machine keeps every rotor winding as a state. Reduced order models keep the rotor
flux linkages that matter on the timescale of interest and represent the rest algebraically, which
removes the fastest states and lets the machine be stepped at the same rate as the network.&lt;/p>
&lt;p>This page derives the equations. Nothing here depends on how they are arranged in software.&lt;/p>
&lt;h2 id="per-unit-system-and-operational-parameters">Per unit system and operational parameters&lt;/h2>
&lt;p>All quantities are in the reciprocal per unit system referred to the direct axis mutual
inductance [&lt;a href="#Kundur1994">Kundur1994&lt;/a>]. With rated apparent power $S_n$ and rated line to line
voltage $V_n$ as the stator base, the base peak phase voltage, base current and base impedance are&lt;/p>
&lt;div class="math">$$V_{base} = \frac{\sqrt{2}}{\sqrt{3}} V_n, \qquad
I_{base} = \frac{S_n}{\frac{3}{2} V_{base}}, \qquad
Z_{base} = \frac{V_n}{S_n / V_n}.$$&lt;/div>&lt;p>The machine is described by operational parameters rather than by winding data: the synchronous
inductances $L_d$ and $L_q$, the transient inductances $L_d&amp;rsquo;$ and $L_q&amp;rsquo;$, the subtransient
inductances $L_d&amp;rsquo;&amp;rsquo;$ and $L_q&amp;rsquo;&amp;rsquo;$, the corresponding open circuit time constants $T_{d0}&amp;rsquo;$,
$T_{q0}&amp;rsquo;$, $T_{d0}&amp;rsquo;&amp;rsquo;$, $T_{q0}&amp;rsquo;&amp;rsquo;$, and the inertia constant $H$. A sixth parameter $T_{aa}$, the
armature to field coupling time constant, distinguishes the two sixth order variants.&lt;/p>
&lt;h2 id="which-states-each-order-retains">Which states each order retains&lt;/h2>
&lt;p>The state variables are the voltages behind the transient and subtransient reactances,
$E_d&amp;rsquo;$, $E_q&amp;rsquo;$, $E_d&amp;rsquo;&amp;rsquo;$ and $E_q&amp;rsquo;&amp;rsquo;$, together with the two mechanical states. They are defined
from the terminal quantities by&lt;/p>
&lt;div class="math">$$E_d' = V_d - L_q' I_q, \qquad E_q' = V_q + L_d' I_d,$$&lt;/div>&lt;p>and analogously for the subtransient pair with $L_d&amp;rsquo;&amp;rsquo;$ and $L_q&amp;rsquo;&amp;rsquo;$.&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Order&lt;/th>
&lt;th>Electrical states&lt;/th>
&lt;th>Physical content&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>3&lt;/td>
&lt;td>$E_q'$&lt;/td>
&lt;td>Field winding only; the q axis rotor is neglected.&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>4&lt;/td>
&lt;td>$E_d&amp;rsquo;$, $E_q'$&lt;/td>
&lt;td>Field winding and one q axis damper.&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>5&lt;/td>
&lt;td>$E_q&amp;rsquo;$, $E_d&amp;rsquo;&amp;rsquo;$, $E_q&amp;rsquo;'$&lt;/td>
&lt;td>Adds both subtransient windings, no q axis transient state.&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>6a&lt;/td>
&lt;td>$E_d&amp;rsquo;$, $E_q&amp;rsquo;$, $E_d&amp;rsquo;&amp;rsquo;$, $E_q&amp;rsquo;'$&lt;/td>
&lt;td>Full transient and subtransient set, with $T_{aa} \neq 0$.&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>6b&lt;/td>
&lt;td>$E_d&amp;rsquo;$, $E_q&amp;rsquo;$, $E_d&amp;rsquo;&amp;rsquo;$, $E_q&amp;rsquo;'$&lt;/td>
&lt;td>Same states with $T_{aa} = 0$.&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>Every order carries the same two mechanical states, so the third order model has five states in
total and the sixth order models have eight.&lt;/p>
&lt;h2 id="voltage-behind-reactance-form">Voltage behind reactance form&lt;/h2>
&lt;p>Written directly, the stator equations couple the machine currents to the network currents, and the
machine inductances appear in the axis frame while the network is solved in the phase frame. The
voltage behind reactance form removes that coupling: the machine is expressed as an internal
voltage in series with a reactance that is constant in the axis frame, so the only quantity that
changes between steps is the internal voltage.&lt;/p>
&lt;p>The internal voltage is not a free variable. Applying the trapezoidal rule to the rotor flux
equations over one step $\Delta t$ gives it as a recursion in quantities already known at the start
of the step. For the transient states,&lt;/p>
&lt;div class="math">$$E_{h,d} = A_d' \, I_q + B_d' \, E_d', \qquad
E_{h,q} = A_q' \, I_d + B_q' \, E_q' + D_q' \left( E_{f}[k-1] + E_{f}[k] \right),$$&lt;/div>&lt;p>where $E_f$ is the field voltage supplied by the excitation system. The coefficients follow from
the trapezoidal integration and depend only on the parameters and the step size:&lt;/p>
&lt;div class="math">$$A_d' = \frac{\Delta t \, Z_d'}{2 T_{q0}' + \Delta t}, \qquad
B_d' = \frac{2 T_{q0}' - \Delta t}{2 T_{q0}' + \Delta t},$$&lt;/div>
&lt;div class="math">$$A_q' = \frac{-\Delta t \, Z_q'}{2 T_{d0}' + \Delta t}, \qquad
B_q' = \frac{2 T_{d0}' - \Delta t}{2 T_{d0}' + \Delta t}, \qquad
D_q' = \frac{\Delta t \left( 1 - T_f \right)}{2 T_{d0}' + \Delta t}.$$&lt;/div>&lt;p>The reactance differences are $Z_q&amp;rsquo; = L_d - L_d&amp;rsquo; - Y_d$ and $Z_d&amp;rsquo; = L_q - L_q&amp;rsquo; - Y_q$. For the
orders without subtransient states $Y_d = Y_q = 0$ and $T_f = 0$. For the sixth order variant with
armature coupling,&lt;/p>
&lt;div class="math">$$Y_d = \frac{T_{d0}''}{T_{d0}'} \frac{L_d''}{L_d'} \left( L_d - L_d' \right), \qquad
Y_q = \frac{T_{q0}''}{T_{q0}'} \frac{L_q''}{L_q'} \left( L_q - L_q' \right), \qquad
T_f = \frac{T_{aa}}{T_{d0}'}.$$&lt;/div>&lt;p>The subtransient states obey a recursion of the same shape with $T_{d0}&amp;rsquo;&amp;rsquo;$ and $T_{q0}&amp;rsquo;&amp;rsquo;$ in place
of the transient time constants, and with the transient history entering as a forcing term.&lt;/p>
&lt;p>Two properties of this form matter. The coefficients are computed once for a fixed step size, since
they contain no state. And because $B_d&amp;rsquo;$ and $B_q&amp;rsquo;$ are the trapezoidal amplification factors
$(2T - \Delta t)/(2T + \Delta t)$, they lie strictly inside the unit interval for any positive time
constant, so the flux recursion is unconditionally stable regardless of step size.&lt;/p>
&lt;h2 id="mechanical-equations">Mechanical equations&lt;/h2>
&lt;p>The rotor obeys the swing equation in per unit,&lt;/p>
&lt;div class="math">$$2H \frac{d \omega_r}{dt} = T_m - T_e, \qquad \frac{d \theta}{dt} = \omega_{base} \, \omega_r,$$&lt;/div>&lt;p>with the electrical torque taken from the axis frame quantities as
$T_e = V_d I_d + V_q I_q$. The load angle follows from the initial operating point as the phase of
$V + j L_q I$, which is the standard construction of the q axis position from terminal conditions.&lt;/p>
&lt;h2 id="solution-schemes">Solution schemes&lt;/h2>
&lt;p>The recursion above evaluates the internal voltage from quantities at the previous step, so the
machine and the network are solved once per step in sequence. Two refinements exist for cases where
that single pass is not accurate enough.&lt;/p>
&lt;p>The predictor corrector method takes the single pass result as a prediction, re-evaluates the flux
recursion using the corrected terminal quantities, and repeats until the change between successive
passes falls below a tolerance. It converges to the solution of the implicit trapezoidal step
rather than to its explicit approximation, at the cost of repeated network solutions.&lt;/p>
&lt;p>The two stage predictor method splits the step differently: it advances the machine state on a
predicted terminal voltage, then applies a single correction derived from the network solution,
without iterating to convergence. It costs one extra network solve per step and removes most of the
one step delay error.&lt;/p>
&lt;p>Both are schemes for solving the same equations. They do not change the model order or the retained
states.&lt;/p>
&lt;h2 id="validity">Validity&lt;/h2>
&lt;p>The reduced order models assume the stator transients are fast enough to be neglected, so the
stator is treated as algebraic. This is the assumption that makes the model valid for
electromechanical studies and invalid where stator dynamics matter, such as during the first cycles
of a close-in fault or for subsynchronous interaction. Neglecting the q axis rotor entirely, as the
third order model does, additionally removes damping that is physically present, so a third order
machine oscillates more than the same machine represented at fourth order.&lt;/p>
&lt;h2 id="references">References&lt;/h2>
&lt;ul>
&lt;li>&lt;a name="Kundur1994">&lt;/a>[Kundur1994] P. Kundur, &lt;em>Power System Stability and Control&lt;/em>. New York: McGraw-Hill, 1994. Source of the reciprocal per unit system and of the operational parameter definitions used throughout this page.&lt;/li>
&lt;/ul></description></item><item><title>Docs: Architecture</title><link>https://sogno.energy/dpsim/docs/developer-guide/architecture-and-conventions/architecture/</link><pubDate>Wed, 25 Mar 2020 00:00:00 +0000</pubDate><guid>https://sogno.energy/dpsim/docs/developer-guide/architecture-and-conventions/architecture/</guid><description>
&lt;h2 id="modules-and-dependencies">Modules and Dependencies&lt;/h2>
&lt;p>The figure below shows the main components of the DPsim library and their dependencies on other software projects.
All functionality is implemented in the C++ core, which can be used standalone or together with the Python interface.
The Python interface is a thin wrapper of the C++ core.
Jupyter notebooks can either use the DPsim Python interface to run simulations or call executables implemented in C++.
The data analysis and plotting is always done in Python using common libraries like Matplotlib.
To collect the simulation results from within Python, one can use the villas-dataprocessing Python package.&lt;/p>
&lt;p>&lt;img src="dpsim_modules.svg" alt="image">&lt;/p>
&lt;p>Another approach to get data in or out of DPsim is the VILLASnode interface, which does not depend on Python at all.
The main purpose of the VILLASnode interface is to exchange data during the simulation runtime, for example, in real-time simulation experiments.
The data could be send to other simulators, hardware or other software components like databases.
Storing the data in databases can be another way of managing (also offline) simulation results if the Python CSV method is not desireable.&lt;/p>
&lt;p>The CIM reader is based on the CIM++ library and provides a comfortable alternative to defining the grid manually in C++ or Python.
In principle, it calls the same functions to create elements, which are also used in the C++ defined example scenarios, but automatically.
DPsim also provides a way to visualize the defined networks before simulation.&lt;/p>
&lt;p>The main solver of DPsim is currently the MNA solver because it enables a rather deterministic computation time per simulation time step, which is necessary for real-time simulation.
Apart from that, it is also well established in offline circuit simulation.
The only dependency of the MNA solver is the linear algebra library Eigen.
For some component models, it is possible to use the Sundials ODE solver in combination with the MNA solver. In that case, the component is solved by the ODE solver whereas the network is still handled by the MNA solver.
A DAE solver is currently under development.
Its main purpose will be offline simulation, for example, to provide reference results where simulation runtime and real-time execution are not relevant.&lt;/p>
&lt;p>The component models depend mostly on the Eigen library.
Even if components are used in combination with Sundials ODE / DAE solvers, we try to keep the specific functions required by these solvers independent of the Sundials package.&lt;/p>
&lt;h2 id="class-hierarchy">Class Hierarchy&lt;/h2>
&lt;p>The &lt;code>Simulation&lt;/code> class holds references to instances of &lt;code>Interface&lt;/code>, &lt;code>Solver&lt;/code>, &lt;code>Logger&lt;/code> and &lt;code>SystemTopology&lt;/code>.
For a simulation scenario, the minimum description would include a &lt;code>SystemTopology&lt;/code> and a solver type.
The &lt;code>Solver&lt;/code> instance is then created by the &lt;code>Simulation&lt;/code>.&lt;/p>
&lt;p>&lt;img src="dpsim_classes_simulation.svg" alt="image">&lt;/p>
&lt;p>An important function of the &lt;code>Simulation&lt;/code> is to collect all tasks, which have to be executed during the simulation.
These tasks include computation steps of the individual power system component models as well as read and write tasks of the interfaces and logging variables etc.
Before the scheduling is done, &lt;code>Simulation&lt;/code> calls &lt;code>getTasks()&lt;/code> to retrieve the tasks from three of these classes: &lt;code>Solver&lt;/code>, &lt;code>Interface&lt;/code> and &lt;code>Logger&lt;/code>.
The power system component and signal-model tasks are collected by the &lt;code>Solver&lt;/code> instances and relayed to the &lt;code>Simulation&lt;/code>, while interfaces and loggers contribute their own tasks directly.&lt;/p>
&lt;p>All power system element classes inherit from the &lt;code>IdentifiedObject&lt;/code> class.
This class corresponds with the &lt;code>IdentifiedObject&lt;/code> of the IEC61970 CIM and has a &lt;code>uid&lt;/code> and &lt;code>name&lt;/code> attribute as well.&lt;/p>
&lt;p>&lt;img src="dpsim_classes_identifiedobject.svg" alt="image">&lt;/p>
&lt;p>The next layer of specialization includes information on the topological connection between network elements.
An electrical bus and network nodes in general are represented by the &lt;code>TopologiclaNode&lt;/code> class.
The connection of electrical components, &lt;code>TopologicalPowerComp&lt;/code>, is managed via terminals of type &lt;code>TopologicalTerminal&lt;/code>.
These three types describe the &lt;em>electrical&lt;/em> connections of the network, which are bidirectional and include voltages and currents.
The signal type elements, &lt;code>TopologicalSignalComp&lt;/code>, can only have unidirectional components, which are not expressed using node and terminals.
Instead, the attribute system is used to define signal type connections.&lt;/p></description></item><item><title>Docs: Nodal Analysis</title><link>https://sogno.energy/dpsim/docs/concepts/nodal-analysis/</link><pubDate>Wed, 18 Mar 2020 00:00:00 +0000</pubDate><guid>https://sogno.energy/dpsim/docs/concepts/nodal-analysis/</guid><description>
&lt;p>A circuit with $b$ branches has $2b$ unknowns since there are $b$ voltages and $b$ currents.
Hence, $2b$ linear independent equations are required to solve the circuit.
If the circuit has $n$ nodes and $b$ branches, it has&lt;/p>
&lt;ul>
&lt;li>Kirchoff&amp;rsquo;s current law (KCL) equations&lt;/li>
&lt;li>Kirchoff&amp;rsquo;s voltage law (KVL) equations&lt;/li>
&lt;li>Characteristic equations (Ohm&amp;rsquo;s Law)&lt;/li>
&lt;/ul>
&lt;p>There are only $n-1$ KCLs since the nth equation is a linear combination of the remaining $n-1$.
At the same time, it can be demonstrated that if we can imagine a very high number of closed paths in the network, only $b-n+1$ are able to provide independent KVLs.
Finally there are $b$ characteristic equations, describing the behavior of the branch, making a total of $2b$ linear independent equations.&lt;/p>
&lt;p>The nodal analysis method reduces the number of equations that need to be solved simultaneously.
$n-1$ voltage variables are defined and solved, writing $n-1$ KCL based equations.
A circuit can be solved using Nodal Analysis as follows&lt;/p>
&lt;ul>
&lt;li>Select a reference node (mathematical ground) and number the remaining $n-1$ nodes, that are the independent voltage variables&lt;/li>
&lt;li>Represent every branch current $i$ as a function of node voltage variables $v$ with the general expression $i = g(v)$&lt;/li>
&lt;li>Write $n-1$ KCL based equations in terms of node voltage variable.&lt;/li>
&lt;/ul>
&lt;p>The resulting equations can be written in matrix form and have to be solved for $v$.&lt;/p>
&lt;div class="math">$$\boldsymbol{Y} \boldsymbol{v} = \boldsymbol{i}$$&lt;/div>&lt;h2 id="assembling-the-system-by-stamping">Assembling the System by Stamping&lt;/h2>
&lt;p>The matrix $\boldsymbol{Y}$ and the right hand side $\boldsymbol{i}$ are never written out by
inspecting the whole circuit at once. Each element contributes its own fixed pattern of entries,
and the system is formed by adding those contributions together. A conductance $G$ between nodes
$k$ and $l$ adds $G$ to the diagonal entries $Y_{kk}$ and $Y_{ll}$ and subtracts it from the
off-diagonal entries $Y_{kl}$ and $Y_{lk}$; if one terminal is the reference node, only the
single diagonal entry appears. A current source injecting $I$ into node $k$ from node $l$ adds
$I$ to $i_k$ and subtracts it from $i_l$.&lt;/p>
&lt;p>This additive assembly is what makes the method practical. An element needs to know only the
indices of the nodes it is attached to, never anything about the rest of the network, and the
same element contributes the same pattern regardless of what it is connected to. It also
explains the structure of the result: each row corresponds to one node and holds a non-zero
entry only for nodes reachable through a single element, so $\boldsymbol{Y}$ is symmetric for
networks of passive elements and sparse for any network of realistic size.&lt;/p>
&lt;h2 id="dynamic-elements-and-the-companion-model">Dynamic Elements and the Companion Model&lt;/h2>
&lt;p>The formulation above assumes every branch current can be written as a function of node voltages
alone. Inductors and capacitors do not satisfy this, since their currents depend on derivatives.
They are brought into the same form by discretising the differential relation over one time step.
Applying the trapezoidal rule to a capacitor between the instants $t - \Delta t$ and $t$ gives&lt;/p>
&lt;div class="math">$$i(t) = \frac{2C}{\Delta t} v(t) - \left( \frac{2C}{\Delta t} v(t - \Delta t) + i(t - \Delta t) \right),$$&lt;/div>&lt;p>and the same treatment of an inductor gives&lt;/p>
&lt;div class="math">$$i(t) = \frac{\Delta t}{2L} v(t) + \left( \frac{\Delta t}{2L} v(t - \Delta t) + i(t - \Delta t) \right).$$&lt;/div>&lt;p>Both have the form of a conductance in parallel with a current source. The conductance depends
only on the element value and the time step, so it is a constant contribution to
$\boldsymbol{Y}$; the current source depends only on quantities from the previous step, which are
known when the current step begins, so it is a contribution to $\boldsymbol{i}$. This pair is
called the companion model of the element, and the current source term is called its history
term. Once every dynamic element has been replaced by its companion model, the network at each
instant is a purely resistive one and the nodal formulation applies unchanged.&lt;/p>
&lt;h2 id="extending-the-formulation-for-voltage-sources">Extending the Formulation for Voltage Sources&lt;/h2>
&lt;p>An ideal voltage source cannot be stamped as a conductance, because its current is not determined
by the voltage across it. The formulation is extended by admitting the source current as an
additional unknown and adding the equation that constrains its terminal voltage. The system
becomes&lt;/p>
&lt;div class="math">$$\begin{bmatrix} \boldsymbol{Y} &amp; \boldsymbol{A} \\ \boldsymbol{A}^{\mathsf{T}} &amp; \boldsymbol{0} \end{bmatrix}
\begin{bmatrix} \boldsymbol{v} \\ \boldsymbol{j} \end{bmatrix}
=
\begin{bmatrix} \boldsymbol{i} \\ \boldsymbol{u} \end{bmatrix},$$&lt;/div>&lt;p>where $\boldsymbol{j}$ collects the unknown source currents, $\boldsymbol{u}$ the prescribed
source voltages, and $\boldsymbol{A}$ has a single $+1$ and $-1$ per source marking its terminals.
This extension is known as modified nodal analysis, and it is the form actually solved. The
augmented matrix is no longer positive definite and carries zeros on part of its diagonal, which
is why the solver has to be one that tolerates that rather than one specialised to the passive
case.&lt;/p>
&lt;h2 id="solving-over-time">Solving Over Time&lt;/h2>
&lt;p>Within a simulation the same system is solved once per time step. The left hand side depends only
on the element values, the topology and the time step, none of which change from one step to the
next in the ordinary case, so the matrix is factorised once and each step reuses that
factorisation with a new right hand side. The cost of a step is then a forward and backward
substitution rather than a full solve, which is what makes the method viable for large networks
and for real time.&lt;/p>
&lt;p>Two situations invalidate the factorisation. A switching event changes the topology and therefore
the matrix, so the affected configuration has to be factorised again; simulations that switch
frequently often pre-compute a factorisation for each configuration instead. A non-linear element
makes the entries themselves depend on the solution, which requires iterating within the step
until the node voltages and the element operating points agree.&lt;/p></description></item><item><title>Docs: RLC-Elements</title><link>https://sogno.energy/dpsim/docs/concepts/models/rlc-elements/</link><pubDate>Wed, 18 Mar 2020 00:00:00 +0000</pubDate><guid>https://sogno.energy/dpsim/docs/concepts/models/rlc-elements/</guid><description>
&lt;h2 id="emt-equations-and-modified-nodal-analysis">EMT Equations and Modified Nodal Analysis&lt;/h2>
&lt;h3 id="inductance">Inductance&lt;/h3>
&lt;p>An inductance is described by&lt;/p>
&lt;div class="math">$$v_j(t) - v_k(t) = v_L(t) = L \frac{\mathrm{d} i_L(t)}{\mathrm{d}t}$$&lt;/div>&lt;p>Integration results in an equation to compute the current at time $t$ from a previous state at $t - \Delta t$.&lt;/p>
&lt;div class="math">$$i_L(t) = i_L(t - \Delta t) + \frac{1}{L} \ \int_{t - \Delta t}^{t} v_L(\tau) \ \mathrm{d} \tau$$&lt;/div>&lt;p>There are various methods to discretize this equation in order to solve it numerically.
The trapezoidal rule, an implicit second-order method, is commonly applied for circuit simulation:&lt;/p>
&lt;div class="math">$$\int_{t - \Delta t}^{t} f(\tau) \ \mathrm{d} \tau \approx \frac{\Delta t}{2}(f(t) + f(t - \Delta t))$$&lt;/div>&lt;p>Applying the trapezoidal rule to leads to&lt;/p>
&lt;div class="math">$$i_L(t) = i_L(t - \Delta t) + \frac{\Delta t}{2L}(v_L(t) + v_L(t - \Delta t))$$&lt;/div>&lt;p>This can be rewritten in terms of an equivalent conductance and current source and the number of time steps $k$ with size $\Delta t$.&lt;/p>
&lt;div class="math">$$i_L(k) = g_L v_L(k) + i_{L,equiv}(k-1)$$&lt;/div>
&lt;div class="math">$$i_{L,equiv}(k-1) = i_L(k-1) + \frac{\Delta t}{2L} v_L(k-1)$$&lt;/div>
&lt;div class="math">$$g_L = \frac{\Delta t}{2L}$$&lt;/div>&lt;p>Hence, components described by differential equations are transformed into a DC equivalent circuit as depicted in the figure below.&lt;/p>
&lt;p>&lt;img src="electrical_resistive_companion_inductance.svg" alt="inductance resistive companion">&lt;/p>
&lt;h3 id="capacitance">Capacitance&lt;/h3>
&lt;p>The same procedure can be applied to a capacitance.
Integration on both side yields&lt;/p>
&lt;div class="math">$$i_C(t) = C \frac{\mathrm{d}}{\mathrm{d}t} \ v_C(t)$$&lt;/div>
&lt;div class="math">$$v_C(t) = v_C(t - \Delta t) + \frac{1}{C} \int_{t - \Delta t}^t i_C(\tau) \mathrm{d} \tau$$&lt;/div>&lt;p>Finally, the equivalent circuit is described by a current source and a conductance.&lt;/p>
&lt;div class="math">$$i_{C}(k) = g_{C} v_C(k) + i_{C,equiv}(k-1)$$&lt;/div>
&lt;div class="math">$$i_{C,equiv}(k-1) = -i_{C}(k-1) - g_C v_C(k-1)$$&lt;/div>
&lt;div class="math">$$g_{C} = \frac{2C}{\Delta t}$$&lt;/div>&lt;p>This equation set is visualized in the figure below.&lt;/p>
&lt;p>&lt;img src="electrical_resistive_companion_capacitance.svg" alt="capacitance resistive companion">&lt;/p>
&lt;p>Hence, the vector of unknowns $\boldsymbol{x}$ and the source vector $\boldsymbol{b}$ become time dependent and this leads to the system description:&lt;/p>
&lt;div class="math">$$\boldsymbol{A} \boldsymbol{x}(t) = \boldsymbol{b}(t)$$&lt;/div>&lt;p>To simulate the transient behavior of circuits, this linear equation has to be solved repeatedly.
As long as the system topology and the time step is fixed, the system matrix is constant.&lt;/p>
&lt;h3 id="extension-with-dynamic-phasors">Extension with Dynamic Phasors&lt;/h3>
&lt;p>The dynamic phasor concept can be integrated with nodal analysis.
The overall procedure does not change but the system equations are rewritten using complex numbers and all variables need to be expressed in terms of dynamic phasors.
Therefore, the resistive companion representations of inductances and capacitances have to be adapted as well.&lt;/p>
&lt;h3 id="inductance-1">Inductance&lt;/h3>
&lt;p>In dynamic phasors the integration of the inductance equation yields&lt;/p>
&lt;div class="math">$$\begin{align}
\langle v_L \rangle(t) &amp;= \Big \langle L \frac{\mathrm{d} i_L}{\mathrm{d}t} \Big \rangle(t) \nonumber \\
&amp;= L \frac{\mathrm{d}}{dt} \langle i_L \rangle(t) + j \omega L \ \langle i_L \rangle(t)
\end{align}$$&lt;/div>
&lt;div class="math">$$\langle i_L \rangle(t) = \langle i_L \rangle(t - \Delta t) + \int_{t - \Delta t}^t \frac{1}{L} \langle v_L \rangle(\tau) - j \omega \ \langle i_L \rangle(\tau) \mathrm{d} \tau$$&lt;/div>&lt;p>Applying the trapezoidal method leads to the finite difference equation:&lt;/p>
&lt;div class="math">$$\begin{aligned}
\langle i_L \rangle(k) = \langle i_L \rangle(k-1) + \frac{\Delta t}{2} \bigg[ \frac{1}{L} (\langle v_L \rangle(k) + \langle v_L \rangle(k-1))
- j \omega (\langle i_L \rangle(t) + \langle i_L \rangle(k-1) \bigg]
\end{aligned}$$&lt;/div>&lt;p>Solving this for $\langle i_L \rangle(k)$ results in the \ac{DP} equivalent circuit model:&lt;/p>
&lt;div class="math">$$\langle i_L \rangle(k) = \frac{a - jab}{1 + b^2} \langle v_L \rangle(k) + \langle i_{L,equiv} \rangle(k-1)$$&lt;/div>&lt;p>with&lt;/p>
&lt;div class="math">$$a = \frac{\Delta t}{2L}, \qquad b = \frac{\Delta t \omega}{2}$$&lt;/div>
&lt;div class="math">$$\langle i_{L,equiv} \rangle(k-1) = \frac{1 - b^2 - j2b}{1 + b^2} \langle i_L \rangle(k-1) + \frac{a - jab}{1 + b^2} \langle v_L \rangle(k-1)$$&lt;/div>&lt;h3 id="capacitance-1">Capacitance&lt;/h3>
&lt;p>Similarly, a capacitance is described by as follows&lt;/p>
&lt;div class="math">$$\langle i_C \rangle(k) = C \ \frac{\mathrm{d} \langle v_C \rangle}{\mathrm{d} t} + j \omega C \ \langle v_C \rangle(t)$$&lt;/div>
&lt;div class="math">$$v_C(t) = v_C(t- \Delta t) + \int_{t- \Delta t}^{t} \frac{1}{C} \ i_C(\tau) -j \omega \ v_C(\tau) \ \mathrm{d} \tau$$&lt;/div>&lt;p>Applying the trapezoidal rule for the capacitance equation leads to the finite difference equation:&lt;/p>
&lt;div class="math">$$\begin{aligned}
\langle v_C \rangle(k) = \langle v_C \rangle(k-1)
+ \frac{\Delta t}{2} \bigg[ \frac{1}{C} \ \langle i_C \rangle(k) - j \omega \ \langle v_C \rangle(k) \\
+ \frac{1}{C} \ \langle i_C \rangle(k-1) - j \omega \ \langle v_C \rangle(k-1) \bigg]
\end{aligned}$$&lt;/div>&lt;p>The DP model for the capacitance is defined by&lt;/p>
&lt;div class="math">$$\langle i_C \rangle(k) = \frac{1+jb}{a} \ \langle v_C \rangle(k) + \langle i_{C,equiv} \rangle(k-1)$$&lt;/div>&lt;p>with&lt;/p>
&lt;div class="math">$$a = \frac{\Delta t}{2C}, \qquad
b = \frac{\Delta t \omega}{2}$$&lt;/div>
&lt;div class="math">$$\langle i_{C,equiv} \rangle(k-1) = - \frac{1-jb}{a} \ \langle v_C \rangle(k-1) - \langle i_C \rangle(k-1)$$&lt;/div>&lt;h3 id="rl-series-element">RL-series element&lt;/h3>
&lt;p>In dynamic phasors the integration of the inductance equation yields&lt;/p>
&lt;div class="math">$$\langle v \rangle(t) = L \frac{\mathrm{d}}{dt} \langle i \rangle(t) + j \omega L \ \langle i \rangle(t) + R \ \langle i \rangle(t)$$&lt;/div>
&lt;div class="math">$$\langle i \rangle(t) = \langle i \rangle(t - \Delta t) + \int_{t - \Delta t}^t \frac{1}{L} \langle v \rangle(\tau) - j \omega \ \langle i \rangle(\tau) - \frac{R}{L} \ \langle i \rangle(\tau) \mathrm{d} \tau$$&lt;/div>&lt;p>Applying the trapezoidal method leads to the finite difference equation:&lt;/p>
&lt;div class="math">$$\begin{aligned}
\langle i \rangle(k) = \langle i \rangle(k-1) + \frac{\Delta t}{2} \bigg[ \frac{1}{L} (\langle v \rangle(k) + \langle v \rangle(k-1))
- \left( j \omega + \frac{R}{L} \right) (\langle i \rangle(k) + \langle i \rangle(k-1)) \bigg]
\end{aligned}$$&lt;/div>&lt;p>Solving this for $\langle i \rangle(k)$ results in the \ac{DP} equivalent circuit model:&lt;/p>
&lt;div class="math">$$\langle i \rangle(k) = \frac{a + Ra^2 - jab}{(1+Ra)^2 + b^2} \langle v \rangle(k) + \langle i_{equiv} \rangle(k-1)$$&lt;/div>&lt;p>with&lt;/p>
&lt;div class="math">$$a = \frac{\Delta t}{2L}, \qquad b = \frac{\Delta t \omega}{2}$$&lt;/div>
&lt;div class="math">$$\langle i_{equiv} \rangle(k-1) = \frac{1 - b^2 - j2b + 2Ra + (Ra)^2 - j2Rab}{(1+Ra^2) + b^2} \langle i \rangle(k-1) + \frac{a + Ra^2 - jab}{(1+Ra)^2 + b^2} \langle v \rangle(k-1)$$&lt;/div></description></item><item><title>Docs: EMT Ph3 Averaged Voltage Source Inverter</title><link>https://sogno.energy/dpsim/docs/concepts/models/power-electronics/emt-ph3-averaged-vsi/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://sogno.energy/dpsim/docs/concepts/models/power-electronics/emt-ph3-averaged-vsi/</guid><description>
&lt;p>This model represents a grid-following averaged voltage source inverter in the EMT domain.
Because its state-space form is recomputed as the operating point moves, it is solved simultaneously with the network rather than through a delayed injection.
The model includes a PLL, filtered active/reactive power measurement, outer power control, inner current control, and an LC filter with coupling resistance to the grid node.&lt;/p>
&lt;p>The terminal input is the PCC voltage vector&lt;/p>
&lt;div class="math">$$\mathbf{u} =
\begin{bmatrix}
u_a &amp; u_b &amp; u_c
\end{bmatrix}^\top ,$$&lt;/div>&lt;p>and the state vector is&lt;/p>
&lt;div class="math">$$\mathbf{x} =
\begin{bmatrix}
\theta_{\mathrm{PLL}} &amp;
\phi_{\mathrm{PLL}} &amp;
P &amp;
Q &amp;
\phi_d &amp;
\phi_q &amp;
\gamma_d &amp;
\gamma_q &amp;
v_{c,a} &amp;
v_{c,b} &amp;
v_{c,c} &amp;
i_{f,a} &amp;
i_{f,b} &amp;
i_{f,c}
\end{bmatrix}^\top .$$&lt;/div>&lt;p>The model output is the interface current injected into the MNA system,&lt;/p>
&lt;div class="math">$$\mathbf{y} =
\frac{\mathbf{u} - \mathbf{v}_c}{R_c}.$$&lt;/div>&lt;h2 id="model-equations">Model equations&lt;/h2>
&lt;p>The controller uses the opposite current direction, i.e. positive current denotes inverter injection into the grid,&lt;/p>
&lt;div class="math">$$\mathbf{i}_{rc} =
\frac{\mathbf{v}_c - \mathbf{u}}{R_c}.$$&lt;/div>&lt;p>The Park transformation with PLL angle $\theta_{\mathrm{PLL}}$ is used to obtain dq quantities,&lt;/p>
&lt;div class="math">$$\begin{bmatrix}
v_{c,d} \\
v_{c,q}
\end{bmatrix}
=
\mathbf{T}(\theta_{\mathrm{PLL}})\mathbf{v}_c,
\qquad
\begin{bmatrix}
i_{rc,d} \\
i_{rc,q}
\end{bmatrix}
=
\mathbf{T}(\theta_{\mathrm{PLL}})\mathbf{i}_{rc}.$$&lt;/div>&lt;p>The instantaneous active and reactive powers are calculated as&lt;/p>
&lt;div class="math">$$p = v_{c,d} i_{rc,d} + v_{c,q} i_{rc,q},$$&lt;/div>
&lt;div class="math">$$q = -v_{c,d} i_{rc,q} + v_{c,q} i_{rc,d}.$$&lt;/div>&lt;p>The PLL and power-filter dynamics are&lt;/p>
&lt;div class="math">$$\dot{\theta}_{\mathrm{PLL}}
=
\omega_n + K_{p,\mathrm{PLL}} v_{c,q} +
K_{i,\mathrm{PLL}} \phi_{\mathrm{PLL}},$$&lt;/div>
&lt;div class="math">$$\dot{\phi}_{\mathrm{PLL}} = v_{c,q},$$&lt;/div>
&lt;div class="math">$$\dot{P} = \omega_c(p - P),
\qquad
\dot{Q} = \omega_c(q - Q).$$&lt;/div>&lt;p>The outer power-control integrators and current references are&lt;/p>
&lt;div class="math">$$\dot{\phi}_d = P_{\mathrm{ref}} - P,
\qquad
\dot{\phi}_q = Q - Q_{\mathrm{ref}},$$&lt;/div>
&lt;div class="math">$$i_{d,\mathrm{ref}}
=
K_{p,P}(P_{\mathrm{ref}} - P) + K_{i,P}\phi_d,$$&lt;/div>
&lt;div class="math">$$i_{q,\mathrm{ref}}
=
K_{p,P}(Q - Q_{\mathrm{ref}}) + K_{i,P}\phi_q.$$&lt;/div>&lt;p>The inner current-control integrators and voltage references are&lt;/p>
&lt;div class="math">$$\dot{\gamma}_d = i_{d,\mathrm{ref}} - i_{rc,d},
\qquad
\dot{\gamma}_q = i_{q,\mathrm{ref}} - i_{rc,q},$$&lt;/div>
&lt;div class="math">$$v_{d,\mathrm{ref}}
=
K_{p,I}(i_{d,\mathrm{ref}} - i_{rc,d}) +
K_{i,I}\gamma_d,$$&lt;/div>
&lt;div class="math">$$v_{q,\mathrm{ref}}
=
K_{p,I}(i_{q,\mathrm{ref}} - i_{rc,q}) +
K_{i,I}\gamma_q.$$&lt;/div>&lt;p>The reference voltage is transformed back to abc coordinates,&lt;/p>
&lt;div class="math">$$\mathbf{v}_{\mathrm{ref}}
=
\mathbf{T}^{-1}(\theta_{\mathrm{PLL}})
\begin{bmatrix}
v_{d,\mathrm{ref}} \\
v_{q,\mathrm{ref}}
\end{bmatrix}.$$&lt;/div>&lt;p>The LC filter dynamics are&lt;/p>
&lt;div class="math">$$\dot{\mathbf{v}}_c
=
\frac{1}{C_f}\mathbf{i}_f
+
\frac{1}{C_f R_c}(\mathbf{u} - \mathbf{v}_c),$$&lt;/div>
&lt;div class="math">$$\dot{\mathbf{i}}_f
=
\frac{1}{L_f}
\left(
\mathbf{v}_{\mathrm{ref}}
-
\mathbf{v}_c
-
R_f \mathbf{i}_f
\right).$$&lt;/div>&lt;p>At each simulation step, the nonlinear model is locally linearized into the affine state-space form&lt;/p>
&lt;div class="math">$$\dot{\mathbf{x}}
\approx
\mathbf{A}\mathbf{x}
+
\mathbf{B}\mathbf{u}
+
\mathbf{E},$$&lt;/div>
&lt;div class="math">$$\mathbf{y}
\approx
\mathbf{C}\mathbf{x}
+
\mathbf{D}\mathbf{u}
+
\mathbf{F},$$&lt;/div>&lt;p>which is then discretized and stamped into the EMT MNA system.&lt;/p>
&lt;p>How this is arranged in code, together with the source and the runnable examples, is covered under
&lt;a href="https://sogno.energy/dpsim/docs/developer-guide/model-implementations/emt-ph3-averaged-vsi-implementation/">EMT Ph3 averaged VSI implementation&lt;/a>.&lt;/p></description></item><item><title>Docs: Exciters and Power System Stabiliser</title><link>https://sogno.energy/dpsim/docs/concepts/models/synchronous-generator-regulators/exciters/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://sogno.energy/dpsim/docs/concepts/models/synchronous-generator-regulators/exciters/</guid><description>
&lt;p>DC1 type model is the standard IEEE type DC1 exciter, whereas the other model is a simplified version of the IEEE DC1 type model. The inputs of the exciters are the magnitude of the terminal voltage of the generator connected to the exciter $v_h$ and the voltage reference $v_{ref}$, which is defined as a variable since other devices such as over-excitation limiters or power system stabilizers (PSS) modify such reference with additional signals. At the moment, no over-excitation limiters have been implemented in DPSim so that the reference voltage is given by:
$$
v_{ref}(t) = v_{ref,0} + v_{pss}(t)
$$
where $v_{ref,0}$ is initialized after the power flow computations and $v_{pss}(t)$ is the output of the (optional) PSS connected to the exciter. The output of the exciter systems is the induced emf by the field current at $t=k + \Delta t$: $v_{ef}(k + \Delta t)$ (sometimes the alternative notation $e_{fd}(k + \Delta t)$ is used).&lt;/p>
&lt;h2 id="ieee-type-dc1-exciter-model">IEEE Type DC1 exciter model&lt;/h2>
&lt;center>
&lt;figure margin=30%>
&lt;img src="./images/DC1C_exciter.png" alt="DC1_exciter">
&lt;figcaption>Fig. 1: Control diagram of the IEEE Type DC1 exciter &lt;/br>
Adapted from: Milano, Frequency Variations in Power Systems
&lt;/figcaption>
&lt;/figure>
&lt;/center>
This model is used to represent field controlled dc commutator exciters with continuously acting voltage regulators (especially the direct-acting rheostatic, rotating
amplifier, and magnetic amplifier types). The control diagram of this exciter is depicted in Fig. 1 and it is described by the following set of differential equations:
&lt;/br>&lt;/br>
&lt;p>$$
T_{R} \frac{d}{dt} v_{R}(t) = v_{h}(t) - v_{R}(t)
$$&lt;/p>
&lt;p>$$
T_{b} \frac{d}{dt} v_{b}(t) = v_{ref} - v_{R}(t) - v_{f}(t) - v_{b}(t),
$$
$$
T_{a} \frac{d}{dt} v_{a}(t) = K_{a} v_{in}(t) - v_{a}(t),
$$
$$
T_{f} \frac{d}{dt} v_{f}(t) - K_{f} \frac{d}{dt} v_{ef}(t) = -v_{f}(t),
$$
$$
T_{ef} \frac{d}{dt} v_{ef}(t) = v_{a}(t) - (K_{ef} + sat(t)) v_{ef}(t),
$$
where $v_h$ is the module of the machine&amp;rsquo;s terminal voltage, and $v_{in}$ is the amplifier input signal, which for the IEEE Type DC1 is given by:
$$
v_{in}(t) = T_{c} \frac{d}{dt} v_b(t) + v_b(t).
$$&lt;/p>
&lt;p>The ceiling function approximates the saturation of the excitation winding:
$$
sat(t) = A_{ef} e^{(B_{ef} | v_{ef}(t) | )}
$$&lt;/p>
&lt;p>The set of differential equations are discretized using forward euler in order to solve it numerically, which leads to the following set of algebraic equations:
$$
v_R(k + \Delta t) = v_R(k) + \frac{\Delta t}{T_R} ( v_h(k) - v_R(k) ),
$$
$$
v_b(k + \Delta t) = v_b(k)(1 - \frac{\Delta t}{T_b}) + \frac{\Delta t}{T_b} ( v_{ref}(k) - v_R(k) - v_f(k)),
$$
$$
v_{in}(k + \Delta t) = \Delta t \cdot \frac{T_c}{T_b} (v_{ref}(k) - v_R(k) - v_{f}(k) - v_b(k)) + v_b(k+1),
$$
$$
v_a(k + \Delta t) = v_a(k) + \frac{\Delta t}{T_a} ( v_{in}(k) K_a - v_a(k) ),
$$
$$
v_f(k + \Delta t) = (1 - \frac{\Delta t}{T_f}) v_f(k) + \frac{\Delta t K_f}{T_f T_{ef}} ( v_{a}(k) - (K_{ef} + sat(k)) v_{ef}(k) ),
$$
$$
v_{ef}(k + \Delta t) = v_{ef}(k) + \frac{\Delta t}{T_{ef}} ( v_{a}(k) - (sat(k) + K_{ef}) v_{ef}(k)),
$$
$$
sat(k) = A_{ef} e^{(B_{ef} | v_{ef}(k) | )}
$$&lt;/p>
&lt;p>Since the values of all variables for $t=k$ are known, $v_{ef}(k+1)$ can be easily calculated using the discretised equations, which is carried out in the &lt;code>preStep&lt;/code> function of the generator connected to each exciter.&lt;/p>
&lt;p>The initial values of all variables, which are used in the first simulation step, are calculated assuming that the simulation starts in the steady. This is equivalent to assume that all derivative are equal to zero, which leads to:
$$
v_R(k=0) = v_h(k=0),
$$
$$
v_f(k=0) = 0
$$
$$
v_a(k=0) = K_{ef} v_{ef}(k=0) + A_{ef} e^{B_{ef} |v_{ef} (k=0)|} v_{ef}(k=0),
$$
$$
v_{in}(k=0) = \frac{v_a(k=0)}{K_a},
$$
$$
v_b(k=0) = v_{in}(k=0),
$$
$$
v_{ref}(t=0) = v_{in}(t=0) + v_b(t=0),
$$
where $v_h(k=0)$, $v_{ef}(k=0)$ are calculated after the power flow analysis and after the initialization of synchronous machines (see section initialization of SG).&lt;/p>
&lt;h2 id="simplified-ieee-type-dc1-exciter-model-dc1simp">Simplified IEEE Type DC1 exciter model (DC1Simp)&lt;/h2>
&lt;center>
&lt;figure>
&lt;img src="./images/DC1CSimp_exciter.png" alt="DC1A_exciter">
&lt;figcaption>&lt;/br>Fig. 2: Control diagram of the IEEE Type DC1 exciter &lt;/br>
Adapted from: Milano, Power System Modelling and Scripting
&lt;/figcaption>
&lt;/figure>
&lt;/center>
&lt;/br>
&lt;p>Because the time constants $T_b$ and $T_c$ of the IEEE Type DC1 exciter model are frequently small enough to be neglected, in DPSim a simplified model of this exciter which neglect these time constants is also implemented. The control diagram of this exciter is depicted in Fig. 2 and it is described by the following set of differential equations:
$$
T_R \frac{d}{dt} v_R(t) = v_h(t) - v_R(t)
$$
$$
T_a \frac{d}{dt} v_a(t) = - v_a(t) + K_a v_{in}(t)
$$
$$
T_f \frac{d}{dt} v_f(t) - K_f \frac{d}{dt} v_{ef}(t) = -v_f(t),
$$
$$
T_e \frac{d}{dt} v_{ef}(t) = v_a(t) - v_{ef}(t) (sat(t) + K_{ef})
$$
where $v_h$​ is the module of the machine&amp;rsquo;s terminal voltage, and $v_{in}$​ is the amplifier input signal, which is given by:
$$
v_{in}(t) = v_{ref} (t) - v_R(t) - v_f(t)
$$
The set of differential equations are discretized using forward euler in order to solve it numerically, which leads to the following set of algebraic equations:
$$
v_R(k + \Delta t) = v_R(k) + \frac{\Delta t}{T_R} ( v_h(k) - v_R(k) ),
$$
$$
v_{in}(k) = v_{ref}(k) - v_R(k) - v_f(k),
$$
$$
v_a(k + \Delta t) = v_a(k) + \frac{\Delta t}{T_a} ( v_{in}(k) K_a - v_a(k) ),
$$
$$
v_f(k + \Delta t) = (1 - \frac{\Delta t}{T_f}) v_f(k) + \frac{\Delta t K_f}{T_f T_{ef}} ( v_{a}(k) - (K_{ef} + sat(k)) v_{ef}(k) ),
$$
$$
v_{ef}(k + \Delta t) = v_{ef}(k) + \frac{\Delta t}{T_{ef}} ( v_{a}(k) - (sat(k) + K_{ef}) v_{ef}(k)),
$$
$$
sat(k) = A_{ef} e^{(B_{ef} | v_{ef}(k) | )}
$$&lt;/p>
&lt;p>Since the values of all variables for $t=k$ are known, $v_{ef}(k+1)$ can be easily calculated using the discretised equations, which is carried out in the &lt;code>preStep&lt;/code> function of the generator connected to each exciter.&lt;/p>
&lt;p>The initial values of all variables, which are used in the first simulation step, are calculated assuming that the simulation starts in the steady. This is equivalent to assume that all derivative are equal to zero, which leads to:
$$
v_R(k=0) = v_h(k=0),
$$
$$
v_f(k=0) = 0,
$$
$$
v_a(k=0) = K_{ef} v_{ef}(k=0) + A_{ef} e^{B_{ef} |v_{ef} (k=0)|} v_{ef}(k=0),
$$
$$
v_{in}(k=0) = \frac{v_a(k=0)}{K_a},
$$
$$
v_{ref}(t=0) = v_R(t=0) + v_{in}(t=0),
$$
where $v_h(k=0)$, $v_{ef}(k=0)$ are calculated using the power flow analysis and after the initialization of synchronous machines (see section initialization of SG).&lt;/p>
&lt;h2 id="static-exciter">Static Exciter&lt;/h2>
&lt;center>
&lt;figure margin=30%>
&lt;img src="./images/ExciterStatic.drawio.svg" alt="Exciter static">
&lt;figcaption>Fig. 3: Control diagram of the Static Exciter &lt;/br>
Adapted from [6]
&lt;/figcaption>
&lt;/figure>
&lt;/center>
The control diagram of this is depicted in Fig. 3. It can be observed as a simplified version of the DC1 type exciter which is composed only by the regulator, the amplifier and an optional transducer. To discretize the lead-lag compensator using forward euler it is better to split this block into two parallel blocks as depicted in Fig. 4.
&lt;center>
&lt;figure margin=20%>
&lt;img src="./images/ExciterStatic_split.drawio.svg" alt="Exciter static split">
&lt;figcaption>Fig. 4: Control diagram of the Static Exciter &lt;/br>
&lt;/figcaption>
&lt;/figure>
&lt;/center>
where:
&lt;p>$$
C_{a} = \frac{T_{a}}{T_{b}}, \quad C_{b} = \frac{T_{b}-T_{a}}{T_{b}}.
$$
and it is described by the following set of differential equations:
$$
T_{R} \frac{d}{dt} v_{r}(t) = v_{h}(t) - v_{r}(t)
$$
$$
T_{b} \frac{d}{dt} x_{b}(t) = v_{in}(t) - x_{b}(t)
$$
$$
T_{e} \frac{d}{dt} e_{fd}(t) = K_{a} v_{e}(t) - e_{fd}(t),
$$&lt;/p>
&lt;p>Then, the set of differential equations are discretized using forward euler in order to solve it numerically, which leads to the following set of algebraic equations:&lt;/p>
&lt;p>$$
v_r(k + \Delta t) = v_r(k) + \frac{\Delta t}{T_R} ( v_h(k) - v_r(k) ),
$$
$$
v_{in}(k) = v_{ref}(k) - v_{r}(k),
$$
$$
X_b(k + \Delta t) = \frac{\Delta t}{T_{b}} (v_{in}(k) - x_{b}(k)) + x_{b}(k),
$$
$$
v_e(k) = K_{a} (C_{b} x_{b}(k) + C_{a} v_{in} (k)) ,
$$
$$
e_{fd}(k + \Delta t) = \frac{\Delta t}{T_{e}}(v_{e}(k) - e_{fd}(k)) + e_{fd}(k).
$$&lt;/p>
&lt;p>To consider the saturation of $e_{fd}$ there are two different implementations, which is automatically selected depending of value of the parameter $K_{bc}$:&lt;/p>
&lt;p>&lt;strong>Standard ($K_{bc}=0$):&lt;/strong>&lt;/p>
&lt;p>$$
e^{&lt;em>}&lt;em>{fd} = e&lt;/em>{fd, max} \quad \quad if \quad \quad e^{&lt;/em>}&lt;em>{fd} &amp;gt; e&lt;/em>{fd, max} \
e^{&lt;em>}&lt;em>{fd} = e&lt;/em>{fd, min} \quad \quad if \quad \quad e^{&lt;/em>}&lt;em>{fd} &amp;lt; e&lt;/em>{fd, min},
$$&lt;/p>
&lt;p>where $e^{*}_{fd}$ represents the output of the exciter.&lt;/p>
&lt;p>&lt;strong>Anti-windup ($K_{bc}&amp;gt;0$):&lt;/strong> for controllers with an integral component, i.e. also for PID controllers, the so-called &amp;ldquo;windup effect&amp;rdquo; can occur when using the &lt;em>standard&lt;/em> saturation function. A strategy for limiting the anti-windup effect is shown in Fig. 5.&lt;/p>
&lt;center>
&lt;figure margin=20%>
&lt;img src="./images/ExciterStatic_windup.drawio.svg" alt="Exciter static split">
&lt;figcaption>Fig. 5: Control diagram of the Static Exciter with anti windup strategy &lt;/br>
&lt;/figcaption>
&lt;/figure>
&lt;/center>
&lt;p>which means that the input of the differential equation describing $e_{fd}$, $v_{e}$, takes now the following form:&lt;/p>
&lt;p>$$
v_{e} = C_{a} v_{in} + C_{b} x_{b} - K_{bc} (e_{fd} - e_{fd}^{*})
$$&lt;/p>
&lt;p>The initial values of all variables, which are used in the first simulation step, are calculated assuming that the simulation starts in the steady. This is equivalent to assume that all derivative are equal to zero, which leads to:&lt;/p>
&lt;p>$$
v_{r}(t=0) = v_{h}(t=0),
$$&lt;/p>
&lt;p>$$
v_{e}(t=0) = \frac{e_{fd}(t=0)}{K_{a}},
$$&lt;/p>
&lt;p>$$
v_{in}(t=0) = \frac{v_{e}(t=0)}{C_{a}+C_{b}},
$$&lt;/p>
&lt;p>$$
x_{b}(t=0) = v_{in}(t=0),
$$&lt;/p>
&lt;p>$$
v_{ref}(t=0) = v_{in}(t=0) + v_{r}(t=0)
$$&lt;/p>
&lt;h2 id="power-system-stabilizer-pss">Power System Stabilizer (PSS)&lt;/h2>
&lt;p>PSS is a controller of synchronous generators used to enhance damping of electromechanical oscillations. The PSS1A implemented in DPSim accepts three optional input signals: rotor speed $\omega$, active power $P$, and terminal voltage magnitude $V_h$. The combined input signal is:
$$
s(t) = K_w \omega(t) + K_p P(t) + K_v V_h(t)
$$
Setting $K_p = K_v = 0$ recovers the speed-only special case. The PSS output $v_{pss}$ at time $t=k$ is a signal used as the input of the AVR to calculate the field voltage at $t=k+\Delta t$, $v_{fd}(k+\Delta t)$. At present, only one PSS is implemented in DPSim which is a simplified version of the IEEE PSS1A type model.&lt;/p>
&lt;h3 id="ieee-pss1a-type-pss">IEEE PSS1A type PSS&lt;/h3>
&lt;center>
&lt;figure margin=30%>
&lt;img src="./images/PSS_Type1.png" alt="DC1_exciter">
&lt;figcaption>Fig. 6: Control diagram of the PSS Type 1 (speed input only; &lt;/br>
the implementation also accepts active power $K_p P$ and terminal voltage $K_v V_h$). &lt;/br>
Adapted from: Milano, Power System Modelling and Scripting
&lt;/figcaption>
&lt;/figure>
&lt;/center>
&lt;p>The control diagram of this PSS is depicted in Fig. 6. It includes a washout filter and two lead-lag blocks and is described by the following set of differential equations:
$$
T_w \frac{d}{dt} v_1(t) = -(s(t) + v_1(t)),
$$
$$
T_2 \frac{d}{dt} v_2(t) = (1 - \frac{T_1}{T_2})(s(t) + v_1(t)) - v_2(t),
$$
$$
T_4 \frac{d}{dt} v_3(t) = (1 - \frac{T_3}{T_4})\left(v_2(t) + \frac{T_1}{T_2}(s(t) + v_1(t))\right) - v_3(t),
$$
$$
v_{pss}(t) = v_3(t) + \frac{T_3}{T_4}\left(v_2(t) + \frac{T_1}{T_2}(s(t) + v_1(t))\right),
$$&lt;/p>
&lt;p>where $s(t) = K_w \omega(t) + K_p P(t) + K_v V_h(t)$ is the combined input signal and $v_{pss}(t)$ is the output signal used to modify the reference voltage of the AVR.&lt;/p>
&lt;p>The set of differential equations are discretized using forward euler in order to solve it numerically, which leads to the following set of algebraic equations:
$$
v_1(k + \Delta t) = v_1(k) - \frac{\Delta t}{T_w} (s(k) + v_1(k)),
$$
$$
v_2(k + \Delta t) = v_2(k) + \frac{\Delta t}{T_2} \left((1-\frac{T_1}{T_2})(s(k) + v_1(k)) - v_2(k)\right),
$$
$$
v_3(k + \Delta t) = v_3(k) + \frac{\Delta t}{T_4} \left((1-\frac{T_3}{T_4})\left(v_2(k) + \frac{T_1}{T_2}(s(k) + v_1(k))\right) - v_3(k)\right),
$$
$$
v_{pss}(k) = v_3(k) + \frac{T_3}{T_4} \left(v_2(k) + \frac{T_1}{T_2} (s(k) + v_1(k))\right)
$$&lt;/p>
&lt;p>Since the values of all variables for $t=k$ are known, $v_{pss}(k)$ can be easily calculated using the discretised equations, which is carried out in the &lt;code>preStep&lt;/code> function of the generator connected to each exciter. Then, $v_{pss}(k)$ is used as input of the AVR to calculate the field voltage at time $k+1$. The values $v_1(k+1)$, $v_2(k+1)$, $v_3(k+1)$ are stored and used to calculate the PSS output of the next time step.&lt;/p>
&lt;p>The initial values of all variables, which are used in the first simulation step, are calculated assuming that the simulation starts in steady state. This is equivalent to assuming that all derivatives are equal to zero, which leads to:
$$
v_1(k=0) = -s(k=0),
$$
$$
v_2(k=0) = (1 - \frac{T_1}{T_2})(s(k=0) + v_1(k=0)),
$$
$$
v_3(k=0) = (1 - \frac{T_3}{T_4})\left(v_2(k=0) + \frac{T_1}{T_2}(s(k=0) + v_1(k=0))\right),
$$
$$
v_{pss}(k=0) = v_3(k=0) + \frac{T_3}{T_4}\left(v_2(k=0) + \frac{T_1}{T_2}(s(k=0) + v_1(k=0))\right),
$$&lt;/p>
&lt;p>where $s(k=0) = K_w \omega(k=0) + K_p P(k=0) + K_v V_h(k=0)$ is evaluated after the power flow analysis and initialization of synchronous machines (see section initialization of SG). In steady state $\omega(k=0) = 1.0$ (pu), and if $K_p = K_v = 0$ then $v_2 = v_3 = v_{pss} = 0$.&lt;/p>
&lt;h2 id="turbine-governor-models">Turbine Governor Models&lt;/h2>
&lt;p>In DPsim there are two types of Turbine Governor implementations. The &lt;em>Turbine Governor Type 1&lt;/em> implements both the turbine and the governor in one component. In contrast, Steam Turbine and Steam Turbine Governor are implemented as two separate classes and their objects are created independently. Steam/Hydro Turbine and Steam/Hydro Turbine Governor are two blocks that must be connected in series.&lt;/p>
&lt;p>The input of the turbine governor models is the mechanical omega at time $t=k-\Delta t$ and the output is the mechanical power at time $t=k$. This variable is then used by the SG to predict the mechanical omega at time $t=k+\Delta t$.&lt;/p>
&lt;h3 id="turbine-governor-type-1">Turbine Governor Type 1&lt;/h3>
&lt;center>
&lt;figure margin=30%>
&lt;img src="./images/TG_Type1.png" alt="TG_Type1_governor">
&lt;figcaption>&lt;/br>Fig. 7: Control diagram of the turbine governor type 1 &lt;/br>
Source: Milano, Power System Modelling and Scripting
&lt;/figcaption>
&lt;/figure>
&lt;/center>
&lt;/br>
&lt;p>This model includes a governor, a servo and a reheat block. The control diagram of this governor is depicted in Fig. 7 and it is described by the following set of differential equations:
$$
p_{in}(t) = p_{ref} + \frac{1}{R} (\omega_{ref} - \omega(t)),
$$
$$
T_s \frac{d}{dt} x_{g1}(t) = p_{in}(t) - x_{g1}(t),
$$
$$
T_c \frac{d}{dt} x_{g2}(t) = \left(1 - \frac{T_3}{T_c}\right) x_{g1}(t) - x_{g2}(t),
$$
$$
T_5 \frac{d}{dt} x_{g3}(t) = \left(1 - \frac{T_4}{T_5}\right) \left(x_{g2}(t) + \frac{T_3}{T_c} x_{g1}(t)\right) - x_{g3}(t),
$$
$$
\tau_m(t) = x_{g3}(t) + \frac{T_4}{T_5} \left(x_{g2}(t) + \frac{T_3}{T_c} x_{g1}(t)\right),
$$
where $\omega(t)$ is the input signal and $\tau_m(t)$ is the output signal of the governor.&lt;/p>
&lt;p>The differential equations are discretized using the forward Euler method, which leads to the following set of algebraic equations:
$$
p_{in}(k-\Delta t) = p_{ref} + \frac{1}{R} (\omega_{ref} - \omega(k-\Delta t)),
$$
$$
x_{g1}(k) = x_{g1}(k-\Delta t) + \frac{\Delta t}{T_s} \left(p_{in}(k-\Delta t) - x_{g1}(k-\Delta t)\right),
$$
$$
x_{g2}(k) = x_{g2}(k-\Delta t) + \frac{\Delta t}{T_c} \left(\left(1 - \frac{T_3}{T_c}\right) x_{g1}(k-\Delta t) - x_{g2}(k-\Delta t)\right),
$$
$$
x_{g3}(k) = x_{g3}(k-\Delta t) + \frac{\Delta t}{T_5} \left(\left(1 - \frac{T_4}{T_5}\right) \left(x_{g2}(k-\Delta t) + \frac{T_3}{T_c} x_{g1}(k-\Delta t)\right) - x_{g3}(k-\Delta t)\right),
$$
$$
\tau_m(k) = x_{g3}(k) + \frac{T_4}{T_5} \left(x_{g2}(k) + \frac{T_3}{T_c} x_{g1}(k)\right).
$$
Since all variables at $t=k-\Delta t$ are known, $\tau_m(k)$ is computed in the &lt;code>preStep&lt;/code> of the generator and used to approximate the mechanical equations at time $k+\Delta t$.&lt;/p></description></item><item><title>Docs: Adding Dynamics</title><link>https://sogno.energy/dpsim/docs/tutorials/python/adding-dynamics/</link><pubDate>Fri, 31 Jul 2026 00:00:00 +0000</pubDate><guid>https://sogno.energy/dpsim/docs/tutorials/python/adding-dynamics/</guid><description>
&lt;p>The circuit in &lt;a href="https://sogno.energy/dpsim/docs/tutorials/python/first-simulation/">your first simulation&lt;/a> reaches its final value in
a single step. A resistor is a purely algebraic element: it stores no energy, so the circuit has no
state variable and its response to a change is instantaneous.&lt;/p>
&lt;p>An inductor stores energy in its magnetic field, and its current cannot change instantaneously. That
current becomes a state variable, the circuit becomes first order, and it acquires a transient worth
looking at and a reason to care about the time step.&lt;/p>
&lt;p>&lt;img src="circuit.svg" alt="A source, a resistor and an inductor in series.">&lt;/p>
&lt;h2 id="the-circuit">The circuit&lt;/h2>
&lt;p>A source, a resistor and an inductor in series. Only the inductor is new; everything else is the
same shape as before.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-python" data-lang="python">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#204a87;font-weight:bold">import&lt;/span> &lt;span style="color:#000">dpsimpy&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#204a87;font-weight:bold">import&lt;/span> &lt;span style="color:#000">villas.dataprocessing.readtools&lt;/span> &lt;span style="color:#204a87;font-weight:bold">as&lt;/span> &lt;span style="color:#000">rt&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#000">name&lt;/span> &lt;span style="color:#ce5c00;font-weight:bold">=&lt;/span> &lt;span style="color:#4e9a06">&amp;#34;rl_circuit&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#000">gnd&lt;/span> &lt;span style="color:#ce5c00;font-weight:bold">=&lt;/span> &lt;span style="color:#000">dpsimpy&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">dp&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">SimNode&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">gnd&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#000">n1&lt;/span> &lt;span style="color:#ce5c00;font-weight:bold">=&lt;/span> &lt;span style="color:#000">dpsimpy&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">dp&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">SimNode&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#4e9a06">&amp;#34;n1&amp;#34;&lt;/span>&lt;span style="color:#000;font-weight:bold">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#000">n2&lt;/span> &lt;span style="color:#ce5c00;font-weight:bold">=&lt;/span> &lt;span style="color:#000">dpsimpy&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">dp&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">SimNode&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#4e9a06">&amp;#34;n2&amp;#34;&lt;/span>&lt;span style="color:#000;font-weight:bold">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#000">src&lt;/span> &lt;span style="color:#ce5c00;font-weight:bold">=&lt;/span> &lt;span style="color:#000">dpsimpy&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">dp&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">ph1&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">VoltageSource&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#4e9a06">&amp;#34;src&amp;#34;&lt;/span>&lt;span style="color:#000;font-weight:bold">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#000">src&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">V_ref&lt;/span> &lt;span style="color:#ce5c00;font-weight:bold">=&lt;/span> &lt;span style="color:#204a87">complex&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#0000cf;font-weight:bold">100&lt;/span>&lt;span style="color:#000;font-weight:bold">,&lt;/span> &lt;span style="color:#0000cf;font-weight:bold">0&lt;/span>&lt;span style="color:#000;font-weight:bold">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#000">r&lt;/span> &lt;span style="color:#ce5c00;font-weight:bold">=&lt;/span> &lt;span style="color:#000">dpsimpy&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">dp&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">ph1&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">Resistor&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#4e9a06">&amp;#34;r&amp;#34;&lt;/span>&lt;span style="color:#000;font-weight:bold">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#000">r&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">R&lt;/span> &lt;span style="color:#ce5c00;font-weight:bold">=&lt;/span> &lt;span style="color:#0000cf;font-weight:bold">10.0&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#000">l&lt;/span> &lt;span style="color:#ce5c00;font-weight:bold">=&lt;/span> &lt;span style="color:#000">dpsimpy&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">dp&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">ph1&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">Inductor&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#4e9a06">&amp;#34;l&amp;#34;&lt;/span>&lt;span style="color:#000;font-weight:bold">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#000">l&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">L&lt;/span> &lt;span style="color:#ce5c00;font-weight:bold">=&lt;/span> &lt;span style="color:#0000cf;font-weight:bold">0.05&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#000">src&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">connect&lt;/span>&lt;span style="color:#000;font-weight:bold">([&lt;/span>&lt;span style="color:#000">gnd&lt;/span>&lt;span style="color:#000;font-weight:bold">,&lt;/span> &lt;span style="color:#000">n1&lt;/span>&lt;span style="color:#000;font-weight:bold">])&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#000">r&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">connect&lt;/span>&lt;span style="color:#000;font-weight:bold">([&lt;/span>&lt;span style="color:#000">n1&lt;/span>&lt;span style="color:#000;font-weight:bold">,&lt;/span> &lt;span style="color:#000">n2&lt;/span>&lt;span style="color:#000;font-weight:bold">])&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#000">l&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">connect&lt;/span>&lt;span style="color:#000;font-weight:bold">([&lt;/span>&lt;span style="color:#000">n2&lt;/span>&lt;span style="color:#000;font-weight:bold">,&lt;/span> &lt;span style="color:#000">gnd&lt;/span>&lt;span style="color:#000;font-weight:bold">])&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#000">system&lt;/span> &lt;span style="color:#ce5c00;font-weight:bold">=&lt;/span> &lt;span style="color:#000">dpsimpy&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">SystemTopology&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#0000cf;font-weight:bold">50&lt;/span>&lt;span style="color:#000;font-weight:bold">,&lt;/span> &lt;span style="color:#000;font-weight:bold">[&lt;/span>&lt;span style="color:#000">gnd&lt;/span>&lt;span style="color:#000;font-weight:bold">,&lt;/span> &lt;span style="color:#000">n1&lt;/span>&lt;span style="color:#000;font-weight:bold">,&lt;/span> &lt;span style="color:#000">n2&lt;/span>&lt;span style="color:#000;font-weight:bold">],&lt;/span> &lt;span style="color:#000;font-weight:bold">[&lt;/span>&lt;span style="color:#000">src&lt;/span>&lt;span style="color:#000;font-weight:bold">,&lt;/span> &lt;span style="color:#000">r&lt;/span>&lt;span style="color:#000;font-weight:bold">,&lt;/span> &lt;span style="color:#000">l&lt;/span>&lt;span style="color:#000;font-weight:bold">])&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#000">logger&lt;/span> &lt;span style="color:#ce5c00;font-weight:bold">=&lt;/span> &lt;span style="color:#000">dpsimpy&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">Logger&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#000">name&lt;/span>&lt;span style="color:#000;font-weight:bold">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#000">logger&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">log_attribute&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#4e9a06">&amp;#34;i_l&amp;#34;&lt;/span>&lt;span style="color:#000;font-weight:bold">,&lt;/span> &lt;span style="color:#4e9a06">&amp;#34;i_intf&amp;#34;&lt;/span>&lt;span style="color:#000;font-weight:bold">,&lt;/span> &lt;span style="color:#000">l&lt;/span>&lt;span style="color:#000;font-weight:bold">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#000">sim&lt;/span> &lt;span style="color:#ce5c00;font-weight:bold">=&lt;/span> &lt;span style="color:#000">dpsimpy&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">Simulation&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#000">name&lt;/span>&lt;span style="color:#000;font-weight:bold">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#000">sim&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">set_domain&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#000">dpsimpy&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">Domain&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">DP&lt;/span>&lt;span style="color:#000;font-weight:bold">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#000">sim&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">set_system&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#000">system&lt;/span>&lt;span style="color:#000;font-weight:bold">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#000">sim&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">set_time_step&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#0000cf;font-weight:bold">1e-4&lt;/span>&lt;span style="color:#000;font-weight:bold">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#000">sim&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">set_final_time&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#0000cf;font-weight:bold">0.05&lt;/span>&lt;span style="color:#000;font-weight:bold">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#000">sim&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">add_logger&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#000">logger&lt;/span>&lt;span style="color:#000;font-weight:bold">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#000">sim&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">run&lt;/span>&lt;span style="color:#000;font-weight:bold">()&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#000">current&lt;/span> &lt;span style="color:#ce5c00;font-weight:bold">=&lt;/span> &lt;span style="color:#000">rt&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">read_timeseries_dpsim&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#4e9a06">&amp;#34;logs/&amp;#34;&lt;/span> &lt;span style="color:#ce5c00;font-weight:bold">+&lt;/span> &lt;span style="color:#000">name&lt;/span> &lt;span style="color:#ce5c00;font-weight:bold">+&lt;/span> &lt;span style="color:#4e9a06">&amp;#34;.csv&amp;#34;&lt;/span>&lt;span style="color:#000;font-weight:bold">)[&lt;/span>&lt;span style="color:#4e9a06">&amp;#34;i_l&amp;#34;&lt;/span>&lt;span style="color:#000;font-weight:bold">]&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>A second node appears because the resistor and the inductor meet somewhere, and that junction is a
node like any other. Components connect to nodes, never directly to each other, so a series chain of
two elements always needs the node between them.&lt;/p>
&lt;h2 id="what-to-expect-before-running-it">What to expect before running it&lt;/h2>
&lt;p>Two numbers are worth working out first, because they are what the result should be checked against.&lt;/p>
&lt;p>The steady-state current follows from the impedance at the system frequency,&lt;/p>
&lt;div class="math">$$|I| = \frac{|V|}{\sqrt{R^2 + (\omega L)^2}}
= \frac{100}{\sqrt{10^2 + (2\pi \cdot 50 \cdot 0.05)^2}}
= \frac{100}{18.62} = 5.37 \ \mathrm{A},$$&lt;/div>&lt;p>and the transient decays with the time constant $\tau = L/R = 5$ ms, so the circuit settles after
roughly five of those, about 25 ms. The simulation runs for 50 ms, comfortably past that.&lt;/p>
&lt;p>Getting 5.37 A at the end is the check that the circuit was built as intended. A wrong connection
order or a missing component usually shows up here rather than as an error.&lt;/p>
&lt;h2 id="why-the-time-step-matters-now">Why the time step matters now&lt;/h2>
&lt;p>Run the same circuit twice, once at 0.1 ms and once at 5 ms, and compare the inductor current:&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Time&lt;/th>
&lt;th>0.1 ms step&lt;/th>
&lt;th>5 ms step&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>5 ms&lt;/td>
&lt;td>5.699 A&lt;/td>
&lt;td>2.953 A&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>10 ms&lt;/td>
&lt;td>6.105 A&lt;/td>
&lt;td>6.256 A&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>20 ms&lt;/td>
&lt;td>5.271 A&lt;/td>
&lt;td>5.276 A&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>50 ms&lt;/td>
&lt;td>5.371 A&lt;/td>
&lt;td>5.366 A&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>At 5 ms the two disagree by nearly a factor of two. By 20 ms they agree to better than a tenth of a
percent, and both end at the right steady-state value.&lt;/p>
&lt;div class="alert alert-warning" role="alert">
&lt;h4 class="alert-heading">Watch out: a bad step size hides in the final value&lt;/h4>
That pattern is the whole point. The coarse run is not uniformly wrong; it is wrong &lt;strong>during the
transient&lt;/strong> and right afterwards. A step size equal to the time constant cannot resolve a change
that happens over one time constant, but it has no trouble with a value that is no longer changing.
Checking a simulation only at its final value will therefore not detect a step size that is far too
large.
&lt;/div>
&lt;p>The rule that follows: choose the step against the fastest thing you need to see, not against the
duration of the run or the value you expect at the end. Here the fastest thing is $\tau = 5$ ms, and
0.1 ms resolves it with room to spare.&lt;/p>
&lt;h2 id="what-the-values-mean-in-this-domain">What the values mean in this domain&lt;/h2>
&lt;p>The current is complex, and &lt;code>abs()&lt;/code> gives the magnitude of the envelope rather than an instantaneous
current. In this domain the 50 Hz oscillation is not in the numbers at all: it has been moved into
the carrier and handled analytically, which is why a 0.1 ms step is generous here and would be
merely adequate for the same circuit solved as a waveform.&lt;/p>
&lt;p>That difference is the subject of a later step. For now it is enough to know that a flat line in a
dynamic phasor result means a steady sinusoid, not a constant.&lt;/p>
&lt;h2 id="the-script">The script&lt;/h2>
&lt;p>The complete script for this page is &lt;a href="https://github.com/sogno-platform/dpsim/blob/master/examples/Python/Tutorials/02_adding_dynamics.py">&lt;code>02_adding_dynamics.py&lt;/code>&lt;/a> under &lt;code>examples/Python/Tutorials&lt;/code>. The numbers quoted above are the numbers it prints, so if the two ever disagree the page is the one that is wrong.&lt;/p>
&lt;h2 id="next">Next&lt;/h2>
&lt;p>The circuit still has one source and one branch. Next is a network with a line between two buses,
where the state the simulation starts from stops being obvious.&lt;/p>
&lt;p>The elements used here are derived under
&lt;a href="https://sogno.energy/dpsim/docs/concepts/models/rlc-elements/">RLC elements&lt;/a>, and the trapezoidal companion
models behind them under &lt;a href="https://sogno.energy/dpsim/docs/concepts/nodal-analysis/">nodal analysis&lt;/a>.&lt;/p></description></item><item><title>Docs: Branches</title><link>https://sogno.energy/dpsim/docs/concepts/models/branches/</link><pubDate>Fri, 31 Jul 2026 00:00:00 +0000</pubDate><guid>https://sogno.energy/dpsim/docs/concepts/models/branches/</guid><description>
&lt;p>Both line models below are composite components: they do not stamp the system matrix directly
but are built from resistor, inductor and capacitor subcomponents, each of which contributes its
own stamp. See &lt;a href="https://sogno.energy/dpsim/docs/developer-guide/writing-a-model/subcomponents/">subcomponents&lt;/a> for how that
composition works, and &lt;a href="https://sogno.energy/dpsim/docs/concepts/models/rlc-elements/">RLC elements&lt;/a> for the stamps of the
individual elements.&lt;/p>
&lt;p>The transformer is documented separately under &lt;a href="https://sogno.energy/dpsim/docs/concepts/models/transformer/">transformer&lt;/a>.&lt;/p>
&lt;h2 id="rx-line">RX-Line&lt;/h2>
&lt;p>The RX line represents a line by its series resistance and series inductance only, ignoring the
shunt admittance. It is the appropriate choice for short lines, where the charging current is
negligible, and it is what the CIM reader produces for an &lt;code>ACLineSegment&lt;/code> when no shunt data is
present.&lt;/p>
&lt;p>The model is composed of a series resistor and a series inductor between the two terminals:&lt;/p>
&lt;div class="math">$$\underline{Z} = R + j \omega L$$&lt;/div>&lt;p>An additional resistor from the inductor terminal to ground is present to make initialisation
well posed. It is not part of the physical model.&lt;/p>
&lt;p>&lt;code>RxLine&lt;/code> exists in &lt;code>DP::Ph1&lt;/code>, &lt;code>EMT::Ph3&lt;/code>, &lt;code>SP::Ph1&lt;/code> and &lt;code>SP::Ph3&lt;/code>.&lt;/p>
&lt;h2 id="pi-line">PI-Line&lt;/h2>
&lt;p>The PI line adds the shunt admittance of the line, split evenly between the two terminals, which
matters once the line is long enough for the charging current to affect the result. The name
comes from the shape of the equivalent circuit: a series branch with one shunt branch at each
end.&lt;/p>
&lt;p>The series branch carries the resistance and inductance as above. Each terminal additionally
carries half of the total shunt capacitance and half of the total shunt conductance:&lt;/p>
&lt;div class="math">$$\underline{Y}_{shunt} = \frac{G + j \omega C}{2}$$&lt;/div>&lt;p>The shunt capacitance and conductance are specified as totals for the line, and the halving between
the two ends is part of the model rather than something the user does.&lt;/p>
&lt;h2 id="decoupling-line">Decoupling Line&lt;/h2>
&lt;p>The decoupling line is a distributed parameter line based on the Bergeron travelling wave
method. Unlike the two models above it is not primarily a fidelity improvement: its purpose is
to remove the direct coupling between the two terminals so that the network on either side can
be solved as an independent system, which is what makes splitting a network across solvers or
across simulators possible.&lt;/p>
&lt;p>The method rests on the behaviour of a lossless line. For a line with distributed inductance and
capacitance, the quantity $v + Z_c, i$ observed at one end reappears unchanged at the other end
one travel time later, and likewise in the opposite direction. Nothing propagates faster than
that travel time, so the two ends cannot influence each other within it. The surge impedance and
the travel time follow from the line&amp;rsquo;s total inductance and capacitance,&lt;/p>
&lt;div class="math">$$Z_c = \sqrt{\frac{L}{C}}, \qquad \tau = \sqrt{L C}.$$&lt;/div>&lt;p>Each terminal is then represented by a resistance to ground in parallel with a current source.
The resistance is $Z_c + R/4$, and the current source carries the history term, whose value
depends on the voltage and current recorded at the &lt;em>other&lt;/em> terminal one travel time ago. Because
that value is already known when the step begins, it enters the system as a constant injection
rather than as a coupling into the admittance matrix, and the matrix separates into two blocks
that can be factorised and solved independently.&lt;/p>
&lt;p>The series resistance is not distributed along the line. It is lumped, with $R/4$ placed at each
end and the remainder in the middle of the equivalent, which is why the terminating resistance
and the history coefficients carry $R/4$ terms rather than the full $R$.&lt;/p>
&lt;p>The travel time is not required to be a whole number of time steps. The recorded quantities are
held in a buffer of $\lceil \tau / \Delta t \rceil$ samples and the value one travel time ago is
recovered by linear interpolation between the two nearest entries. The one hard requirement is
that the travel time exceed the time step; a line whose $\tau$ is shorter than $\Delta t$ cannot
decouple anything, and setting one up is rejected rather than silently approximated.&lt;/p>
&lt;p>In the dynamic phasor domain the history terms carry an additional rotation $e^{-j \omega_s
\tau}$. This is a direct consequence of working with envelopes: a delay of $\tau$ applied to an
instantaneous waveform becomes, for the envelope, the same delay together with a phase rotation
of the carrier over that interval, as described under
&lt;a href="https://sogno.energy/dpsim/docs/concepts/dyn-phasors/">dynamic phasors&lt;/a>. Note that this rotation is
currently evaluated at a fixed 50 Hz rather than at the system frequency in use.&lt;/p>
&lt;p>The decoupling is exact for the lossless travelling wave line it is derived from. The error
introduced in practice comes from the lumped treatment of the series resistance and from the
interpolation of the delayed quantities, and it grows as the time step approaches the travel
time.&lt;/p>
&lt;h2 id="choosing-between-them">Choosing between them&lt;/h2>
&lt;p>Use the RX line when the shunt admittance can be neglected and you want the smaller system
matrix, since the PI line introduces additional nodes for its shunt branches. Use the PI line
when the line is long enough that its charging current matters, or when you are comparing
against a reference tool that models the shunt branch.&lt;/p>
&lt;p>Both are lumped parameter models and therefore do not reproduce travelling wave behaviour. Use
the decoupling line when you need that behaviour, or when the reason for reaching for a line
model is to split the network in the first place. For the domains each model is available in,
see &lt;a href="https://sogno.energy/dpsim/docs/reference/model-availability/">model availability&lt;/a>.&lt;/p></description></item><item><title>Docs: Build</title><link>https://sogno.energy/dpsim/docs/developer-guide/architecture-and-conventions/build/</link><pubDate>Fri, 31 Jul 2026 00:00:00 +0000</pubDate><guid>https://sogno.energy/dpsim/docs/developer-guide/architecture-and-conventions/build/</guid><description>
&lt;p>All builds start from a checkout of the repository. To build and read the code, cloning over
HTTPS needs no account:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-shell" data-lang="shell">&lt;span style="display:flex;">&lt;span>git clone https://github.com/sogno-platform/dpsim.git
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#204a87">cd&lt;/span> dpsim
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>If you intend to contribute, clone your own fork over SSH instead, since contributions are
accepted from forks only and pushing needs an authenticated remote:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-shell" data-lang="shell">&lt;span style="display:flex;">&lt;span>git clone git@github.com:&amp;lt;your-user&amp;gt;/dpsim.git
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#204a87">cd&lt;/span> dpsim
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>git remote add upstream https://github.com/sogno-platform/dpsim.git
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>The container route below is the most reproducible, because the image already carries every
dependency at the version CI uses. The native routes need those dependencies installed by hand.&lt;/p>
&lt;h2 id="container-based">Container based&lt;/h2>
&lt;p>The commands below use &lt;code>docker&lt;/code>, but the images are ordinary OCI images, so &lt;code>podman&lt;/code> works as a
drop-in replacement throughout. On Fedora and Rocky, &lt;code>podman&lt;/code> is usually the one already
installed. Substitute &lt;code>podman&lt;/code> for &lt;code>docker&lt;/code> in every command if you prefer it.&lt;/p>
&lt;p>The repository ships a development image with all required dependencies:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-shell" data-lang="shell">&lt;span style="display:flex;">&lt;span>docker build -t sogno/dpsim:dev -f packaging/Docker/Dockerfile.dev .
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Alternatively, pull the prebuilt image instead of building it:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-shell" data-lang="shell">&lt;span style="display:flex;">&lt;span>docker pull sogno/dpsim:dev
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Then start an interactive session with the working copy mounted into the container:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-shell" data-lang="shell">&lt;span style="display:flex;">&lt;span>docker run -it -p 8888:8888 -v &lt;span style="color:#204a87;font-weight:bold">$(&lt;/span>&lt;span style="color:#204a87">pwd&lt;/span>&lt;span style="color:#204a87;font-weight:bold">)&lt;/span>:/dpsim --privileged sogno/dpsim:dev bash
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>The &lt;code>-p&lt;/code> option maps port 8888 so a JupyterLab instance inside the container is reachable from
the host. The &lt;code>--privileged&lt;/code> option is required for debug builds. On Windows, the current
directory is spelled differently:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-shell" data-lang="shell">&lt;span style="display:flex;">&lt;span>docker run -it -p 8888:8888 -v &lt;span style="color:#4e9a06">${&lt;/span>&lt;span style="color:#000">pwd&lt;/span>&lt;span style="color:#4e9a06">}&lt;/span>:/dpsim --privileged sogno/dpsim:dev bash
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Inside the container, the C++ and Python libraries build as follows:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-shell" data-lang="shell">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#204a87">cd&lt;/span> /dpsim
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>mkdir build &lt;span style="color:#ce5c00;font-weight:bold">&amp;amp;&amp;amp;&lt;/span> &lt;span style="color:#204a87">cd&lt;/span> build
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>cmake ..
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>cmake --build . --target dpsimpy
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Targets that are not built by default have to be named explicitly, for example:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-shell" data-lang="shell">&lt;span style="display:flex;">&lt;span>cmake --build . --target dpsimpy dpsimpyvillas
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>To build everything:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-shell" data-lang="shell">&lt;span style="display:flex;">&lt;span>cmake --build .
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Optional features are enabled through the CMake options defined in the CMakeLists.txt files,
for example:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-shell" data-lang="shell">&lt;span style="display:flex;">&lt;span>cmake .. -DWITH_GSL&lt;span style="color:#ce5c00;font-weight:bold">=&lt;/span>ON
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>To use the freshly built Python package without installing it, put both the compiled extension
and the pure Python package on the path:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-shell" data-lang="shell">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#204a87">cd&lt;/span> /dpsim/build
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#204a87">export&lt;/span> &lt;span style="color:#000">PYTHONPATH&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">=&lt;/span>&lt;span style="color:#204a87;font-weight:bold">$(&lt;/span>&lt;span style="color:#204a87">pwd&lt;/span>&lt;span style="color:#204a87;font-weight:bold">)&lt;/span>:&lt;span style="color:#204a87;font-weight:bold">$(&lt;/span>&lt;span style="color:#204a87">pwd&lt;/span>&lt;span style="color:#204a87;font-weight:bold">)&lt;/span>/../python/src
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>This is the setup most contributors work with, since it picks up a rebuild immediately without
any reinstall step.&lt;/p>
&lt;p>Do not use &lt;code>pip install -e .&lt;/code> for this. An editable install only links the pure Python sources;
&lt;code>dpsimpy&lt;/code> is a compiled extension, so edits to the C++ are not picked up and you keep running
whatever binary was built at install time. The failure is silent, since the import still
succeeds and simply gives you stale behaviour. Either rebuild and rely on &lt;code>PYTHONPATH&lt;/code> as above,
or reinstall the package after every C++ change.&lt;/p>
&lt;p>To summarise the three ways to get DPsim, in increasing order of involvement: &lt;code>pip install dpsim&lt;/code>
for a released Linux wheel, a native build plus &lt;code>PYTHONPATH&lt;/code> for development, and &lt;code>make install&lt;/code>
to place a build system wide.&lt;/p>
&lt;p>If you develop inside a conda environment, the equivalent is to register the same two
directories from within the active environment. This needs &lt;code>conda-build&lt;/code> installed:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-shell" data-lang="shell">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#204a87">cd&lt;/span> /dpsim/build
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>conda develop &lt;span style="color:#204a87;font-weight:bold">$(&lt;/span>&lt;span style="color:#204a87">pwd&lt;/span>&lt;span style="color:#204a87;font-weight:bold">)&lt;/span> &lt;span style="color:#ce5c00;font-weight:bold">&amp;amp;&amp;amp;&lt;/span> conda develop &lt;span style="color:#204a87;font-weight:bold">$(&lt;/span>&lt;span style="color:#204a87">pwd&lt;/span>&lt;span style="color:#204a87;font-weight:bold">)&lt;/span>/../python/src
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Note that this writes into the environment, so it becomes specific to your setup.&lt;/p>
&lt;p>To run JupyterLab against it:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-shell" data-lang="shell">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#204a87">cd&lt;/span> /dpsim
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>jupyter lab --ip&lt;span style="color:#ce5c00;font-weight:bold">=&lt;/span>&lt;span style="color:#4e9a06">&amp;#34;0.0.0.0&amp;#34;&lt;/span> --allow-root --no-browser
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>To install DPsim system wide instead:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-shell" data-lang="shell">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#204a87">cd&lt;/span> /dpsim/build
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>sudo make install
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="cmake-for-linux">CMake for Linux&lt;/h2>
&lt;p>The authoritative dependency list is whatever the Dockerfiles install, since that is what CI
builds against. See &lt;code>packaging/Docker/Dockerfile.dev&lt;/code> for the Fedora set, and
&lt;a href="https://github.com/sogno-platform/dpsim/blob/master/packaging/Shell/install-fedora-deps.sh">install-fedora-deps.sh&lt;/a>
or &lt;a href="https://github.com/sogno-platform/dpsim/blob/master/packaging/Shell/install-ubuntu-deps.sh">install-ubuntu-deps.sh&lt;/a>
for scripts that install them.&lt;/p>
&lt;p>Both &lt;code>libcimpp&lt;/code> and &lt;code>villas-node&lt;/code> are optional. Neither needs to be built from source, though
the images do not yet take the same route for both.&lt;/p>
&lt;p>libcimpp publishes prebuilt &lt;code>.deb&lt;/code> and &lt;code>.rpm&lt;/code> packages per CIM version as release assets. The
Fedora and Debian images install those directly, while the Rocky image still builds it from
source:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-shell" data-lang="shell">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#8f5902;font-style:italic"># Pick the package matching your distribution and the CIM version you need.&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>wget https://github.com/sogno-platform/libcimpp/releases/download/release%2Fv2.2.0/libcimpp_CGMES_2.4.15_16FEB2016-2.2.0-Linux.deb
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>sudo apt-get install -y ./libcimpp_CGMES_2.4.15_16FEB2016-2.2.0-Linux.deb
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>sudo ldconfig
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>VILLASnode is served from the package repositories at &lt;a href="https://packages.fein-aachen.org">https://packages.fein-aachen.org&lt;/a>, which
carry both &lt;code>debian/&lt;/code> and &lt;code>redhat/&lt;/code>. Note that the images currently still build it from source,
pinned to a specific commit, so the packaged version is the more convenient route for a local
build but is not what CI exercises.&lt;/p>
&lt;p>Building either from source remains supported, and the deps scripts above do that, which is what
you want when you need a specific commit rather than a release.&lt;/p>
&lt;p>Sundials is only needed for the DAE solver. If your distribution does not package it, the
version CI uses is:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-shell" data-lang="shell">&lt;span style="display:flex;">&lt;span>git clone --branch v3.2.1 --recurse-submodules --depth &lt;span style="color:#0000cf;font-weight:bold">1&lt;/span> https://github.com/LLNL/sundials.git
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>mkdir -p sundials/build &lt;span style="color:#ce5c00;font-weight:bold">&amp;amp;&amp;amp;&lt;/span> &lt;span style="color:#204a87">cd&lt;/span> sundials/build
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>cmake .. -DCMAKE_BUILD_TYPE&lt;span style="color:#ce5c00;font-weight:bold">=&lt;/span>Release
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>make -j&lt;span style="color:#204a87;font-weight:bold">$(&lt;/span>nproc&lt;span style="color:#204a87;font-weight:bold">)&lt;/span> install
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Cloning, building and installing then work exactly as in the container section above.&lt;/p>
&lt;h2 id="cmake-for-windows">CMake for Windows&lt;/h2>
&lt;p>Windows is built in CI on &lt;code>windows-latest&lt;/code>, so the recipe below mirrors what
&lt;code>.github/workflows/build_test_windows.yaml&lt;/code> runs. You need Visual Studio with the C++ desktop
development workload, &lt;a href="https://cmake.org/">CMake&lt;/a> and
&lt;a href="https://git-scm.com/download/win">Git for Windows&lt;/a>. For Python support, install Python 3 and
add it to your PATH. Let CMake pick the default generator rather than naming a Visual Studio
version, so the build follows whichever Visual Studio you have.&lt;/p>
&lt;p>For the C++ libraries only:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-shell" data-lang="shell">&lt;span style="display:flex;">&lt;span>mkdir build
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#204a87">cd&lt;/span> build
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>cmake -DWITH_PYBIND&lt;span style="color:#ce5c00;font-weight:bold">=&lt;/span>OFF ..
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>cmake --build . --target dpsim --target dpsim-models --parallel
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>For the Python bindings, install pybind11 first:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-shell" data-lang="shell">&lt;span style="display:flex;">&lt;span>pip install pybind11&lt;span style="color:#ce5c00;font-weight:bold">[&lt;/span>global&lt;span style="color:#ce5c00;font-weight:bold">]&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>mkdir build
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#204a87">cd&lt;/span> build
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>cmake -DWITH_PYBIND&lt;span style="color:#ce5c00;font-weight:bold">=&lt;/span>ON ..
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>cmake --build . --target dpsimpy --parallel
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>If CMake rejects the spdlog dependency because of its minimum policy version, add
&lt;code>-DCMAKE_POLICY_VERSION_MINIMUM=3.5&lt;/code>, which is what CI currently does as a workaround.&lt;/p>
&lt;p>The &lt;code>dpsim-villas&lt;/code> library is not available on Windows, since it requires VILLASnode, which does
not build there. &lt;code>WITH_VILLAS&lt;/code> therefore stays off and the &lt;code>dpsimpyvillas&lt;/code> target does not exist,
so co-simulation examples cannot be built on Windows. The CIM reader is likewise not part of the
CI Windows build, as libcimpp is not installed there.&lt;/p>
&lt;h2 id="cmake-for-macos">CMake for macOS&lt;/h2>
&lt;p>macOS is not covered by CI, so treat this as a starting point rather than a supported path.
Install the dependencies with Homebrew:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-shell" data-lang="shell">&lt;span style="display:flex;">&lt;span>/bin/bash -c &lt;span style="color:#4e9a06">&amp;#34;&lt;/span>&lt;span style="color:#204a87;font-weight:bold">$(&lt;/span>curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh&lt;span style="color:#204a87;font-weight:bold">)&lt;/span>&lt;span style="color:#4e9a06">&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>brew install gcc git cmake graphviz python3 gsl eigen spdlog
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Then build as in the container section. Building on Apple Silicon is known to fail while building
libcimpp, see &lt;a href="https://github.com/sogno-platform/dpsim/issues/609">issue #609&lt;/a>. Configure with
&lt;code>-DWITH_CIM=OFF&lt;/code> if you do not need the CIM reader.&lt;/p>
&lt;h2 id="python-package">Python package&lt;/h2>
&lt;p>Wheels are produced by cibuildwheel in the &lt;code>publish_to_pypi&lt;/code> workflow, currently for
manylinux x86_64 and CPython 3.9 through 3.13. To build a source distribution locally:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-shell" data-lang="shell">&lt;span style="display:flex;">&lt;span>python3 -m build --sdist
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="nix">Nix&lt;/h3>
&lt;p>DPsim can be built using &lt;a href="https://nixos.org/">Nix&lt;/a>, a declarative package manager for
reproducible builds. The following steps require a working single-user or multi-user
installation of Nix, but not necessarily NixOS.&lt;/p>
&lt;p>DPsim uses the Flakes feature, which has to be enabled:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-shell" data-lang="shell">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#204a87">echo&lt;/span> &lt;span style="color:#4e9a06">&amp;#34;experimental-features=nix-command flakes&amp;#34;&lt;/span> &amp;gt; ~/.config/nix/nix.conf
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Building DPsim, including all its dependencies:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-shell" data-lang="shell">&lt;span style="display:flex;">&lt;span>nix build github:sogno-platform/dpsim
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>The result is placed in the &lt;code>result&lt;/code> folder of the current directory. For development, a local
environment can be set up with:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-shell" data-lang="shell">&lt;span style="display:flex;">&lt;span>nix develop github:sogno-platform/dpsim
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>The Flake reference above can be replaced by a local path such as &lt;code>.&lt;/code> when the repository is
already checked out.&lt;/p>
&lt;h2 id="documentation">Documentation&lt;/h2>
&lt;p>The Python and C++ references are generated by separate CMake targets. Both are also built and
published by the &lt;code>documentation&lt;/code> workflow on every push to master.&lt;/p>
&lt;h3 id="python">Python&lt;/h3>
&lt;p>Install &lt;a href="https://www.sphinx-doc.org/en/master/">Sphinx&lt;/a> or use the Docker image, then:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-shell" data-lang="shell">&lt;span style="display:flex;">&lt;span>mkdir -p build &lt;span style="color:#ce5c00;font-weight:bold">&amp;amp;&amp;amp;&lt;/span> &lt;span style="color:#204a87">cd&lt;/span> build
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>cmake ..
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>make docs
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>The result is generated in &lt;code>build/docs/sphinx/html/&lt;/code>. Note that this target requires the Python
bindings, so it is only available when configured with &lt;code>-DWITH_PYBIND=ON&lt;/code>.&lt;/p>
&lt;h3 id="c">C++&lt;/h3>
&lt;p>Install &lt;a href="https://www.doxygen.nl/">Doxygen&lt;/a> or use the Docker image, then:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-shell" data-lang="shell">&lt;span style="display:flex;">&lt;span>mkdir -p build &lt;span style="color:#ce5c00;font-weight:bold">&amp;amp;&amp;amp;&lt;/span> &lt;span style="color:#204a87">cd&lt;/span> build
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>cmake ..
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>make docs_cxx
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>The result is generated in &lt;code>build/docs/doxygen/html/&lt;/code>.&lt;/p>
&lt;h3 id="website">Website&lt;/h3>
&lt;p>The surrounding website is a Hugo site under &lt;code>docs/hugo&lt;/code>. It needs the Hugo version pinned in
the documentation workflow, since the theme does not build with arbitrary versions:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-shell" data-lang="shell">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#204a87">cd&lt;/span> docs/hugo
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>npm ci
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>hugo --minify
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div></description></item><item><title>Docs: Dynamic Phasors</title><link>https://sogno.energy/dpsim/docs/concepts/dyn-phasors/</link><pubDate>Wed, 18 Mar 2020 00:00:00 +0000</pubDate><guid>https://sogno.energy/dpsim/docs/concepts/dyn-phasors/</guid><description>
&lt;p>In the power systems community, dynamic phasors were initially introduced for power electronics analysis &lt;a href="https://ieeexplore.ieee.org/document/76811">Sanders1991&lt;/a> as a more general approach than state-space averaging.
They were used to construct efficient models for the dynamics of switching gate phenomena with a high level of detail as shown in &lt;a href="https://ieeexplore.ieee.org/abstract/document/744524">Mattavelli1999&lt;/a>.
A few years later, dynamic phasors were also employed for power system simulation as described in &lt;a href="https://www.research-collection.ethz.ch/handle/20.500.11850/123490">Demiray2008&lt;/a>.
In &lt;a href="https://ieeexplore.ieee.org/document/4026700">Strunz2006&lt;/a> the authors combine the dynamic phasor approach with the Electromagnetic Transients Program (EMTP) simulator concept which includes Modified Nodal Analysis (MNA).
Further research topics include fault and stability analysis under unbalanced conditions as presented in &lt;a href="https://ieeexplore.ieee.org/document/871734">Stankovic2000&lt;/a> and also rotating machine models have been developed in dynamic phasors &lt;a href="https://ieeexplore.ieee.org/document/4282063">Zhang 2007&lt;/a>.&lt;/p>
&lt;h2 id="bandpass-signals-and-baseband-representation">Bandpass Signals and Baseband Representation&lt;/h2>
&lt;p>Although here, dynamic phasors are presented as a power system modelling tool, it should be noted that the concept is also known in other domains, for example, microwave and communications engineering &lt;a href="https://ieeexplore.ieee.org/document/9100822">Maas2003&lt;/a>, &lt;a href="https://ieeexplore.ieee.org/book/5236646">Suarez2009&lt;/a>, &lt;a href="https://isbnsearch.org/isbn/9780471697909">Haykin2009&lt;/a>, &lt;a href="https://isbnsearch.org/isbn/0131589326">Proakis2001&lt;/a>.
In these domains, the approach is often denoted as base band representation or complex envelope.
Another common term coming from power electrical engineering is shifted frequency analysis (SFA).
In the following, the general approach of dynamic phasors for power system simulation is explained starting from the idea of bandpass signals.
This is because the 50 Hz or 60 Hz fundamental and small deviations from it can be seen as such a bandpass signal.
Futhermore, higher frequencies, for example, generated by power electronics can be modelled in a similar way.&lt;/p>
&lt;h2 id="two-derivations-of-the-same-transform">Two Derivations of the Same Transform&lt;/h2>
&lt;p>The literature reaches the envelope description along two different routes. One
starts from the analytic signal and shifts the spectrum; the other averages a
sliding Fourier series. They are usually presented independently, under
different names, and it is not always stated that they arrive at the same
differential operator. The two routes are set out below so that the equivalence
is visible.&lt;/p>
&lt;h3 id="route-a-shifting-the-spectrum">Route A: shifting the spectrum&lt;/h3>
&lt;p>A bandpass signal is one whose spectrum is concentrated in a band around some
carrier frequency $\omega_s$, narrow compared with $\omega_s$ itself. The
network voltages and currents of a power system are of this kind: energy sits
around the 50 Hz or 60 Hz fundamental and spreads only a little either side of
it during a transient.&lt;/p>
&lt;p>Such a signal can be written exactly as&lt;/p>
&lt;div class="math">$$x(t) = \operatorname{Re}\{\, X(t)\, e^{j \omega_s t} \,\},$$&lt;/div>&lt;p>where the complex quantity $X(t)$ is the envelope. It is obtained from the
analytic signal $x_a(t) = x(t) + j,\mathcal{H}{x}(t)$, with
$\mathcal{H}$ the Hilbert transform, by&lt;/p>
&lt;div class="math">$$X(t) = x_a(t)\, e^{-j \omega_s t}.$$&lt;/div>&lt;p>Multiplication by $e^{-j\omega_s t}$ translates the spectrum down by
$\omega_s$, so the band that sat around the carrier now sits around zero. That
translation is what the name shifted frequency analysis refers to. No
information is lost, and nothing is approximated: the operation is invertible
and $x(t)$ can be recovered from $X(t)$ at any instant.&lt;/p>
&lt;p>The consequence that matters for simulation is what happens to the derivative.
Differentiating the expression above gives&lt;/p>
&lt;div class="math">$$\frac{dx}{dt} = \operatorname{Re}\left\{ \left( \frac{dX}{dt} + j \omega_s X \right) e^{j \omega_s t} \right\},$$&lt;/div>&lt;p>so in envelope coordinates the time derivative becomes&lt;/p>
&lt;div class="math">$$\frac{d}{dt} \;\longrightarrow\; \frac{d}{dt} + j \omega_s .$$&lt;/div>&lt;p>An inductor $v = L,\frac{di}{dt}$ therefore satisfies
$V = L,\frac{dI}{dt} + j \omega_s L, I$. Two limiting cases are worth
noting. When the envelope is constant, the first term vanishes and what remains
is $V = j\omega_s L, I$, the classical steady-state phasor relation. When
$\omega_s = 0$, the second term vanishes and the envelope is the instantaneous
signal itself, which is the electromagnetic transient description. Both the
phasor and the instantaneous representation are special cases of the same
expression.&lt;/p>
&lt;h3 id="route-b-averaging-a-fourier-series">Route B: averaging a Fourier series&lt;/h3>
&lt;p>The dynamic phasor derivation starts elsewhere. Over a window of length
$T = 2\pi/\omega_s$ ending at time $t$, the waveform is expanded in a Fourier
series&lt;/p>
&lt;div class="math">$$x(t + s) = \sum_{k} X_k(t)\, e^{j k \omega_s s}, \qquad s \in (-T, 0],$$&lt;/div>&lt;p>whose coefficients are themselves functions of time,&lt;/p>
&lt;div class="math">$$X_k(t) = \frac{1}{T} \int_{t-T}^{t} x(\tau)\, e^{-j k \omega_s \tau} \, d\tau .$$&lt;/div>&lt;p>These sliding-window coefficients are the dynamic phasors. Each one is constant
whenever the waveform is periodic and varies only as the waveform&amp;rsquo;s shape
changes, which is why they are also called generalized averages. Integrating by
parts gives the property the method is built on,&lt;/p>
&lt;div class="math">$$\frac{dX_k}{dt} = \left\langle \frac{dx}{dt} \right\rangle_k - j k \omega_s X_k ,$$&lt;/div>&lt;p>that is, the same shift as Route A, applied to the $k$-th coefficient.&lt;/p>
&lt;h3 id="where-the-routes-meet">Where the routes meet&lt;/h3>
&lt;p>Retaining only $k = 1$ makes Route B&amp;rsquo;s relation identical to Route A&amp;rsquo;s: the
first coefficient obeys $\frac{d}{dt} + j\omega_s$ and the network equations
that follow are the same. The two derivations produce one model.&lt;/p>
&lt;p>The difference is emphasis rather than substance. Route A treats a single
carrier and is exact for any signal, so the accuracy question is only whether
the signal is genuinely bandpass around the carrier that was chosen. Route B
offers a whole family of coefficients, so it can carry a DC component in
$X_0$, the fundamental in $X_1$ and switching harmonics in higher $X_k$
simultaneously, at the cost of one complex state per coefficient retained.
Truncating that series is where the approximation enters: energy at frequencies
that were not given a coefficient is discarded, and the sliding window acts as a
low-pass filter on what remains, so envelope components varying as fast as
$\omega_s$ itself are attenuated.&lt;/p>
&lt;p>This explains the split in usage. Work concerned with converter switching,
harmonic interaction or unbalanced and fault conditions keeps several
coefficients and speaks of dynamic phasors. Work concerned with transient
simulation of the fundamental keeps one and speaks of shifted frequency
analysis, emphasising the enlarged time step rather than the series. The
communications and microwave literature calls the same object the complex
envelope or the lowpass equivalent. The three names describe one transform, and
the choice between them says more about a paper&amp;rsquo;s lineage than about its
mathematics.&lt;/p>
&lt;h2 id="relationship-to-electromagnetic-transient-models">Relationship to Electromagnetic Transient Models&lt;/h2>
&lt;p>An electromagnetic transient (EMT) model solves for the instantaneous
quantities directly. Because the waveform it integrates oscillates at the
carrier, the step size has to resolve that oscillation, and the accuracy of the
result is bounded by how finely the fundamental period is sampled. A shifted
frequency model integrates the envelope instead. In sinusoidal steady state the
envelope is constant, so the step size is dictated by the bandwidth of the
transient rather than by the carrier, and it can be substantially larger for
the same accuracy. This is the reason the approach is attractive for transient
stability studies and for real-time simulation of large networks.&lt;/p>
&lt;p>The saving is not free, and it is worth being precise about where it comes
from. It is not that the envelope model is a coarser description of the same
system; for a single carrier the transform is exact. It is that the carrier
oscillation has been removed from the quantity being integrated and folded into
the coefficient $j\omega_s$, which the model handles analytically rather than
numerically. What the envelope model cannot represent is content the retained
coefficients do not cover, which for a single-coefficient model means anything
outside the band around $\omega_s$.&lt;/p>
&lt;p>Because $x(t) = \operatorname{Re}{X(t) e^{j\omega_s t}}$ holds at every
instant, converting between the two descriptions is an algebraic operation at a
given point in time, not a filtering or a fitting problem. That is what makes
mixed-domain and co-simulation setups possible: part of a network can be solved
in envelope form and part in instantaneous form, with the interface performing
the multiplication in one direction and the extraction of the envelope in the
other. Two conditions have to be respected at such an interface. The envelope
is defined relative to a chosen $\omega_s$ and a chosen time origin, so both
sides must agree on the carrier frequency and share a phase reference; and the
extraction direction needs the analytic signal, which for a general
instantaneous waveform cannot be formed from a single sample. Any delay
introduced between the two sides appears as a phase error on the carrier, and
that error grows with $\omega_s$ rather than with the envelope&amp;rsquo;s own rate of
change.&lt;/p>
&lt;p>The state-space form of the shift, and how the resulting complex system is
solved as a real one, are covered in
&lt;a href="https://sogno.energy/dpsim/docs/concepts/state-space-nodal/">State-Space Nodal&lt;/a>.&lt;/p></description></item><item><title>Docs: DP Ph1 Averaged Voltage Source Inverter</title><link>https://sogno.energy/dpsim/docs/concepts/models/power-electronics/dp-ph1-averaged-vsi/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://sogno.energy/dpsim/docs/concepts/models/power-electronics/dp-ph1-averaged-vsi/</guid><description>
&lt;p>This model ports the same grid-following averaged inverter into the dynamic-phasor (DP) domain, as a single positive-sequence complex envelope rather than three abc waveforms.
The PLL, power filter, outer power control, and inner current control are baseband and stay real; only the LC filter&amp;rsquo;s two states are genuine carrier-band envelopes and carry the $-j\omega_n$ shift described in &lt;a href="https://sogno.energy/dpsim/docs/concepts/state-space-nodal/">State-Space Nodal&lt;/a>.&lt;/p>
&lt;p>The terminal input is the PCC voltage envelope&lt;/p>
&lt;div class="math">$$u = U ,$$&lt;/div>&lt;p>and the state vector is the mixed real/complex-envelope form&lt;/p>
&lt;div class="math">$$\mathbf{x} =
\begin{bmatrix}
\psi &amp;
\phi_{\mathrm{PLL}} &amp;
P &amp;
Q &amp;
\phi_d &amp;
\phi_q &amp;
\gamma_d &amp;
\gamma_q &amp;
\operatorname{Re}\{V_c\} &amp;
\operatorname{Im}\{V_c\} &amp;
\operatorname{Re}\{I_f\} &amp;
\operatorname{Im}\{I_f\}
\end{bmatrix}^\top ,$$&lt;/div>&lt;p>where $\psi := \theta_{\mathrm{PLL}} - \omega_n t$ is the PLL angle&amp;rsquo;s deviation from the nominal carrier phase, tracked instead of the raw, unboundedly growing $\theta_{\mathrm{PLL}}$ for relinearization accuracy, and $V_c$, $I_f$ are complex envelopes replacing EMT&amp;rsquo;s six abc filter states.&lt;/p>
&lt;p>The model output is the interface current injected into the MNA system,&lt;/p>
&lt;div class="math">$$y = \frac{U - V_c}{R_c}.$$&lt;/div>&lt;h2 id="model-equations">Model equations&lt;/h2>
&lt;p>The controller uses the opposite current direction, i.e. positive current denotes inverter injection into the grid,&lt;/p>
&lt;div class="math">$$I_{rc} = \frac{V_c - U}{R_c}.$$&lt;/div>&lt;p>Because the DP envelope already demodulates the carrier, the dq quantities are obtained by rotating the envelope by $\psi$ alone, not by the full absolute angle $\theta_{\mathrm{PLL}}$,&lt;/p>
&lt;div class="math">$$V_{c,dq} = V_c\, e^{-j\psi}, \qquad I_{rc,dq} = I_{rc}\, e^{-j\psi},$$&lt;/div>&lt;p>with $v_{c,d} = \operatorname{Re}{V_{c,dq}}$, $v_{c,q} = \operatorname{Im}{V_{c,dq}}$, and likewise for $i_{rc,d}$, $i_{rc,q}$.&lt;/p>
&lt;p>The instantaneous active and reactive powers are calculated as&lt;/p>
&lt;div class="math">$$p = v_{c,d} i_{rc,d} + v_{c,q} i_{rc,q},$$&lt;/div>
&lt;div class="math">$$q = -v_{c,d} i_{rc,q} + v_{c,q} i_{rc,d},$$&lt;/div>&lt;p>identical in form to EMT&amp;rsquo;s; &lt;code>DP::Ph1&lt;/code>&amp;rsquo;s own voltage/current scale already represents total power directly, with no three-phase multiplier.&lt;/p>
&lt;p>The PLL and power-filter dynamics are&lt;/p>
&lt;div class="math">$$\dot{\psi}
=
K_{p,\mathrm{PLL}} v_{c,q} +
K_{i,\mathrm{PLL}} \phi_{\mathrm{PLL}},$$&lt;/div>
&lt;div class="math">$$\dot{\phi}_{\mathrm{PLL}} = v_{c,q},$$&lt;/div>
&lt;div class="math">$$\dot{P} = \omega_c(p - P),
\qquad
\dot{Q} = \omega_c(q - Q).$$&lt;/div>&lt;p>The outer power-control integrators and current references are&lt;/p>
&lt;div class="math">$$\dot{\phi}_d = P_{\mathrm{ref}} - P,
\qquad
\dot{\phi}_q = Q - Q_{\mathrm{ref}},$$&lt;/div>
&lt;div class="math">$$i_{d,\mathrm{ref}}
=
K_{p,P}(P_{\mathrm{ref}} - P) + K_{i,P}\phi_d,$$&lt;/div>
&lt;div class="math">$$i_{q,\mathrm{ref}}
=
K_{p,P}(Q - Q_{\mathrm{ref}}) + K_{i,P}\phi_q.$$&lt;/div>&lt;p>The inner current-control integrators and voltage references are&lt;/p>
&lt;div class="math">$$\dot{\gamma}_d = i_{d,\mathrm{ref}} - i_{rc,d},
\qquad
\dot{\gamma}_q = i_{q,\mathrm{ref}} - i_{rc,q},$$&lt;/div>
&lt;div class="math">$$v_{d,\mathrm{ref}}
=
K_{p,I}(i_{d,\mathrm{ref}} - i_{rc,d}) +
K_{i,I}\gamma_d,$$&lt;/div>
&lt;div class="math">$$v_{q,\mathrm{ref}}
=
K_{p,I}(i_{q,\mathrm{ref}} - i_{rc,q}) +
K_{i,I}\gamma_q.$$&lt;/div>&lt;p>The reference voltage is transformed back to a complex envelope, rotating by $\psi$,&lt;/p>
&lt;div class="math">$$V_{\mathrm{ref}} = (v_{d,\mathrm{ref}} + j v_{q,\mathrm{ref}})\, e^{j\psi}.$$&lt;/div>&lt;p>The LC filter dynamics carry the envelope&amp;rsquo;s carrier shift explicitly,&lt;/p>
&lt;div class="math">$$\dot{V}_c
=
\frac{1}{C_f} I_f
+
\frac{1}{C_f R_c}(U - V_c)
- j\omega_n V_c,$$&lt;/div>
&lt;div class="math">$$\dot{I}_f
=
\frac{1}{L_f}
\left(
V_{\mathrm{ref}}
-
V_c
-
R_f I_f
\right)
- j\omega_n I_f.$$&lt;/div>&lt;p>At each simulation step, the nonlinear model is locally linearized into the affine state-space form, packing the 8 real states and the real/imaginary parts of the 2 complex states into one real 12-vector,&lt;/p>
&lt;div class="math">$$\dot{\mathbf{x}}
\approx
\mathbf{A}\mathbf{x}
+
\mathbf{B}\mathbf{u}
+
\mathbf{E},$$&lt;/div>
&lt;div class="math">$$\mathbf{y}
\approx
\mathbf{C}\mathbf{x}
+
\mathbf{D}\mathbf{u}
+
\mathbf{F},$$&lt;/div>&lt;p>which is then discretized and stamped into the network equations.&lt;/p>
&lt;p>How this is arranged in code, together with the source and the runnable examples, is covered under
&lt;a href="https://sogno.energy/dpsim/docs/developer-guide/model-implementations/dp-ph1-averaged-vsi-implementation/">DP Ph1 averaged VSI implementation&lt;/a>.&lt;/p></description></item><item><title>Docs: LLM Pull Request Review</title><link>https://sogno.energy/dpsim/docs/contributing/llm-pr-review/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://sogno.energy/dpsim/docs/contributing/llm-pr-review/</guid><description>
&lt;h2 id="overview">Overview&lt;/h2>
&lt;p>DPsim ships an optional, non-blocking pull-request reviewer that runs a series of
specialised passes over the diff of a pull request using a large language model
and posts a single review comment. It is intended as an assistive first pass: it
never requests changes and cannot block a merge, so a human review remains
authoritative.&lt;/p>
&lt;p>The reviewer lives under &lt;code>.github/llm-review/&lt;/code> (the prompts in &lt;code>prompts.py&lt;/code> and a
pure-standard-library runner in &lt;code>review.py&lt;/code>) and is driven by two workflows:
&lt;code>llm-review-collect.yml&lt;/code>, which runs on the pull request itself, and
&lt;code>llm-review.yml&lt;/code>, which performs the review. The split is what makes reviewing
pull requests from forks safe (see &lt;a href="#fork-pull-requests">Fork pull requests&lt;/a>). It
communicates with any OpenAI-compatible chat endpoint, configured through the
environment variables described below.&lt;/p>
&lt;h2 id="how-it-works">How it works&lt;/h2>
&lt;p>For each pull request the runner reads the &lt;code>base..head&lt;/code> diff and sends it, in
turn, to a set of focused review stages, each with its own prompt. The stages
cover model equations and their derivation, MNA stamping and domain modeling,
numerical correctness, task scheduling and attribute usage, real-time safety,
C++ class design and reuse, naming and in-code documentation, logging discipline,
the Python bindings, input parsing, the build system and dependencies, testing
and component coverage, and licensing and pull-request hygiene. Each stage returns
a strict JSON list of findings. A final synthesis pass deduplicates and
prioritises them, and the runner posts them as one review, anchoring inline
comments only to lines present in the diff.&lt;/p>
&lt;p>The prompts encode DPsim&amp;rsquo;s documented conventions (see
&lt;a href="https://sogno.energy/dpsim/docs/contributing/">Guidelines&lt;/a>) and the recurring points raised in
past reviews, so the feedback stays specific to this project rather than generic.&lt;/p>
&lt;h2 id="configuration">Configuration&lt;/h2>
&lt;p>The workflow requires one repository secret:&lt;/p>
&lt;ul>
&lt;li>&lt;code>RWTH_LLM_TOKEN&lt;/code>: the bearer API key for the chat endpoint.&lt;/li>
&lt;/ul>
&lt;p>The following repository Actions variables are optional and override the
defaults baked into the workflow:&lt;/p>
&lt;ul>
&lt;li>&lt;code>LLM_BASE_URL&lt;/code>: the OpenAI-compatible base URL.&lt;/li>
&lt;li>&lt;code>LLM_MODEL&lt;/code>: the model identifier.&lt;/li>
&lt;li>&lt;code>LLM_CHAT_PATH&lt;/code>: the chat path appended to the base URL (default
&lt;code>/chat/completions&lt;/code>).&lt;/li>
&lt;li>&lt;code>LLM_REVIEW_RUNNER&lt;/code>: the runner label (default &lt;code>ubuntu-latest&lt;/code>; see
&lt;a href="#runner-selection">Runner selection&lt;/a>).&lt;/li>
&lt;/ul>
&lt;p>The workflow&amp;rsquo;s baked-in defaults target an OpenAI-compatible deployment; override
&lt;code>LLM_BASE_URL&lt;/code> and &lt;code>LLM_MODEL&lt;/code> to point at a different endpoint or model.&lt;/p>
&lt;h2 id="obtaining-an-api-key">Obtaining an API key&lt;/h2>
&lt;p>Obtain a bearer API key from the chosen OpenAI-compatible provider and store it as
the &lt;code>RWTH_LLM_TOKEN&lt;/code> repository secret. The key is only exposed to workflow runs on
pull requests from the repository itself, never from forks.&lt;/p>
&lt;p>Before storing the secret, a single request confirms that the key reaches the
model:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-bash" data-lang="bash">&lt;span style="display:flex;">&lt;span>curl -sS -X POST &lt;span style="color:#4e9a06">&amp;#34;&lt;/span>&lt;span style="color:#000">$LLM_BASE_URL&lt;/span>&lt;span style="color:#4e9a06">/chat/completions&amp;#34;&lt;/span> &lt;span style="color:#4e9a06">\
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4e9a06">&lt;/span> -H &lt;span style="color:#4e9a06">&amp;#34;Authorization: Bearer &lt;/span>&lt;span style="color:#000">$RWTH_LLM_TOKEN&lt;/span>&lt;span style="color:#4e9a06">&amp;#34;&lt;/span> &lt;span style="color:#4e9a06">\
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4e9a06">&lt;/span> -H &lt;span style="color:#4e9a06">&amp;#34;Content-Type: application/json&amp;#34;&lt;/span> &lt;span style="color:#4e9a06">\
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4e9a06">&lt;/span> -d &lt;span style="color:#4e9a06">&amp;#34;{\&amp;#34;model\&amp;#34;:\&amp;#34;&lt;/span>&lt;span style="color:#000">$LLM_MODEL&lt;/span>&lt;span style="color:#4e9a06">\&amp;#34;,\&amp;#34;messages\&amp;#34;:[{\&amp;#34;role\&amp;#34;:\&amp;#34;user\&amp;#34;,\&amp;#34;content\&amp;#34;:\&amp;#34;ok\&amp;#34;}]}&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="running-locally">Running locally&lt;/h2>
&lt;p>The runner has a dry-run mode that executes the full pipeline and prints the
assembled review instead of posting it. It needs no GitHub token and no Actions
runner, only network access to the endpoint:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-bash" data-lang="bash">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#204a87">cd&lt;/span> .github/llm-review
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#204a87">export&lt;/span> &lt;span style="color:#000">LLM_BASE_URL&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">=&lt;/span>&lt;span style="color:#4e9a06">&amp;#39;&amp;lt;openai-compatible-base-url&amp;gt;&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#204a87">export&lt;/span> &lt;span style="color:#000">LLM_MODEL&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">=&lt;/span>&lt;span style="color:#4e9a06">&amp;#39;&amp;lt;model-identifier&amp;gt;&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#204a87">export&lt;/span> &lt;span style="color:#000">LLM_CHAT_PATH&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">=&lt;/span>&lt;span style="color:#4e9a06">&amp;#39;/chat/completions&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#204a87">export&lt;/span> &lt;span style="color:#000">LLM_API_KEY&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">=&lt;/span>&lt;span style="color:#4e9a06">&amp;#34;&lt;/span>&lt;span style="color:#000">$RWTH_LLM_TOKEN&lt;/span>&lt;span style="color:#4e9a06">&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#204a87">export&lt;/span> &lt;span style="color:#000">BASE_SHA&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">=&lt;/span>&lt;span style="color:#204a87;font-weight:bold">$(&lt;/span>git rev-parse origin/main&lt;span style="color:#204a87;font-weight:bold">)&lt;/span> &lt;span style="color:#000">HEAD_SHA&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">=&lt;/span>&lt;span style="color:#204a87;font-weight:bold">$(&lt;/span>git rev-parse HEAD&lt;span style="color:#204a87;font-weight:bold">)&lt;/span> &lt;span style="color:#000">PR_NUMBER&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">=&lt;/span>&lt;span style="color:#0000cf;font-weight:bold">0&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>python3 review.py --dry-run
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="runner-selection">Runner selection&lt;/h2>
&lt;p>The workflow defaults to a GitHub-hosted &lt;code>ubuntu-latest&lt;/code> runner. If the chosen
endpoint is only reachable from within a particular network, set the
&lt;code>LLM_REVIEW_RUNNER&lt;/code> variable to a self-hosted runner label registered inside that
network; no change to the workflow is required.&lt;/p>
&lt;h2 id="fork-pull-requests">Fork pull requests&lt;/h2>
&lt;p>Reviewing pull requests from forks requires care, because a fork&amp;rsquo;s code is
untrusted and must never gain access to the secret. The reviewer uses the
&lt;code>workflow_run&lt;/code> pattern for this, rather than &lt;code>pull_request_target&lt;/code>, and splits the
work into two workflows:&lt;/p>
&lt;ul>
&lt;li>&lt;code>llm-review-collect.yml&lt;/code> runs on the &lt;code>pull_request&lt;/code> event, including from
forks. GitHub withholds secrets from fork &lt;code>pull_request&lt;/code> runs, so this job has
no key. It checks out nothing and runs no code from the pull request; it only
records the PR number and commit SHAs, taken from trusted GitHub context, into
an artifact.&lt;/li>
&lt;li>&lt;code>llm-review.yml&lt;/code> runs on &lt;code>workflow_run&lt;/code>, after the collect job completes, in
the base repository context where the secret is available. It checks out the
base repository&amp;rsquo;s own code, never the pull request&amp;rsquo;s, and reads the diff as
data through the GitHub API. It never builds or executes anything from the pull
request.&lt;/li>
&lt;/ul>
&lt;p>Two properties keep the key safe. First, the key is only ever sent as an
&lt;code>Authorization&lt;/code> header to the configured LLM endpoint, and is never placed in the
model prompt, so a prompt-injection payload in the diff cannot reveal it. Second,
the privileged job runs only trusted base-repository code, so untrusted pull
request code never executes with the secret in scope. The PR metadata read from
the collect artifact is validated (numeric PR number, hexadecimal SHAs) before
use. For this to operate, both the workflows and the secret must reside on the
repository the pull requests target.&lt;/p></description></item><item><title>Docs: Power Flow Solvers</title><link>https://sogno.energy/dpsim/docs/developer-guide/solvers/powerflow-solvers/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://sogno.energy/dpsim/docs/developer-guide/solvers/powerflow-solvers/</guid><description>
&lt;p>What DPsim implements. For the underlying formulation, the mismatch function and the Jacobian,
see &lt;a href="https://sogno.energy/dpsim/docs/concepts/powerflow/">power flow&lt;/a>.&lt;/p>
&lt;h2 id="solver-implementations">Solver Implementations&lt;/h2>
&lt;p>DPsim ships two implementations of the Newton-Raphson power flow solver with power
mismatch and polar coordinates. Both produce identical results (to round-off); they
differ only in how the Jacobian is stored and factorized:&lt;/p>
&lt;ul>
&lt;li>&lt;code>PFSolverPowerPolar&lt;/code> (dense): assembles a dense Jacobian and computes a fresh
factorization every Newton iteration. This is the default.&lt;/li>
&lt;li>&lt;code>PFSolverPowerPolarSparse&lt;/code> (sparse): assembles the Jacobian into a sparse matrix
whose sparsity pattern is fixed (derived once from the network admittance matrix).
The symbolic factorization (ordering) is analyzed once and reused; only the numeric
values are recomputed each Newton iteration. The first iteration of every power flow
solve does a full factorization with pivoting, and subsequent iterations refactorize
while reusing that ordering (via KLU when available). This scales better on large,
sparse grids.&lt;/li>
&lt;/ul>
&lt;p>The dense solver is used by default. To opt in to the sparse solver:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-python" data-lang="python">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#000">sim&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">set_pf_solver_use_sparse&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#204a87;font-weight:bold">True&lt;/span>&lt;span style="color:#000;font-weight:bold">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>The flag is ignored and the dense solver is used if DPsim was built without a sparse
linear solver. The benchmark notebook
&lt;code>examples/Notebooks/Grids/PF_Sparse_vs_Dense.ipynb&lt;/code> runs a range of network sizes both
ways, verifies the converged voltages match, and compares run time.&lt;/p>
&lt;h2 id="generator-reactive-power-limits">Generator Reactive Power Limits&lt;/h2>
&lt;p>A PV bus assumes its generator can produce whatever reactive power the Newton-Raphson
solution asks for, holding $\vert V_k \vert$ at its setpoint. Real generators cannot: Q
is bounded by $Q_{min}$ and $Q_{max}$. DPsim can enforce these bounds with a
bidirectional PV↔PQ outer loop:&lt;/p>
&lt;ol>
&lt;li>Run the inner Newton-Raphson solve to convergence (as described above).&lt;/li>
&lt;li>For every PV bus, compute the generator&amp;rsquo;s actual reactive output. If it exceeds
$Q_{max}$ or falls below $Q_{min}$, pin the injection at the violated limit and
convert the bus to PQ.&lt;/li>
&lt;li>For every bus pinned this way in an earlier pass, check whether the constraint has
relaxed: if $\vert V_k \vert$ has moved past its original setpoint in the releasing
direction, restore voltage control and convert the bus back to PV.&lt;/li>
&lt;li>Repeat from step 1 until no bus switches, an outer-iteration cap is hit, or a
per-bus switch counter trips (an anti-oscillation guard, since a bus can otherwise
toggle PV↔PQ indefinitely near the boundary).&lt;/li>
&lt;/ol>
&lt;p>Enforcement is opt-in and defaults off, so a system with no limits configured behaves
exactly as before:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-python" data-lang="python">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#000">sim&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">set_pf_solver_enforce_q_limits&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#204a87;font-weight:bold">True&lt;/span>&lt;span style="color:#000;font-weight:bold">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>$Q_{min}$/$Q_{max}$ are set per generator via &lt;code>SynchronGenerator.set_parameters(..., q_limit_max=..., q_limit_min=...)&lt;/code>; the defaults are $\pm\infty$ (unlimited).
Generators sharing a bus have their limits summed. The two limits are enforced
independently, with no assumption about sign or relative magnitude: asymmetric bounds
(e.g. $Q_{max}=150$ MVAr, $Q_{min}=-30$ MVAr) and same-sign bounds (e.g. a generator
restricted to $Q \in [20, 150]$ MVAr, always producing, or $Q \in [-150, -20]$ MVAr,
always absorbing) are both enforced correctly.&lt;/p>
&lt;p>&lt;strong>Limitation: no P-dependent capability curve.&lt;/strong> $Q_{min}$ and $Q_{max}$ are constants
set once per generator, not a function of active power output $P$. A real synchronous
generator&amp;rsquo;s reactive capability is a &amp;ldquo;D-curve&amp;rdquo; bounded by three physically distinct
mechanisms: the stator (armature) current limit $\sqrt{P^2+Q^2} \le S_{rated}$, the
rotor (field) current / heating limit on the over-excited (Q-providing) side, and the
under-excitation limiter (UEL) / steady-state stability limit on the under-excited
(Q-absorbing) side. All three tighten as $P$ approaches rated output, and the over- and
under-excited bounds come from unrelated physical limits, so the true feasible region is
neither symmetric in $Q$ nor independent of $P$. DPsim does not model this curve; a
generator&amp;rsquo;s $Q$ headroom is the same regardless of how much $P$ it is producing at the
time. Flat per-generator limits are a common baseline in power-flow tools generally, so
this is not a regression, but a P-dependent capability curve is not currently
implemented.&lt;/p>
&lt;p>The notebook &lt;code>examples/Notebooks/Grids/PF_Generator_Qlimits.ipynb&lt;/code> validates the
switching behavior on a small hand-wired case (binding and non-binding limits, dense vs.
sparse agreement).&lt;/p></description></item><item><title>Docs: Turbines and Governors</title><link>https://sogno.energy/dpsim/docs/concepts/models/synchronous-generator-regulators/turbines-governors/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://sogno.energy/dpsim/docs/concepts/models/synchronous-generator-regulators/turbines-governors/</guid><description>
&lt;h2 id="steam-governor">Steam Governor&lt;/h2>
&lt;center>
&lt;figure margin=30%>
&lt;img src="./images/SteamGovernor.drawio.svg" alt="Steam Governor">
&lt;figcaption>&lt;/br>Fig. 8: Control diagram of the steam turbine governor &lt;/br>
Adapted from [6]
&lt;/figcaption>
&lt;/figure>
&lt;/center>
&lt;/br>
&lt;p>The control diagram of this model is depicted in Fig. 8. This model receives as input the frequency deviation $\Delta\omega = \omega_{ref} - \omega$ from the nominal frequency (normally $50,\text{Hz}$ or $60,\text{Hz}$) and produces the valve opening signal $p_{gv}$ for the turbine. $p_{ref}$ is the mechanical power produced at nominal frequency. The governor implements a lead-lag controller $\frac{K(1+sT_2)}{(1+sT_1)}$ where $K=1/R$ and $R$ is the droop coefficient, followed by a PT1 integrator with embedded rate limiters and an anti-windup loop. To avoid unnecessary dead-beat behaviour, complex transfer functions with more than one pole and zero are decomposed via partial fraction expansion into parallel PT1 elements, as shown in Fig. 9.&lt;/p>
&lt;center>
&lt;figure margin=30%>
&lt;img src="./images/SteamGovernor_split.drawio.svg" alt="Steam Governor split">
&lt;figcaption>&lt;/br>Fig. 9: Control diagram of the steam turbine governor after partial-fraction decomposition &lt;/br>
&lt;/figcaption>
&lt;/figure>
&lt;/center>
&lt;/br>
&lt;p>Analogous to the static exciter model, the integrator uses an anti-windup strategy as shown in Fig. 10.&lt;/p>
&lt;center>
&lt;figure margin=30%>
&lt;img src="./images/SteamGovernor_windup.drawio.svg" alt="Steam Governor anti-windup">
&lt;figcaption>&lt;/br>Fig. 10: Control diagram of the steam turbine governor with anti-windup strategy &lt;/br>
&lt;/figcaption>
&lt;/figure>
&lt;/center>
&lt;/br>
&lt;p>The forward-Euler discretised equations are:
$$
\Delta \omega (k-\Delta t) = \omega_{ref} - \omega (k-\Delta t),
$$
$$
p_{1}(k) = p_{1}(k-\Delta t) + \frac{\Delta t}{T_{1}} \left(\Delta \omega (k-\Delta t) \cdot \frac{T_{1} - T_{2}}{T_{1}} - p_{1}(k-\Delta t)\right),
$$
$$
p(k-\Delta t) = \frac{1}{R} \left(p_{1}(k-\Delta t) + \Delta \omega(k-\Delta t) \cdot \frac{T_{2}}{T_{1}}\right),
$$
$$
\dot{p}(k-\Delta t) = \frac{1}{T_{3}}\left(p(k-\Delta t) + p_{ref} - p_{gv}(k-\Delta t)\right) - K_{bc} \left(p_{gv}^{&lt;em>}(k-\Delta t) - p_{gv}(k-\Delta t)\right),
$$
$$
p_{gv}^{&lt;/em>}(k) = p_{gv}^{*}(k-\Delta t) + \Delta t \cdot \dot{p}(k-\Delta t),
$$&lt;/p>
&lt;p>and&lt;/p>
&lt;p>$$
p_{gv}(k) = p_{gv}^{&lt;em>}(k) \quad \text{if} \quad P_{m,\min} \leq p_{gv}^{&lt;/em>}(k) \leq P_{m,\max}, \
p_{gv}(k) = P_{m,\max} \quad \text{if} \quad p_{gv}^{&lt;em>}(k) &amp;gt; P_{m,\max}, \
p_{gv}(k) = P_{m,\min} \quad \text{if} \quad p_{gv}^{&lt;/em>}(k) &amp;lt; P_{m,\min}.
$$&lt;/p>
&lt;p>If $T_1 = 0$ the $p_1(k)$ equation is skipped and $p(k)$ is instead:
$$
p(k-\Delta t) = \frac{1}{R} \left(\Delta \omega(k-\Delta t) + \frac{T_{2}}{\Delta t} \left(\Delta \omega(k-\Delta t) - \Delta \omega(k-2\Delta t)\right)\right).
$$&lt;/p>
&lt;p>Assuming the simulation starts in steady state (all derivatives zero, $\Delta\omega(0)=0$), the initial values are:
$$
p_{1}(t=0) = 0, \quad p(t=0) = 0, \quad p_{ref} = p_{gv}^{*}(t=0) = p_{gv}(t=0).
$$&lt;/p>
&lt;h2 id="steam-turbine">Steam Turbine&lt;/h2>
&lt;center>
&lt;figure margin=30%>
&lt;img src="./images/SteamTurbine.drawio.svg" alt="Steam Turbine">
&lt;figcaption>&lt;/br>Fig. 11: Control diagram of the steam turbine &lt;/br>
Adapted from [6]
&lt;/figcaption>
&lt;/figure>
&lt;/center>
&lt;/br>
&lt;p>The steam turbine receives the valve opening signal $p_{gv}$ from the Steam Governor and outputs mechanical power $p_m$ to the synchronous generator. It is divided into high-pressure (HP), intermediate-pressure (IP), and low-pressure (LP) stages, each modelled as a first-order lag with time constants $T_{CH}$, $T_{RH}$, $T_{CO}$ respectively. Setting a time constant to zero disables that lag element. The total mechanical power is a weighted sum of each stage: $F_{HP} + F_{IP} + F_{LP} = 1$ must hold. The forward-Euler discretised equations are:&lt;/p>
&lt;p>$$
p_{hp}(k) = p_{hp}(k-\Delta t) + \frac{\Delta t}{T_{CH}} \left(p_{gv}(k-\Delta t) - p_{hp}(k-\Delta t)\right),
$$
$$
p_{ip}(k) = p_{ip}(k-\Delta t) + \frac{\Delta t}{T_{RH}} \left(p_{hp}(k-\Delta t) - p_{ip}(k-\Delta t)\right),
$$
$$
p_{lp}(k) = p_{lp}(k-\Delta t) + \frac{\Delta t}{T_{CO}} \left(p_{ip}(k-\Delta t) - p_{lp}(k-\Delta t)\right),
$$
$$
p_{m}(k) = F_{HP} \cdot p_{hp}(k) + F_{IP} \cdot p_{ip}(k) + F_{LP} \cdot p_{lp}(k).
$$&lt;/p>
&lt;p>Assuming the simulation starts in steady state (all derivatives zero), the initial values are:
$$
p_{hp}(t=0) = p_{gv}(t=0), \quad p_{ip}(t=0) = p_{hp}(t=0), \quad p_{lp}(t=0) = p_{ip}(t=0),
$$
$$
p_{m}(t=0) = F_{HP} \cdot p_{hp}(t=0) + F_{IP} \cdot p_{ip}(t=0) + F_{LP} \cdot p_{lp}(t=0).
$$&lt;/p>
&lt;h2 id="hydro-turbine-governor">Hydro Turbine Governor&lt;/h2>
&lt;center>
&lt;figure margin=30%>
&lt;img src="./images/HydroGovernor.drawio.svg" alt="Hydro Governor">
&lt;figcaption>&lt;/br>Fig. 12: Control diagram of a hydro turbine governor &lt;/br>
Adapted from [6]
&lt;/figcaption>
&lt;/figure>
&lt;/center>
&lt;/br>
&lt;p>The Hydro Turbine Governor receives the frequency deviation $\Delta\omega = \omega_{ref} - \omega$ as input and produces the valve/gate opening signal $p_{gv}$ for the turbine. $p_{ref}$ is the mechanical power produced at nominal frequency. The controller transfer function is $K\frac{1+sT_2}{(1+sT_1)(1+sT_3)}$, where $K=\frac{1}{R}$ and $R$ is the droop coefficient. The transfer function is decomposed into two parallel PT1 blocks as shown in Fig. 13.&lt;/p>
&lt;center>
&lt;figure margin=30%>
&lt;img src="./images/HydroGovernor_split.drawio.svg" alt="Hydro Governor split">
&lt;figcaption>&lt;/br>Fig. 13: Control diagram of a hydro turbine governor after partial-fraction decomposition &lt;/br>
&lt;/figcaption>
&lt;/figure>
&lt;/center>
&lt;/br>
&lt;p>The forward-Euler discretised equations are:
$$
x_{1}(k) = x_{1}(k-\Delta t) + \frac{\Delta t}{T_{1}} \left(\Delta\omega(k-\Delta t) - x_{1}(k-\Delta t)\right),
$$
$$
x_{2}(k) = x_{2}(k-\Delta t) + \frac{\Delta t}{T_{3}} \left(\Delta\omega(k-\Delta t) - x_{2}(k-\Delta t)\right),
$$
$$
p^{*}&lt;em>{gv}(k) = \frac{1}{R}\left(A \cdot x&lt;/em>{1}(k) + B \cdot x_{2}(k)\right) + p_{ref},
$$&lt;/p>
&lt;p>where
$$
A = \frac{T_{1}-T_{2}}{T_{1}-T_{3}}, \qquad B = \frac{T_{2}-T_{3}}{T_{1}-T_{3}},
$$&lt;/p>
&lt;p>and the output limiter is applied as:
$$
p_{gv}(k) = \begin{cases}
P_{m,\max} &amp;amp; \text{if } p^{&lt;em>}&lt;em>{gv}(k) &amp;gt; P&lt;/em>{m,\max}, \
P_{m,\min} &amp;amp; \text{if } p^{&lt;/em>}&lt;em>{gv}(k) &amp;lt; P&lt;/em>{m,\min}, \
p^{*}_{gv}(k) &amp;amp; \text{otherwise.}
\end{cases}
$$&lt;/p>
&lt;p>Assuming the simulation starts in steady state (all derivatives zero, $\Delta\omega(t=0)=0$), the initial values are:
$$
x_{1}(t=0) = 0, \quad x_{2}(t=0) = 0, \quad p_{ref} = p_{gv}(t=0).
$$&lt;/p>
&lt;h2 id="hydro-turbine">Hydro Turbine&lt;/h2>
&lt;center>
&lt;figure margin=30%>
&lt;img src="./images/HydroTurbine.drawio.svg" alt="Hydro Turbine">
&lt;figcaption>&lt;/br>Fig. 14: Control diagram of a hydro turbine &lt;/br>
Adapted from [6]
&lt;/figcaption>
&lt;/figure>
&lt;/center>
&lt;/br>
&lt;p>The Hydro Turbine receives the gate opening signal $p_{gv}$ from the Hydro Turbine Governor and outputs mechanical power $p_m$ to the synchronous generator. The transfer function is specified by the water starting time $T_W$ and can be represented as the sum of two parallel blocks as shown in Fig. 15.&lt;/p>
&lt;center>
&lt;figure margin=30%>
&lt;img src="./images/HydroTurbine_split.drawio.svg" alt="Hydro Turbine split">
&lt;figcaption>&lt;/br>Fig. 15: Control diagram of a hydro turbine after decomposition &lt;/br>
Adapted from [6]
&lt;/figcaption>
&lt;/figure>
&lt;/center>
&lt;/br>
&lt;p>The forward-Euler discretised equations are:
$$
x_{1}(k) = x_{1}(k-\Delta t) + \frac{\Delta t}{0.5,T_{W}} \left(p_{gv}(k-\Delta t) - x_{1}(k-\Delta t)\right),
$$
$$
p_{m}(k) = 3,x_{1}(k) - 2,p_{gv}(k).
$$&lt;/p>
&lt;p>Assuming the simulation starts in steady state (all derivatives zero), the initial values are:
$$
x_{1}(t=0) = p_{gv}(t=0), \quad p_{m}(t=0) = p_{gv}(t=0).
$$&lt;/p></description></item><item><title>Docs: Alternative Solver Implementation</title><link>https://sogno.energy/dpsim/docs/developer-guide/solvers/alternative-solvers/</link><pubDate>Fri, 31 Jul 2026 00:00:00 +0000</pubDate><guid>https://sogno.energy/dpsim/docs/developer-guide/solvers/alternative-solvers/</guid><description>
&lt;p>The methods are derived under
&lt;a href="https://sogno.energy/dpsim/docs/concepts/alternative-solvers/">alternative solution methods&lt;/a>. This page
covers the code and the configuration.&lt;/p>
&lt;h2 id="daesolver">&lt;code>DAESolver&lt;/code>&lt;/h2>
&lt;p>Wraps the IDA integrator from Sundials. &lt;code>initialize&lt;/code> builds the state and derivative vectors,
registers each component&amp;rsquo;s residual function, then creates the solver with &lt;code>IDACreate&lt;/code>, passes the
solver instance as user data so the residual callbacks can reach it, and sets scalar relative and
absolute tolerances with &lt;code>IDASStolerances&lt;/code>.&lt;/p>
&lt;p>Components take part by implementing &lt;code>DAEInterface&lt;/code> and contributing their residual. The offset
vector recorded at the top of the file defines how each component&amp;rsquo;s block is laid out within the
global residual.&lt;/p>
&lt;p>Two practical notes. The solver chooses its own steps, so a run&amp;rsquo;s cost is not predictable and it
cannot be used under a real-time timer. And several &lt;code>std::cout&lt;/code> calls remain in the initialization
path, so it prints to standard output independently of the logger.&lt;/p>
&lt;h2 id="odesolver-and-odeintsolver">&lt;code>ODESolver&lt;/code> and &lt;code>ODEintSolver&lt;/code>&lt;/h2>
&lt;p>&lt;code>ODESolver&lt;/code> wraps CVODE from Sundials for a single component, sizing the problem from
&lt;code>mOdePreState&lt;/code> and attaching a dense linear solver. &lt;code>ODEintSolver&lt;/code> does the same job with boost&amp;rsquo;s
odeint, calling &lt;code>comp-&amp;gt;odeint(y, ydot, t)&lt;/code>.&lt;/p>
&lt;p>Both integrate one component across a network step while the network itself stays on its fixed step,
so the coupling is staggered and first-order accurate regardless of the inner integrator&amp;rsquo;s order.&lt;/p>
&lt;h2 id="diakopticssolver">&lt;code>DiakopticsSolver&lt;/code>&lt;/h2>
&lt;p>Constructed with the system and an explicit list of components to tear, which must implement
&lt;code>MNATearInterface&lt;/code>. &lt;code>system.splitSubnets&lt;/code> performs the partition, &lt;code>initSubnets&lt;/code> builds the
per-subnetwork node and component lists, and &lt;code>mNodeSubnetMap&lt;/code> records which subnetwork owns each
node.&lt;/p>
&lt;p>&lt;code>createTearMatrices&lt;/code> is specialised per value type, and the sizes differ in a way worth noting: the
&lt;code>Real&lt;/code> specialisation allocates &lt;code>tearComponents * phaseMultiplier&lt;/code>, while the &lt;code>Complex&lt;/code> one
allocates &lt;strong>twice&lt;/strong> that, because a complex quantity is carried as a real-augmented pair. The phase
multiplier is 3 when the subnetwork phase type is &lt;code>ABC&lt;/code> and 1 otherwise, taken from the first node
of the system.&lt;/p>
&lt;p>The removed-branch system is dense and small. A comment in the source notes that the reduction could
still be sped up by exploiting the block diagonal structure of the inverse, so the present
implementation is correct rather than optimal.&lt;/p>
&lt;h2 id="linear-backends-under-mna">Linear backends under MNA&lt;/h2>
&lt;div class="alert alert-info" role="alert">
&lt;h4 class="alert-heading">Requires the matching build options&lt;/h4>
The nodal solver does not implement its factorisation. &lt;code>MNASolverFactory&lt;/code> selects an adapter, and
&lt;code>mSupportedSolverImpls&lt;/code> is compiled conditionally, so which of the implementations below exist
depends entirely on how DPsim was configured. The GPU adapters need a CUDA build.
&lt;/div>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Implementation&lt;/th>
&lt;th>Adapter&lt;/th>
&lt;th>Notes&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;code>KLU&lt;/code>&lt;/td>
&lt;td>&lt;code>KLUAdapter&lt;/code>&lt;/td>
&lt;td>Default, and the fallback when the choice is &lt;code>Undef&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>SparseLU&lt;/code>&lt;/td>
&lt;td>&lt;code>SparseLUAdapter&lt;/code>&lt;/td>
&lt;td>Eigen&amp;rsquo;s sparse LU&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>DenseLU&lt;/code>&lt;/td>
&lt;td>&lt;code>DenseLUAdapter&lt;/code>&lt;/td>
&lt;td>Dense, for small systems&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>CUDADense&lt;/code>&lt;/td>
&lt;td>&lt;code>GpuDenseAdapter&lt;/code>&lt;/td>
&lt;td>Requires a CUDA build&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>CUDASparse&lt;/code>&lt;/td>
&lt;td>&lt;code>GpuSparseAdapter&lt;/code>&lt;/td>
&lt;td>Requires a CUDA build&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>CUDAMagma&lt;/code>&lt;/td>
&lt;td>&lt;code>GpuMagmaAdapter&lt;/code>&lt;/td>
&lt;td>Requires a CUDA build with Magma&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>Plugin&lt;/code>&lt;/td>
&lt;td>loaded at runtime&lt;/td>
&lt;td>For a solver outside the tree&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>&lt;code>DirectLinearSolverConfiguration&lt;/code> tunes the chosen backend, and not every option applies to every
one:&lt;/p>
&lt;ul>
&lt;li>&lt;code>SCALING_METHOD&lt;/code>: none, sum or max&lt;/li>
&lt;li>&lt;code>FILL_IN_REDUCTION_METHOD&lt;/code>: &lt;code>AMD&lt;/code>, &lt;code>AMD_NV&lt;/code>, &lt;code>AMD_RA&lt;/code> or &lt;code>COLAMD&lt;/code>. The &lt;code>NV&lt;/code> and &lt;code>RA&lt;/code> variants take
the set of time-varying entries into account when ordering, which is what makes partial
refactorization effective for a network with switching elements.&lt;/li>
&lt;li>&lt;code>PARTIAL_REFACTORIZATION_METHOD&lt;/code>: none, factorization path, or refactorization restart. This is the
lever that matters when a switch or a variable component changes the matrix every step.&lt;/li>
&lt;li>&lt;code>USE_BTF&lt;/code>: block triangular form on or off&lt;/li>
&lt;/ul>
&lt;p>The defaults are chosen for a general network. The combination of an ordering that knows about
varying entries with partial refactorization is what makes repeated switching affordable, and it is
inert if the matrix never changes.&lt;/p>
&lt;h2 id="source">Source&lt;/h2>
&lt;p>Under &lt;code>dpsim/src/&lt;/code>: &lt;code>DAESolver.cpp&lt;/code>, &lt;code>ODESolver.cpp&lt;/code>, &lt;code>ODEintSolver.cpp&lt;/code>, &lt;code>DiakopticsSolver.cpp&lt;/code>,
and the six &lt;code>*Adapter.cpp&lt;/code> files.&lt;/p></description></item><item><title>Docs: A Network, and Where It Starts From</title><link>https://sogno.energy/dpsim/docs/tutorials/python/two-bus-network/</link><pubDate>Fri, 31 Jul 2026 00:00:00 +0000</pubDate><guid>https://sogno.energy/dpsim/docs/tutorials/python/two-bus-network/</guid><description>
&lt;p>The circuits so far started from nothing and settled. That is fine for a resistor and an inductor,
and useless for a network: a real system is already running when you start looking at it, and the
transient you care about is the one caused by an event, not by switching the whole grid on.&lt;/p>
&lt;p>This tutorial builds a two-bus network, solves its steady state with a powerflow, and starts the
dynamic simulation from that solution.&lt;/p>
&lt;p>&lt;img src="circuit.svg" alt="A slack bus, a line, and a load at the far bus.">&lt;/p>
&lt;h2 id="part-one-the-powerflow">Part one: the powerflow&lt;/h2>
&lt;p>A powerflow is a different kind of simulation. It has no time step in any meaningful sense; it
solves the algebraic steady state, iterating until the bus voltages are consistent with the
specified powers.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-python" data-lang="python">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#204a87;font-weight:bold">import&lt;/span> &lt;span style="color:#000">dpsimpy&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#204a87;font-weight:bold">import&lt;/span> &lt;span style="color:#000">villas.dataprocessing.readtools&lt;/span> &lt;span style="color:#204a87;font-weight:bold">as&lt;/span> &lt;span style="color:#000">rt&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#000">Vnom&lt;/span> &lt;span style="color:#ce5c00;font-weight:bold">=&lt;/span> &lt;span style="color:#0000cf;font-weight:bold">20e3&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#000">n1pf&lt;/span> &lt;span style="color:#ce5c00;font-weight:bold">=&lt;/span> &lt;span style="color:#000">dpsimpy&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">sp&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">SimNode&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#4e9a06">&amp;#34;n1&amp;#34;&lt;/span>&lt;span style="color:#000;font-weight:bold">,&lt;/span> &lt;span style="color:#000">dpsimpy&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">PhaseType&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">Single&lt;/span>&lt;span style="color:#000;font-weight:bold">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#000">n2pf&lt;/span> &lt;span style="color:#ce5c00;font-weight:bold">=&lt;/span> &lt;span style="color:#000">dpsimpy&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">sp&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">SimNode&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#4e9a06">&amp;#34;n2&amp;#34;&lt;/span>&lt;span style="color:#000;font-weight:bold">,&lt;/span> &lt;span style="color:#000">dpsimpy&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">PhaseType&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">Single&lt;/span>&lt;span style="color:#000;font-weight:bold">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#000">slack&lt;/span> &lt;span style="color:#ce5c00;font-weight:bold">=&lt;/span> &lt;span style="color:#000">dpsimpy&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">sp&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">ph1&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">NetworkInjection&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#4e9a06">&amp;#34;slack&amp;#34;&lt;/span>&lt;span style="color:#000;font-weight:bold">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#000">slack&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">set_parameters&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#000">voltage_set_point&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">=&lt;/span>&lt;span style="color:#000">Vnom&lt;/span>&lt;span style="color:#000;font-weight:bold">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#000">slack&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">set_base_voltage&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#000">Vnom&lt;/span>&lt;span style="color:#000;font-weight:bold">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#000">slack&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">modify_power_flow_bus_type&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#000">dpsimpy&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">PowerflowBusType&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">VD&lt;/span>&lt;span style="color:#000;font-weight:bold">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#000">line&lt;/span> &lt;span style="color:#ce5c00;font-weight:bold">=&lt;/span> &lt;span style="color:#000">dpsimpy&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">sp&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">ph1&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">PiLine&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#4e9a06">&amp;#34;line&amp;#34;&lt;/span>&lt;span style="color:#000;font-weight:bold">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#000">line&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">set_parameters&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#000">R&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">=&lt;/span>&lt;span style="color:#0000cf;font-weight:bold">0.5&lt;/span>&lt;span style="color:#000;font-weight:bold">,&lt;/span> &lt;span style="color:#000">L&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">=&lt;/span>&lt;span style="color:#0000cf;font-weight:bold">0.5&lt;/span> &lt;span style="color:#ce5c00;font-weight:bold">/&lt;/span> &lt;span style="color:#0000cf;font-weight:bold">314&lt;/span>&lt;span style="color:#000;font-weight:bold">,&lt;/span> &lt;span style="color:#000">C&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">=&lt;/span>&lt;span style="color:#0000cf;font-weight:bold">50e-6&lt;/span>&lt;span style="color:#000;font-weight:bold">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#000">line&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">set_base_voltage&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#000">Vnom&lt;/span>&lt;span style="color:#000;font-weight:bold">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#000">load&lt;/span> &lt;span style="color:#ce5c00;font-weight:bold">=&lt;/span> &lt;span style="color:#000">dpsimpy&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">sp&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">ph1&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">Load&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#4e9a06">&amp;#34;load&amp;#34;&lt;/span>&lt;span style="color:#000;font-weight:bold">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#000">load&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">set_parameters&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#000">active_power&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">=&lt;/span>&lt;span style="color:#0000cf;font-weight:bold">100e3&lt;/span>&lt;span style="color:#000;font-weight:bold">,&lt;/span> &lt;span style="color:#000">reactive_power&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">=&lt;/span>&lt;span style="color:#0000cf;font-weight:bold">50e3&lt;/span>&lt;span style="color:#000;font-weight:bold">,&lt;/span> &lt;span style="color:#000">nominal_voltage&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">=&lt;/span>&lt;span style="color:#000">Vnom&lt;/span>&lt;span style="color:#000;font-weight:bold">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#000">load&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">modify_power_flow_bus_type&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#000">dpsimpy&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">PowerflowBusType&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">PQ&lt;/span>&lt;span style="color:#000;font-weight:bold">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#000">slack&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">connect&lt;/span>&lt;span style="color:#000;font-weight:bold">([&lt;/span>&lt;span style="color:#000">n1pf&lt;/span>&lt;span style="color:#000;font-weight:bold">])&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#000">line&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">connect&lt;/span>&lt;span style="color:#000;font-weight:bold">([&lt;/span>&lt;span style="color:#000">n1pf&lt;/span>&lt;span style="color:#000;font-weight:bold">,&lt;/span> &lt;span style="color:#000">n2pf&lt;/span>&lt;span style="color:#000;font-weight:bold">])&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#000">load&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">connect&lt;/span>&lt;span style="color:#000;font-weight:bold">([&lt;/span>&lt;span style="color:#000">n2pf&lt;/span>&lt;span style="color:#000;font-weight:bold">])&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#000">system_pf&lt;/span> &lt;span style="color:#ce5c00;font-weight:bold">=&lt;/span> &lt;span style="color:#000">dpsimpy&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">SystemTopology&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#0000cf;font-weight:bold">50&lt;/span>&lt;span style="color:#000;font-weight:bold">,&lt;/span> &lt;span style="color:#000;font-weight:bold">[&lt;/span>&lt;span style="color:#000">n1pf&lt;/span>&lt;span style="color:#000;font-weight:bold">,&lt;/span> &lt;span style="color:#000">n2pf&lt;/span>&lt;span style="color:#000;font-weight:bold">],&lt;/span> &lt;span style="color:#000;font-weight:bold">[&lt;/span>&lt;span style="color:#000">slack&lt;/span>&lt;span style="color:#000;font-weight:bold">,&lt;/span> &lt;span style="color:#000">line&lt;/span>&lt;span style="color:#000;font-weight:bold">,&lt;/span> &lt;span style="color:#000">load&lt;/span>&lt;span style="color:#000;font-weight:bold">])&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#000">logger_pf&lt;/span> &lt;span style="color:#ce5c00;font-weight:bold">=&lt;/span> &lt;span style="color:#000">dpsimpy&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">Logger&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#4e9a06">&amp;#34;pf&amp;#34;&lt;/span>&lt;span style="color:#000;font-weight:bold">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#000">logger_pf&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">log_attribute&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#4e9a06">&amp;#34;v1&amp;#34;&lt;/span>&lt;span style="color:#000;font-weight:bold">,&lt;/span> &lt;span style="color:#4e9a06">&amp;#34;v&amp;#34;&lt;/span>&lt;span style="color:#000;font-weight:bold">,&lt;/span> &lt;span style="color:#000">n1pf&lt;/span>&lt;span style="color:#000;font-weight:bold">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#000">logger_pf&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">log_attribute&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#4e9a06">&amp;#34;v2&amp;#34;&lt;/span>&lt;span style="color:#000;font-weight:bold">,&lt;/span> &lt;span style="color:#4e9a06">&amp;#34;v&amp;#34;&lt;/span>&lt;span style="color:#000;font-weight:bold">,&lt;/span> &lt;span style="color:#000">n2pf&lt;/span>&lt;span style="color:#000;font-weight:bold">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#000">sim_pf&lt;/span> &lt;span style="color:#ce5c00;font-weight:bold">=&lt;/span> &lt;span style="color:#000">dpsimpy&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">Simulation&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#4e9a06">&amp;#34;pf&amp;#34;&lt;/span>&lt;span style="color:#000;font-weight:bold">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#000">sim_pf&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">set_system&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#000">system_pf&lt;/span>&lt;span style="color:#000;font-weight:bold">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#000">sim_pf&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">set_domain&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#000">dpsimpy&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">Domain&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">SP&lt;/span>&lt;span style="color:#000;font-weight:bold">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#000">sim_pf&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">set_solver&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#000">dpsimpy&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">Solver&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">NRP&lt;/span>&lt;span style="color:#000;font-weight:bold">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#000">sim_pf&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">set_solver_component_behaviour&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#000">dpsimpy&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">SolverBehaviour&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">Initialization&lt;/span>&lt;span style="color:#000;font-weight:bold">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#000">sim_pf&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">do_init_from_nodes_and_terminals&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#204a87;font-weight:bold">False&lt;/span>&lt;span style="color:#000;font-weight:bold">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#000">sim_pf&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">set_time_step&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#0000cf;font-weight:bold">0.1&lt;/span>&lt;span style="color:#000;font-weight:bold">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#000">sim_pf&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">set_final_time&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#0000cf;font-weight:bold">0.1&lt;/span>&lt;span style="color:#000;font-weight:bold">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#000">sim_pf&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">add_logger&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#000">logger_pf&lt;/span>&lt;span style="color:#000;font-weight:bold">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#000">sim_pf&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">run&lt;/span>&lt;span style="color:#000;font-weight:bold">()&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Four things here are new and none of them are optional.&lt;/p>
&lt;p>The powerflow is built in the &lt;strong>static phasor&lt;/strong> domain, &lt;code>dpsimpy.sp&lt;/code>, whatever domain the dynamic
run will use. The solver is set to &lt;code>Solver.NRP&lt;/code>, the Newton-Raphson powerflow solver, rather than
the default nodal solver.&lt;/p>
&lt;p>&lt;code>modify_power_flow_bus_type&lt;/code> is what makes the problem solvable. Every bus must declare which two of
its four quantities are known: &lt;code>VD&lt;/code> fixes voltage magnitude and angle, and there must be exactly one
such bus, the slack, which absorbs whatever mismatch remains. &lt;code>PQ&lt;/code> fixes active and reactive power,
which is what a load specifies. Without these the powerflow has no boundary conditions.&lt;/p>
&lt;p>&lt;code>set_base_voltage&lt;/code> is required on components because the solver works in per unit, and
&lt;code>do_init_from_nodes_and_terminals(False)&lt;/code> tells the components not to try to initialize themselves
from node voltages that do not exist yet, since establishing those voltages is the job this
simulation is doing.&lt;/p>
&lt;p>Running it gives 20 000 V at the slack and &lt;strong>20 075 V&lt;/strong> at the load bus. The load bus sitting above
nominal is not an error: the line&amp;rsquo;s shunt capacitance supplies more reactive power at this load than
the series impedance drops.&lt;/p>
&lt;h2 id="part-two-the-dynamic-run">Part two: the dynamic run&lt;/h2>
&lt;p>The dynamic network is built separately, in the domain the simulation will actually use, and then
takes its initial state from the powerflow solution.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-python" data-lang="python">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#000">n1&lt;/span> &lt;span style="color:#ce5c00;font-weight:bold">=&lt;/span> &lt;span style="color:#000">dpsimpy&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">dp&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">SimNode&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#4e9a06">&amp;#34;n1&amp;#34;&lt;/span>&lt;span style="color:#000;font-weight:bold">,&lt;/span> &lt;span style="color:#000">dpsimpy&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">PhaseType&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">Single&lt;/span>&lt;span style="color:#000;font-weight:bold">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#000">n2&lt;/span> &lt;span style="color:#ce5c00;font-weight:bold">=&lt;/span> &lt;span style="color:#000">dpsimpy&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">dp&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">SimNode&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#4e9a06">&amp;#34;n2&amp;#34;&lt;/span>&lt;span style="color:#000;font-weight:bold">,&lt;/span> &lt;span style="color:#000">dpsimpy&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">PhaseType&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">Single&lt;/span>&lt;span style="color:#000;font-weight:bold">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#000">slack_d&lt;/span> &lt;span style="color:#ce5c00;font-weight:bold">=&lt;/span> &lt;span style="color:#000">dpsimpy&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">dp&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">ph1&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">NetworkInjection&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#4e9a06">&amp;#34;slack&amp;#34;&lt;/span>&lt;span style="color:#000;font-weight:bold">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#000">slack_d&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">set_parameters&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#000">V_ref&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">=&lt;/span>&lt;span style="color:#204a87">complex&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#000">Vnom&lt;/span>&lt;span style="color:#000;font-weight:bold">,&lt;/span> &lt;span style="color:#0000cf;font-weight:bold">0&lt;/span>&lt;span style="color:#000;font-weight:bold">))&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#000">line_d&lt;/span> &lt;span style="color:#ce5c00;font-weight:bold">=&lt;/span> &lt;span style="color:#000">dpsimpy&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">dp&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">ph1&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">PiLine&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#4e9a06">&amp;#34;line&amp;#34;&lt;/span>&lt;span style="color:#000;font-weight:bold">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#000">line_d&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">set_parameters&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#000">series_resistance&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">=&lt;/span>&lt;span style="color:#0000cf;font-weight:bold">0.5&lt;/span>&lt;span style="color:#000;font-weight:bold">,&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#000">series_inductance&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">=&lt;/span>&lt;span style="color:#0000cf;font-weight:bold">0.5&lt;/span> &lt;span style="color:#ce5c00;font-weight:bold">/&lt;/span> &lt;span style="color:#0000cf;font-weight:bold">314&lt;/span>&lt;span style="color:#000;font-weight:bold">,&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#000">parallel_capacitance&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">=&lt;/span>&lt;span style="color:#0000cf;font-weight:bold">50e-6&lt;/span>&lt;span style="color:#000;font-weight:bold">,&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#000;font-weight:bold">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#000">load_d&lt;/span> &lt;span style="color:#ce5c00;font-weight:bold">=&lt;/span> &lt;span style="color:#000">dpsimpy&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">dp&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">ph1&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">RXLoad&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#4e9a06">&amp;#34;load&amp;#34;&lt;/span>&lt;span style="color:#000;font-weight:bold">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#000">load_d&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">set_parameters&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#000">active_power&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">=&lt;/span>&lt;span style="color:#0000cf;font-weight:bold">100e3&lt;/span>&lt;span style="color:#000;font-weight:bold">,&lt;/span> &lt;span style="color:#000">reactive_power&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">=&lt;/span>&lt;span style="color:#0000cf;font-weight:bold">50e3&lt;/span>&lt;span style="color:#000;font-weight:bold">,&lt;/span> &lt;span style="color:#000">volt&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">=&lt;/span>&lt;span style="color:#000">Vnom&lt;/span>&lt;span style="color:#000;font-weight:bold">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#000">slack_d&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">connect&lt;/span>&lt;span style="color:#000;font-weight:bold">([&lt;/span>&lt;span style="color:#000">n1&lt;/span>&lt;span style="color:#000;font-weight:bold">])&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#000">line_d&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">connect&lt;/span>&lt;span style="color:#000;font-weight:bold">([&lt;/span>&lt;span style="color:#000">n1&lt;/span>&lt;span style="color:#000;font-weight:bold">,&lt;/span> &lt;span style="color:#000">n2&lt;/span>&lt;span style="color:#000;font-weight:bold">])&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#000">load_d&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">connect&lt;/span>&lt;span style="color:#000;font-weight:bold">([&lt;/span>&lt;span style="color:#000">n2&lt;/span>&lt;span style="color:#000;font-weight:bold">])&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#000">system_dp&lt;/span> &lt;span style="color:#ce5c00;font-weight:bold">=&lt;/span> &lt;span style="color:#000">dpsimpy&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">SystemTopology&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#0000cf;font-weight:bold">50&lt;/span>&lt;span style="color:#000;font-weight:bold">,&lt;/span> &lt;span style="color:#000;font-weight:bold">[&lt;/span>&lt;span style="color:#000">n1&lt;/span>&lt;span style="color:#000;font-weight:bold">,&lt;/span> &lt;span style="color:#000">n2&lt;/span>&lt;span style="color:#000;font-weight:bold">],&lt;/span> &lt;span style="color:#000;font-weight:bold">[&lt;/span>&lt;span style="color:#000">slack_d&lt;/span>&lt;span style="color:#000;font-weight:bold">,&lt;/span> &lt;span style="color:#000">line_d&lt;/span>&lt;span style="color:#000;font-weight:bold">,&lt;/span> &lt;span style="color:#000">load_d&lt;/span>&lt;span style="color:#000;font-weight:bold">])&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#000">system_dp&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">init_with_powerflow&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#000">systemPF&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">=&lt;/span>&lt;span style="color:#000">system_pf&lt;/span>&lt;span style="color:#000;font-weight:bold">,&lt;/span> &lt;span style="color:#000">domain&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">=&lt;/span>&lt;span style="color:#000">dpsimpy&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">Domain&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">DP&lt;/span>&lt;span style="color:#000;font-weight:bold">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#000">logger&lt;/span> &lt;span style="color:#ce5c00;font-weight:bold">=&lt;/span> &lt;span style="color:#000">dpsimpy&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">Logger&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#4e9a06">&amp;#34;dyn&amp;#34;&lt;/span>&lt;span style="color:#000;font-weight:bold">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#000">logger&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">log_attribute&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#4e9a06">&amp;#34;v2&amp;#34;&lt;/span>&lt;span style="color:#000;font-weight:bold">,&lt;/span> &lt;span style="color:#4e9a06">&amp;#34;v&amp;#34;&lt;/span>&lt;span style="color:#000;font-weight:bold">,&lt;/span> &lt;span style="color:#000">n2&lt;/span>&lt;span style="color:#000;font-weight:bold">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#000">sim&lt;/span> &lt;span style="color:#ce5c00;font-weight:bold">=&lt;/span> &lt;span style="color:#000">dpsimpy&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">Simulation&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#4e9a06">&amp;#34;dyn&amp;#34;&lt;/span>&lt;span style="color:#000;font-weight:bold">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#000">sim&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">set_system&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#000">system_dp&lt;/span>&lt;span style="color:#000;font-weight:bold">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#000">sim&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">set_domain&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#000">dpsimpy&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">Domain&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">DP&lt;/span>&lt;span style="color:#000;font-weight:bold">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#000">sim&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">set_time_step&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#0000cf;font-weight:bold">1e-3&lt;/span>&lt;span style="color:#000;font-weight:bold">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#000">sim&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">set_final_time&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#0000cf;font-weight:bold">0.05&lt;/span>&lt;span style="color:#000;font-weight:bold">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#000">sim&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">add_logger&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#000">logger&lt;/span>&lt;span style="color:#000;font-weight:bold">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#000">sim&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">run&lt;/span>&lt;span style="color:#000;font-weight:bold">()&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;code>init_with_powerflow&lt;/code> matches the two networks by node name and copies the solved voltages across,
which is why the node names must agree between the two topologies. It is the only line connecting
the two halves.&lt;/p>
&lt;p>The result is the point of the whole exercise. The dynamic run starts at &lt;strong>20 074.8 V&lt;/strong> and ends at
&lt;strong>20 074.9 V&lt;/strong>: it begins in steady state rather than settling into one. Without the powerflow it
would start from zero and spend the first several cycles charging the line, and any event applied
during that period would be mixed in with a startup transient that has nothing to do with the
system.&lt;/p>
&lt;h2 id="parameter-names-differ-between-domains">Parameter names differ between domains&lt;/h2>
&lt;p>The same component takes different keyword names in different domains. The static phasor line takes
&lt;code>R&lt;/code>, &lt;code>L&lt;/code> and &lt;code>C&lt;/code>; the dynamic phasor line takes &lt;code>series_resistance&lt;/code>, &lt;code>series_inductance&lt;/code> and
&lt;code>parallel_capacitance&lt;/code>. The load is &lt;code>Load&lt;/code> with &lt;code>nominal_voltage&lt;/code> in the powerflow and &lt;code>RXLoad&lt;/code> with
&lt;code>volt&lt;/code> in the dynamic run.&lt;/p>
&lt;div class="alert alert-warning" role="alert">
&lt;h4 class="alert-heading">Watch out: parameter names differ between domains&lt;/h4>
This catches people, and the failure is loud rather than silent: passing the wrong keyword raises a
&lt;code>TypeError&lt;/code> that lists the accepted signature. Read that list rather than guessing, and check the
&lt;a href="https://sogno.energy/dpsim/docs/reference/">generated reference&lt;/a> when adding a component you have not used
before.
&lt;/div>
&lt;h2 id="the-script">The script&lt;/h2>
&lt;p>The complete script for this page is &lt;a href="https://github.com/sogno-platform/dpsim/blob/master/examples/Python/Tutorials/03_two_bus_network.py">&lt;code>03_two_bus_network.py&lt;/code>&lt;/a> under &lt;code>examples/Python/Tutorials&lt;/code>. The numbers quoted above are the numbers it prints, so if the two ever disagree the page is the one that is wrong.&lt;/p>
&lt;h2 id="next">Next&lt;/h2>
&lt;p>The network now starts where it should, so an event applied to it produces a clean response. Next is
applying one: a fault, using a switch.&lt;/p>
&lt;p>The powerflow method is described under &lt;a href="https://sogno.energy/dpsim/docs/concepts/powerflow/">powerflow&lt;/a>, and
the loads and lines used here under &lt;a href="https://sogno.energy/dpsim/docs/concepts/models/loads/">loads&lt;/a> and
&lt;a href="https://sogno.energy/dpsim/docs/concepts/models/branches/">branches&lt;/a>.&lt;/p></description></item><item><title>Docs: Transformer</title><link>https://sogno.energy/dpsim/docs/concepts/models/transformer/</link><pubDate>Thu, 22 Jul 2021 00:00:00 +0000</pubDate><guid>https://sogno.energy/dpsim/docs/concepts/models/transformer/</guid><description>
&lt;h2 id="2-winding-transformer">2-Winding Transformer&lt;/h2>
&lt;p>The transformer model is composed of an RL-segment and an ideal transformer.
The single line diagram is depicted in the figure below.&lt;/p>
&lt;p>&lt;img src="electrical_transformer.svg" alt="Transformer">&lt;/p>
&lt;p>If node reduction is not applied, two virtual nodes are created to stamp this model into the system matrix.&lt;/p>
&lt;p>Furthermore, the ideal transformer has an additional equation, which requires an extension of the system matrix.
The complete matrix stamp for the ideal transformer is&lt;/p>
&lt;div class="math">$$\begin{array}{c|c c c}
~ &amp; j &amp; k &amp; l \cr
\hline
j &amp; &amp; &amp; -1 \cr
k &amp; &amp; &amp; T \cr
l &amp; 1 &amp; -T &amp; 0
\end{array}
\begin{pmatrix}
v_j \cr
v_k \cr
i_{l} \cr
\end{pmatrix}
=
\begin{pmatrix}
\cr
\cr
0\cr
\end{pmatrix}$$&lt;/div>&lt;p>The variable $j$ denotes the high voltage node while $k$ is the low voltage node.
$l$ indicates the inserted row and column to accommodate the relation between the two voltages at the ends of the transformer.
The transformer ratio is defined as $T = V_{j} / V_{k}$.
A phase shift can be introduced if $T$ is considered as a complex number.&lt;/p>
&lt;h2 id="why-the-ideal-part-needs-an-extra-equation">Why the ideal part needs an extra equation&lt;/h2>
&lt;p>The ideal transformer imposes two constraints at once: the voltages are in a fixed ratio and the
powers on the two sides are equal, which makes the currents inversely proportional to the same ratio,&lt;/p>
&lt;div class="math">$$\frac{v_j}{v_k} = T, \qquad i_k = -T \, i_j .$$&lt;/div>&lt;p>Neither is a current balance at a node, so neither can be written as an admittance. This is the same
situation as an ideal voltage source described under &lt;a href="https://sogno.energy/dpsim/docs/concepts/models/sources/">sources&lt;/a>: the
system is extended with the branch current as an unknown, the constraint occupies the added row, and
the added diagonal entry is zero. The asymmetry of the stamp, $-1$ against $T$ in the added column
and $1$ against $-T$ in the added row, is exactly the statement that voltage scales by $T$ while
current scales by $1/T$ with opposite sign.&lt;/p>
&lt;p>Making $T$ complex adds a phase shift, which is how a delta-wye connection is represented without
modelling the windings. The magnitude and the angle then carry the tap ratio and the vector group
respectively.&lt;/p>
&lt;h2 id="series-impedance-and-the-direction-of-the-ratio">Series impedance and the direction of the ratio&lt;/h2>
&lt;p>The winding resistance and leakage inductance are lumped into one series branch on one side of the
ideal part rather than split between the two sides. Referring an impedance across an ideal
transformer scales it by $T^2$, so the choice of side is a choice of reference, not an
approximation, and the parameters have to be given consistently with it.&lt;/p>
&lt;p>The ratio is defined greater than one, from high voltage to low. Supplying it the other way round
describes the same physical device but with the two ends exchanged, so a transformer given an
inverted ratio has to have its terminal assignment inverted with it to remain the same transformer.&lt;/p>
&lt;h2 id="numerical-damping">Numerical damping&lt;/h2>
&lt;p>Connecting an inductive branch between two nodes that have no other path to ground leaves those
nodes weakly defined, and the resulting matrix can be poorly conditioned or singular. Small shunt
elements at each terminal remove that, at the cost of a negligible current that would not exist in
the physical device.&lt;/p>
&lt;p>Those elements are sized from the transformer&amp;rsquo;s rated power, which makes the rating a required
parameter rather than documentation. Without a positive rating there is no scale to size them
against, and the natural result is an infinite resistance and a zero capacitance whose admittance is
not a number. A single such entry propagates through the factorisation and destroys the whole
solution, not merely the transformer, so the rating cannot be treated as optional.&lt;/p></description></item><item><title>Docs: Attributes</title><link>https://sogno.energy/dpsim/docs/developer-guide/attributes-and-scheduling/attributes/</link><pubDate>Fri, 01 May 2020 00:00:00 +0000</pubDate><guid>https://sogno.energy/dpsim/docs/developer-guide/attributes-and-scheduling/attributes/</guid><description>
&lt;p>In DPsim, an attribute is a special kind of variable which usually stores a scalar or matrix value used in the simulation.
Examples for attributes are the voltage of a node, the reference current of a current source, or the left and right vectors of the MNA matrix system.
In general, attributes are instances of the &lt;code>Attribute&amp;lt;T&amp;gt;&lt;/code> class, but they are usually stored and accessed through a custom smart pointer of type
&lt;code>const AttributeBase::Ptr&lt;/code> (which expands to &lt;code>const AttributePointer&amp;lt;AttributeBase&amp;gt;&lt;/code>).&lt;/p>
&lt;p>Through the template parameter &lt;code>T&lt;/code> of the &lt;code>Attribute&amp;lt;T&amp;gt;&lt;/code> class, attributes can have different value types, most commonly &lt;code>Real&lt;/code>, &lt;code>Complex&lt;/code>, &lt;code>Matrix&lt;/code>, or &lt;code>MatrixComp&lt;/code>. Additionally, attributes can fall into one of two categories:
&lt;strong>Static&lt;/strong> attributes have a fixed value which can only be changed explicitly through the attribute&amp;rsquo;s &lt;code>set&lt;/code>-method or through a mutable reference obtained through &lt;code>get&lt;/code>.
&lt;strong>Dynamic&lt;/strong> attributes on the other hand can dynamically re-compute their value from other attributes every time they are read. This can for example be used to create a scalar attribute of type &lt;code>Real&lt;/code> whose value always contains the magnitude of another, different attribute of type &lt;code>Complex&lt;/code>.&lt;/p>
&lt;p>Any simulation component or class which inherits from &lt;code>IdentifiedObject&lt;/code> contains an instance of an &lt;code>AttributeList&lt;/code>.
This list can be used to store all the attributes present in this component and later access them via a &lt;code>String&lt;/code> instead of having to use the member variable directly.
For reasons of code clarity and runtime safety, the member variables should still be used whenever possible.&lt;/p>
&lt;h2 id="creating-and-storing-attributes">Creating and Storing Attributes&lt;/h2>
&lt;p>Normally, a new attribute is created by using the &lt;code>create&lt;/code> or &lt;code>createDynamic&lt;/code> method of an &lt;code>AttributeList&lt;/code> object.
These two methods will create a new attribute of the given type and insert it into the &lt;code>AttributeList&lt;/code> under the given name. After the name, &lt;code>create&lt;/code> can take an additional parameter of type &lt;code>T&lt;/code> which will be used as the initial value for this attribute.
Afterwards, a pointer to the attribute is returned which can then be stored in a component&amp;rsquo;s member variable. Usually this is done in the
component&amp;rsquo;s constructor in an initialization list:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-cpp" data-lang="cpp">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#8f5902;font-style:italic">/// Component class Base::Ph1::PiLine
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#8f5902;font-style:italic">&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#204a87;font-weight:bold">public&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">:&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#8f5902;font-style:italic">// Definition of attributes
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#8f5902;font-style:italic">&lt;/span> &lt;span style="color:#204a87;font-weight:bold">const&lt;/span> &lt;span style="color:#000">Attribute&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">&amp;lt;&lt;/span>&lt;span style="color:#000">Real&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">&amp;gt;::&lt;/span>&lt;span style="color:#000">Ptr&lt;/span> &lt;span style="color:#000">mSeriesRes&lt;/span>&lt;span style="color:#000;font-weight:bold">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#204a87;font-weight:bold">const&lt;/span> &lt;span style="color:#000">Attribute&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">&amp;lt;&lt;/span>&lt;span style="color:#000">Real&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">&amp;gt;::&lt;/span>&lt;span style="color:#000">Ptr&lt;/span> &lt;span style="color:#000">mSeriesInd&lt;/span>&lt;span style="color:#000;font-weight:bold">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#204a87;font-weight:bold">const&lt;/span> &lt;span style="color:#000">Attribute&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">&amp;lt;&lt;/span>&lt;span style="color:#000">Real&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">&amp;gt;::&lt;/span>&lt;span style="color:#000">Ptr&lt;/span> &lt;span style="color:#000">mParallelCap&lt;/span>&lt;span style="color:#000;font-weight:bold">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#204a87;font-weight:bold">const&lt;/span> &lt;span style="color:#000">Attribute&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">&amp;lt;&lt;/span>&lt;span style="color:#000">Real&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">&amp;gt;::&lt;/span>&lt;span style="color:#000">Ptr&lt;/span> &lt;span style="color:#000">mParallelCond&lt;/span>&lt;span style="color:#000;font-weight:bold">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#8f5902;font-style:italic">// Component constructor: Initializes the attributes in the initialization list
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#8f5902;font-style:italic">&lt;/span>&lt;span style="color:#000">Base&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">::&lt;/span>&lt;span style="color:#000">Ph1&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">::&lt;/span>&lt;span style="color:#000">PiLine&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#000">CPS&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">::&lt;/span>&lt;span style="color:#000">AttributeList&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">::&lt;/span>&lt;span style="color:#000">Ptr&lt;/span> &lt;span style="color:#000">attributeList&lt;/span>&lt;span style="color:#000;font-weight:bold">)&lt;/span> &lt;span style="color:#ce5c00;font-weight:bold">:&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#000">mSeriesRes&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#000">attributeList&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">-&amp;gt;&lt;/span>&lt;span style="color:#000">create&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">&amp;lt;&lt;/span>&lt;span style="color:#000">Real&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">&amp;gt;&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#4e9a06">&amp;#34;R_series&amp;#34;&lt;/span>&lt;span style="color:#000;font-weight:bold">)),&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#000">mSeriesInd&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#000">attributeList&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">-&amp;gt;&lt;/span>&lt;span style="color:#000">create&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">&amp;lt;&lt;/span>&lt;span style="color:#000">Real&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">&amp;gt;&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#4e9a06">&amp;#34;L_series&amp;#34;&lt;/span>&lt;span style="color:#000;font-weight:bold">)),&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#000">mParallelCap&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#000">attributeList&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">-&amp;gt;&lt;/span>&lt;span style="color:#000">create&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">&amp;lt;&lt;/span>&lt;span style="color:#000">Real&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">&amp;gt;&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#4e9a06">&amp;#34;C_parallel&amp;#34;&lt;/span>&lt;span style="color:#000;font-weight:bold">)),&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#000">mParallelCond&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#000">attributeList&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">-&amp;gt;&lt;/span>&lt;span style="color:#000">create&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">&amp;lt;&lt;/span>&lt;span style="color:#000">Real&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">&amp;gt;&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#4e9a06">&amp;#34;G_parallel&amp;#34;&lt;/span>&lt;span style="color:#000;font-weight:bold">))&lt;/span> &lt;span style="color:#000;font-weight:bold">{&lt;/span> &lt;span style="color:#000;font-weight:bold">};&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>When a class has no access to an &lt;code>AttributeList&lt;/code> object (for example the &lt;code>Simulation&lt;/code> class), attributes can instead be created through the
&lt;code>make&lt;/code> methods on &lt;code>AttributeStatic&amp;lt;T&amp;gt;&lt;/code> and &lt;code>AttributeDynamic&amp;lt;T&amp;gt;&lt;/code>:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-cpp" data-lang="cpp">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#8f5902;font-style:italic">// Simulation class
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#8f5902;font-style:italic">&lt;/span>&lt;span style="color:#000">Simulation&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">::&lt;/span>&lt;span style="color:#000">Simulation&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#000">String&lt;/span> &lt;span style="color:#000">name&lt;/span>&lt;span style="color:#000;font-weight:bold">,&lt;/span> &lt;span style="color:#000">Logger&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">::&lt;/span>&lt;span style="color:#000">Level&lt;/span> &lt;span style="color:#000">logLevel&lt;/span>&lt;span style="color:#000;font-weight:bold">)&lt;/span> &lt;span style="color:#ce5c00;font-weight:bold">:&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#000">mName&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#000">AttributeStatic&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">&amp;lt;&lt;/span>&lt;span style="color:#000">String&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">&amp;gt;::&lt;/span>&lt;span style="color:#000">make&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#000">name&lt;/span>&lt;span style="color:#000;font-weight:bold">)),&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#000">mFinalTime&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#000">AttributeStatic&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">&amp;lt;&lt;/span>&lt;span style="color:#000">Real&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">&amp;gt;::&lt;/span>&lt;span style="color:#000">make&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#0000cf;font-weight:bold">0.001&lt;/span>&lt;span style="color:#000;font-weight:bold">)),&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#000">mTimeStep&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#000">AttributeStatic&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">&amp;lt;&lt;/span>&lt;span style="color:#000">Real&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">&amp;gt;::&lt;/span>&lt;span style="color:#000">make&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#0000cf;font-weight:bold">0.001&lt;/span>&lt;span style="color:#000;font-weight:bold">)),&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#000">mSplitSubnets&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#000">AttributeStatic&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">&amp;lt;&lt;/span>&lt;span style="color:#000">Bool&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">&amp;gt;::&lt;/span>&lt;span style="color:#000">make&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#204a87">true&lt;/span>&lt;span style="color:#000;font-weight:bold">)),&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#000">mSteadyStateInit&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#000">AttributeStatic&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">&amp;lt;&lt;/span>&lt;span style="color:#000">Bool&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">&amp;gt;::&lt;/span>&lt;span style="color:#000">make&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#204a87">false&lt;/span>&lt;span style="color:#000;font-weight:bold">)),&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#8f5902;font-style:italic">//...
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#8f5902;font-style:italic">&lt;/span>&lt;span style="color:#000;font-weight:bold">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#8f5902;font-style:italic">// ...
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#8f5902;font-style:italic">&lt;/span>&lt;span style="color:#000;font-weight:bold">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="working-with-static-attributes">Working with Static Attributes&lt;/h2>
&lt;p>As stated above, the value of a static attribute can only be changed through the attribute&amp;rsquo;s &lt;code>set&lt;/code>-method or by writing its value through a mutable reference obtained by calling &lt;code>get&lt;/code>. This means that the value will not change between consecutive reads. Because of the performance benefits static
attributes provide over dynamic attributes, attributes should be static whenever possible.&lt;/p>
&lt;p>The value of a static attribute can be read by using the attribute&amp;rsquo;s &lt;code>get&lt;/code>-function (i.e. &lt;code>attr-&amp;gt;get&lt;/code>) or by applying the &lt;code>*&lt;/code> operator on the already dereferenced pointer (i.e. &lt;code>**attr&lt;/code>), which is overloaded to also call the &lt;code>get&lt;/code> function. Both methods return a &lt;strong>mutable reference&lt;/strong> to the attribute&amp;rsquo;s value of type &lt;code>T&amp;amp;&lt;/code>:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-cpp" data-lang="cpp">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#000">AttributeBase&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">::&lt;/span>&lt;span style="color:#000">Ptr&lt;/span> &lt;span style="color:#000">attr&lt;/span> &lt;span style="color:#ce5c00;font-weight:bold">=&lt;/span> &lt;span style="color:#000">AttributeStatic&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">&amp;lt;&lt;/span>&lt;span style="color:#000">Real&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">&amp;gt;::&lt;/span>&lt;span style="color:#000">make&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#0000cf;font-weight:bold">0.001&lt;/span>&lt;span style="color:#000;font-weight:bold">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#000">Real&lt;/span> &lt;span style="color:#000">read1&lt;/span> &lt;span style="color:#ce5c00;font-weight:bold">=&lt;/span> &lt;span style="color:#000">attr&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">-&amp;gt;&lt;/span>&lt;span style="color:#000">get&lt;/span>&lt;span style="color:#000;font-weight:bold">();&lt;/span> &lt;span style="color:#8f5902;font-style:italic">//read1 = 0.001
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#8f5902;font-style:italic">&lt;/span>&lt;span style="color:#000">Real&lt;/span> &lt;span style="color:#000">read2&lt;/span> &lt;span style="color:#ce5c00;font-weight:bold">=&lt;/span> &lt;span style="color:#ce5c00;font-weight:bold">**&lt;/span>&lt;span style="color:#000">attr&lt;/span>&lt;span style="color:#000;font-weight:bold">;&lt;/span> &lt;span style="color:#8f5902;font-style:italic">//read2 = 0.001
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#8f5902;font-style:italic">&lt;/span>&lt;span style="color:#000">Real&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">&amp;amp;&lt;/span> &lt;span style="color:#000">read3&lt;/span> &lt;span style="color:#ce5c00;font-weight:bold">=&lt;/span> &lt;span style="color:#ce5c00;font-weight:bold">**&lt;/span>&lt;span style="color:#000">attr&lt;/span>&lt;span style="color:#000;font-weight:bold">;&lt;/span> &lt;span style="color:#8f5902;font-style:italic">//read3 = 0.001
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>The value of an attribute can be changed by either writing to the mutable reference obtained from &lt;code>get&lt;/code>, or by calling the &lt;code>set&lt;/code>-method:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-cpp" data-lang="cpp">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#000">AttributeBase&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">::&lt;/span>&lt;span style="color:#000">Ptr&lt;/span> &lt;span style="color:#000">attr&lt;/span> &lt;span style="color:#ce5c00;font-weight:bold">=&lt;/span> &lt;span style="color:#000">AttributeStatic&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">&amp;lt;&lt;/span>&lt;span style="color:#000">Real&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">&amp;gt;::&lt;/span>&lt;span style="color:#000">make&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#0000cf;font-weight:bold">0.001&lt;/span>&lt;span style="color:#000;font-weight:bold">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#000">Real&lt;/span> &lt;span style="color:#000">read1&lt;/span> &lt;span style="color:#ce5c00;font-weight:bold">=&lt;/span> &lt;span style="color:#ce5c00;font-weight:bold">**&lt;/span>&lt;span style="color:#000">attr&lt;/span>&lt;span style="color:#000;font-weight:bold">;&lt;/span> &lt;span style="color:#8f5902;font-style:italic">//read1 = 0.001
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#8f5902;font-style:italic">&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">**&lt;/span>&lt;span style="color:#000">attr&lt;/span> &lt;span style="color:#ce5c00;font-weight:bold">=&lt;/span> &lt;span style="color:#0000cf;font-weight:bold">0.002&lt;/span>&lt;span style="color:#000;font-weight:bold">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#000">Real&lt;/span> &lt;span style="color:#000">read2&lt;/span> &lt;span style="color:#ce5c00;font-weight:bold">=&lt;/span> &lt;span style="color:#ce5c00;font-weight:bold">**&lt;/span>&lt;span style="color:#000">attr&lt;/span>&lt;span style="color:#000;font-weight:bold">;&lt;/span> &lt;span style="color:#8f5902;font-style:italic">//read2 = 0.002
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#8f5902;font-style:italic">&lt;/span>&lt;span style="color:#000">attr&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">-&amp;gt;&lt;/span>&lt;span style="color:#000">set&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#0000cf;font-weight:bold">0.003&lt;/span>&lt;span style="color:#000;font-weight:bold">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#000">Real&lt;/span> &lt;span style="color:#000">read3&lt;/span> &lt;span style="color:#ce5c00;font-weight:bold">=&lt;/span> &lt;span style="color:#ce5c00;font-weight:bold">**&lt;/span>&lt;span style="color:#000">attr&lt;/span>&lt;span style="color:#000;font-weight:bold">;&lt;/span> &lt;span style="color:#8f5902;font-style:italic">//read3 = 0.003
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="working-with-dynamic-attributes">Working with Dynamic Attributes&lt;/h2>
&lt;p>In general, dynamic attributes can be accessed via the same &lt;code>get&lt;/code> and &lt;code>set&lt;/code>-methods described above for static attributes. However,
dynamic attributes can additionally have &lt;strong>dependencies&lt;/strong> on other attributes which affect the behavior of these methods.
Usually, this is used to dynamically compute the attribute&amp;rsquo;s value from the value of another attribute. In the simplest case, a dynamic
attribute can be set to &lt;strong>reference&lt;/strong> another (static or dynamic) attribute using the &lt;code>setReference&lt;/code>-method. After this method has been called,
the dynamic attribute&amp;rsquo;s value will always reflect the value of the attribute it references:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-cpp" data-lang="cpp">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#000">AttributeBase&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">::&lt;/span>&lt;span style="color:#000">Ptr&lt;/span> &lt;span style="color:#000">attr1&lt;/span> &lt;span style="color:#ce5c00;font-weight:bold">=&lt;/span> &lt;span style="color:#000">AttributeStatic&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">&amp;lt;&lt;/span>&lt;span style="color:#000">Real&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">&amp;gt;::&lt;/span>&lt;span style="color:#000">make&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#0000cf;font-weight:bold">0.001&lt;/span>&lt;span style="color:#000;font-weight:bold">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#000">AttributeBase&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">::&lt;/span>&lt;span style="color:#000">Ptr&lt;/span> &lt;span style="color:#000">attr2&lt;/span> &lt;span style="color:#ce5c00;font-weight:bold">=&lt;/span> &lt;span style="color:#000">AttributeDynamic&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">&amp;lt;&lt;/span>&lt;span style="color:#000">Real&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">&amp;gt;::&lt;/span>&lt;span style="color:#000">make&lt;/span>&lt;span style="color:#000;font-weight:bold">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#000">attr2&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">-&amp;gt;&lt;/span>&lt;span style="color:#000">setReference&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#000">attr1&lt;/span>&lt;span style="color:#000;font-weight:bold">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#000">Real&lt;/span> &lt;span style="color:#000">read1&lt;/span> &lt;span style="color:#ce5c00;font-weight:bold">=&lt;/span> &lt;span style="color:#ce5c00;font-weight:bold">**&lt;/span>&lt;span style="color:#000">attr2&lt;/span>&lt;span style="color:#000;font-weight:bold">;&lt;/span> &lt;span style="color:#8f5902;font-style:italic">//read1 = 0.001
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#8f5902;font-style:italic">&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">**&lt;/span>&lt;span style="color:#000">attr1&lt;/span> &lt;span style="color:#ce5c00;font-weight:bold">=&lt;/span> &lt;span style="color:#0000cf;font-weight:bold">0.002&lt;/span>&lt;span style="color:#000;font-weight:bold">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#000">Real&lt;/span> &lt;span style="color:#000">read2&lt;/span> &lt;span style="color:#ce5c00;font-weight:bold">=&lt;/span> &lt;span style="color:#ce5c00;font-weight:bold">**&lt;/span>&lt;span style="color:#000">attr2&lt;/span>&lt;span style="color:#000;font-weight:bold">;&lt;/span> &lt;span style="color:#8f5902;font-style:italic">//read2 = 0.002
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>When working with references between multiple dynamic attributes, the direction in which the references are defined can be important:
References should always be set in such a way that the reference relationships form a one-way chain. Only the last attribute in such a reference chain (which itself does not reference anything) should be modified by external code (i.e. through mutable references or the &lt;code>set&lt;/code>-method). This ensures that changes are always reflected in all attributes in the chain. For example, the following setup might lead to errors because it overwrites an existing reference:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-cpp" data-lang="cpp">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#8f5902;font-style:italic">// Overwriting an existing reference relationship
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#8f5902;font-style:italic">&lt;/span>&lt;span style="color:#000">AttributeBase&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">::&lt;/span>&lt;span style="color:#000">Ptr&lt;/span> &lt;span style="color:#000">A&lt;/span> &lt;span style="color:#ce5c00;font-weight:bold">=&lt;/span> &lt;span style="color:#000">AttributeDynamic&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">&amp;lt;&lt;/span>&lt;span style="color:#000">Real&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">&amp;gt;::&lt;/span>&lt;span style="color:#000">make&lt;/span>&lt;span style="color:#000;font-weight:bold">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#000">AttributeBase&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">::&lt;/span>&lt;span style="color:#000">Ptr&lt;/span> &lt;span style="color:#000">B&lt;/span> &lt;span style="color:#ce5c00;font-weight:bold">=&lt;/span> &lt;span style="color:#000">AttributeDynamic&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">&amp;lt;&lt;/span>&lt;span style="color:#000">Real&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">&amp;gt;::&lt;/span>&lt;span style="color:#000">make&lt;/span>&lt;span style="color:#000;font-weight:bold">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#000">AttributeBase&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">::&lt;/span>&lt;span style="color:#000">Ptr&lt;/span> &lt;span style="color:#000">C&lt;/span> &lt;span style="color:#ce5c00;font-weight:bold">=&lt;/span> &lt;span style="color:#000">AttributeDynamic&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">&amp;lt;&lt;/span>&lt;span style="color:#000">Real&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">&amp;gt;::&lt;/span>&lt;span style="color:#000">make&lt;/span>&lt;span style="color:#000;font-weight:bold">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#000">B&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">-&amp;gt;&lt;/span>&lt;span style="color:#000">setReference&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#000">A&lt;/span>&lt;span style="color:#000;font-weight:bold">);&lt;/span> &lt;span style="color:#8f5902;font-style:italic">// Current chain: B -&amp;gt; A
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#8f5902;font-style:italic">&lt;/span>&lt;span style="color:#000">B&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">-&amp;gt;&lt;/span>&lt;span style="color:#000">setReference&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#000">C&lt;/span>&lt;span style="color:#000;font-weight:bold">);&lt;/span> &lt;span style="color:#8f5902;font-style:italic">// Current chain: B -&amp;gt; C, reference on A is overwritten
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#8f5902;font-style:italic">&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#ce5c00;font-weight:bold">**&lt;/span>&lt;span style="color:#000">C&lt;/span> &lt;span style="color:#ce5c00;font-weight:bold">=&lt;/span> &lt;span style="color:#0000cf;font-weight:bold">0.1&lt;/span>&lt;span style="color:#000;font-weight:bold">;&lt;/span> &lt;span style="color:#8f5902;font-style:italic">// Change will not be reflected in A
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Correct implementation:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-cpp" data-lang="cpp">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#000">AttributeBase&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">::&lt;/span>&lt;span style="color:#000">Ptr&lt;/span> &lt;span style="color:#000">A&lt;/span> &lt;span style="color:#ce5c00;font-weight:bold">=&lt;/span> &lt;span style="color:#000">AttributeDynamic&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">&amp;lt;&lt;/span>&lt;span style="color:#000">Real&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">&amp;gt;::&lt;/span>&lt;span style="color:#000">make&lt;/span>&lt;span style="color:#000;font-weight:bold">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#000">AttributeBase&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">::&lt;/span>&lt;span style="color:#000">Ptr&lt;/span> &lt;span style="color:#000">B&lt;/span> &lt;span style="color:#ce5c00;font-weight:bold">=&lt;/span> &lt;span style="color:#000">AttributeDynamic&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">&amp;lt;&lt;/span>&lt;span style="color:#000">Real&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">&amp;gt;::&lt;/span>&lt;span style="color:#000">make&lt;/span>&lt;span style="color:#000;font-weight:bold">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#000">AttributeBase&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">::&lt;/span>&lt;span style="color:#000">Ptr&lt;/span> &lt;span style="color:#000">C&lt;/span> &lt;span style="color:#ce5c00;font-weight:bold">=&lt;/span> &lt;span style="color:#000">AttributeDynamic&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">&amp;lt;&lt;/span>&lt;span style="color:#000">Real&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">&amp;gt;::&lt;/span>&lt;span style="color:#000">make&lt;/span>&lt;span style="color:#000;font-weight:bold">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#000">B&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">-&amp;gt;&lt;/span>&lt;span style="color:#000">setReference&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#000">A&lt;/span>&lt;span style="color:#000;font-weight:bold">);&lt;/span> &lt;span style="color:#8f5902;font-style:italic">// Current chain: B -&amp;gt; A
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#8f5902;font-style:italic">&lt;/span>&lt;span style="color:#000">C&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">-&amp;gt;&lt;/span>&lt;span style="color:#000">setReference&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#000">B&lt;/span>&lt;span style="color:#000;font-weight:bold">);&lt;/span> &lt;span style="color:#8f5902;font-style:italic">// Current chain: C -&amp;gt; B -&amp;gt; A
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#8f5902;font-style:italic">&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#ce5c00;font-weight:bold">**&lt;/span>&lt;span style="color:#000">A&lt;/span> &lt;span style="color:#ce5c00;font-weight:bold">=&lt;/span> &lt;span style="color:#0000cf;font-weight:bold">0.1&lt;/span>&lt;span style="color:#000;font-weight:bold">;&lt;/span> &lt;span style="color:#8f5902;font-style:italic">// Updating the last attribute in the chain will update A, B, and C
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Aside from setting references, it is also possible to completely recompute a dynamic attribute&amp;rsquo;s value every time it is read. This can for example be used to create attributes which reference a single matrix coefficient of another attribute, or which represent the magnitude or phase of a complex attribute.
Dynamic attributes which depend on one other attribute in this way are also called &lt;strong>derived&lt;/strong> attributes, and they can be created by calling one
of the various &lt;code>derive...&lt;/code> methods on the original attribute:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-cpp" data-lang="cpp">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#000">AttributeBase&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">::&lt;/span>&lt;span style="color:#000">Ptr&lt;/span> &lt;span style="color:#000">attr1&lt;/span> &lt;span style="color:#ce5c00;font-weight:bold">=&lt;/span> &lt;span style="color:#000">AttributeStatic&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">&amp;lt;&lt;/span>&lt;span style="color:#000">Complex&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">&amp;gt;::&lt;/span>&lt;span style="color:#000">make&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#000">Complex&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#0000cf;font-weight:bold">3&lt;/span>&lt;span style="color:#000;font-weight:bold">,&lt;/span> &lt;span style="color:#0000cf;font-weight:bold">4&lt;/span>&lt;span style="color:#000;font-weight:bold">));&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#000">AttributeBase&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">::&lt;/span>&lt;span style="color:#000">Ptr&lt;/span> &lt;span style="color:#000">attr2&lt;/span> &lt;span style="color:#ce5c00;font-weight:bold">=&lt;/span> &lt;span style="color:#000">attr1&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">-&amp;gt;&lt;/span>&lt;span style="color:#000">deriveMag&lt;/span>&lt;span style="color:#000;font-weight:bold">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#000">Real&lt;/span> &lt;span style="color:#000">read1&lt;/span> &lt;span style="color:#ce5c00;font-weight:bold">=&lt;/span> &lt;span style="color:#ce5c00;font-weight:bold">**&lt;/span>&lt;span style="color:#000">attr2&lt;/span>&lt;span style="color:#000;font-weight:bold">;&lt;/span> &lt;span style="color:#8f5902;font-style:italic">// read1 = 5
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#8f5902;font-style:italic">&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">**&lt;/span>&lt;span style="color:#000">attr1&lt;/span> &lt;span style="color:#ce5c00;font-weight:bold">=&lt;/span> &lt;span style="color:#000">Complex&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#0000cf;font-weight:bold">1&lt;/span>&lt;span style="color:#000;font-weight:bold">,&lt;/span> &lt;span style="color:#0000cf;font-weight:bold">0&lt;/span>&lt;span style="color:#000;font-weight:bold">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#000">Real&lt;/span> &lt;span style="color:#000">read2&lt;/span> &lt;span style="color:#ce5c00;font-weight:bold">=&lt;/span> &lt;span style="color:#ce5c00;font-weight:bold">**&lt;/span>&lt;span style="color:#000">attr2&lt;/span>&lt;span style="color:#000;font-weight:bold">;&lt;/span> &lt;span style="color:#8f5902;font-style:italic">// read2 = 1
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>There is also a general &lt;code>derive&lt;/code>-method which can take a custom &lt;code>getter&lt;/code> and &lt;code>setter&lt;/code> lambda function for computing the derived attribute from its dependency.
For more complex cases involving dependencies on multiple attributes, the &lt;code>AttributeDynamic&lt;/code> class has a method called &lt;code>addTask&lt;/code> which can be used to add arbitrary computation tasks which are executed when the attribute is read or written to. For more information, check the method comments in &lt;code>Attribute.h&lt;/code>.&lt;/p>
&lt;h2 id="using-attributes-for-logging-and-interfacing">Using Attributes for Logging and Interfacing&lt;/h2>
&lt;p>When setting up a simulation, there are some methods which require an instance of &lt;code>AttributeBase::Ptr&lt;/code> as a parameter. Examples for this
are the logger methods (e.g. &lt;code>DataLogger::logAttribute&lt;/code>) and &lt;a href="https://sogno.energy/dpsim/docs/user-guide/co-simulation/">interface&lt;/a> methods (e.g. &lt;code>InterfaceVillas::exportAttribute&lt;/code>). To obtain the
required attribute pointer, one can either directly access the public member variables of the component the attribute belongs to, or use the component&amp;rsquo;s &lt;code>attribute(String name)&lt;/code> method which will look up the attribute in the component&amp;rsquo;s &lt;code>AttributeList&lt;/code>:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-cpp" data-lang="cpp">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#204a87;font-weight:bold">auto&lt;/span> &lt;span style="color:#000">r1&lt;/span> &lt;span style="color:#ce5c00;font-weight:bold">=&lt;/span> &lt;span style="color:#000">DP&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">::&lt;/span>&lt;span style="color:#000">Ph1&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">::&lt;/span>&lt;span style="color:#000">Resistor&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">::&lt;/span>&lt;span style="color:#000">make&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#4e9a06">&amp;#34;r_1&amp;#34;&lt;/span>&lt;span style="color:#000;font-weight:bold">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#000">r1&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">-&amp;gt;&lt;/span>&lt;span style="color:#000">setParameters&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#0000cf;font-weight:bold">5&lt;/span>&lt;span style="color:#000;font-weight:bold">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#204a87;font-weight:bold">auto&lt;/span> &lt;span style="color:#000">logger&lt;/span> &lt;span style="color:#ce5c00;font-weight:bold">=&lt;/span> &lt;span style="color:#000">DataLogger&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">::&lt;/span>&lt;span style="color:#000">make&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#4e9a06">&amp;#34;simName&amp;#34;&lt;/span>&lt;span style="color:#000;font-weight:bold">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#8f5902;font-style:italic">// Access the attribute through the member variable
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#8f5902;font-style:italic">&lt;/span>&lt;span style="color:#000">logger&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">-&amp;gt;&lt;/span>&lt;span style="color:#000">logAttribute&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#4e9a06">&amp;#34;i12&amp;#34;&lt;/span>&lt;span style="color:#000;font-weight:bold">,&lt;/span> &lt;span style="color:#000">r1&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">-&amp;gt;&lt;/span>&lt;span style="color:#000">mIntfCurrent&lt;/span>&lt;span style="color:#000;font-weight:bold">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#204a87;font-weight:bold">auto&lt;/span> &lt;span style="color:#000">intf&lt;/span> &lt;span style="color:#ce5c00;font-weight:bold">=&lt;/span> &lt;span style="color:#000">std&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">::&lt;/span>&lt;span style="color:#000">make_shared&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">&amp;lt;&lt;/span>&lt;span style="color:#000">InterfaceVillas&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">&amp;gt;&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#000">config&lt;/span>&lt;span style="color:#000;font-weight:bold">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#8f5902;font-style:italic">// Access the attribute through the AttributeList
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#8f5902;font-style:italic">&lt;/span>&lt;span style="color:#000">intf&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">-&amp;gt;&lt;/span>&lt;span style="color:#000">exportAttribute&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#000">r1&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">-&amp;gt;&lt;/span>&lt;span style="color:#000">attribute&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#a40000">&amp;#39;&lt;/span>&lt;span style="color:#000">i_intf&lt;/span>&lt;span style="color:#a40000">&amp;#39;&lt;/span>&lt;span style="color:#000;font-weight:bold">),&lt;/span> &lt;span style="color:#0000cf;font-weight:bold">0&lt;/span>&lt;span style="color:#000;font-weight:bold">,&lt;/span> &lt;span style="color:#204a87">true&lt;/span>&lt;span style="color:#000;font-weight:bold">,&lt;/span> &lt;span style="color:#204a87">true&lt;/span>&lt;span style="color:#000;font-weight:bold">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#8f5902;font-style:italic">// Access the attribute through the member variable and use deriveCoeff to convert it to a scalar value
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#8f5902;font-style:italic">&lt;/span>&lt;span style="color:#000">intf&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">-&amp;gt;&lt;/span>&lt;span style="color:#000">exportAttribute&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#000">r1&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">-&amp;gt;&lt;/span>&lt;span style="color:#000">mIntfVoltage&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">-&amp;gt;&lt;/span>&lt;span style="color:#000">deriveCoeff&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">&amp;lt;&lt;/span>&lt;span style="color:#000">Complex&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">&amp;gt;&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#0000cf;font-weight:bold">0&lt;/span>&lt;span style="color:#000;font-weight:bold">,&lt;/span> &lt;span style="color:#0000cf;font-weight:bold">0&lt;/span>&lt;span style="color:#000;font-weight:bold">),&lt;/span> &lt;span style="color:#0000cf;font-weight:bold">0&lt;/span>&lt;span style="color:#000;font-weight:bold">,&lt;/span> &lt;span style="color:#204a87">true&lt;/span>&lt;span style="color:#000;font-weight:bold">);&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>When creating a simulation in Python, the component&amp;rsquo;s member variables are usually not accessible, so the &lt;code>attr&lt;/code>-method has to be used for all accesses:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-python" data-lang="python">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#8f5902;font-style:italic"># dpsim-mqtt.py&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#000">intf&lt;/span> &lt;span style="color:#ce5c00;font-weight:bold">=&lt;/span> &lt;span style="color:#000">dpsimpyvillas&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">InterfaceVillas&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#000">name&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">=&lt;/span>&lt;span style="color:#4e9a06">&amp;#39;dpsim-mqtt&amp;#39;&lt;/span>&lt;span style="color:#000;font-weight:bold">,&lt;/span> &lt;span style="color:#000">config&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">=&lt;/span>&lt;span style="color:#000">mqtt_config&lt;/span>&lt;span style="color:#000;font-weight:bold">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#000">intf&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">import_attribute&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#000">evs&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">attr&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#4e9a06">&amp;#39;V_ref&amp;#39;&lt;/span>&lt;span style="color:#000;font-weight:bold">),&lt;/span> &lt;span style="color:#0000cf;font-weight:bold">0&lt;/span>&lt;span style="color:#000;font-weight:bold">,&lt;/span> &lt;span style="color:#204a87;font-weight:bold">True&lt;/span>&lt;span style="color:#000;font-weight:bold">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#000">intf&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">export_attribute&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#000">r12&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">attr&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#4e9a06">&amp;#39;i_intf&amp;#39;&lt;/span>&lt;span style="color:#000;font-weight:bold">)&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">derive_coeff&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#0000cf;font-weight:bold">0&lt;/span>&lt;span style="color:#000;font-weight:bold">,&lt;/span> &lt;span style="color:#0000cf;font-weight:bold">0&lt;/span>&lt;span style="color:#000;font-weight:bold">),&lt;/span> &lt;span style="color:#0000cf;font-weight:bold">0&lt;/span>&lt;span style="color:#000;font-weight:bold">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="using-attributes-to-schedule-tasks">Using Attributes to Schedule Tasks&lt;/h2>
&lt;p>Attributes are also used to determine dependencies of tasks on data, which is information required by the scheduler.
For the usual &lt;code>MNAPreStep&lt;/code> and &lt;code>MNAPostStep&lt;/code> tasks, these dependencies are configured in the &lt;code>mnaAddPreStepDependencies&lt;/code> and &lt;code>mnaAddPostStepDependencies&lt;/code> methods:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-cpp" data-lang="cpp">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#204a87;font-weight:bold">void&lt;/span> &lt;span style="color:#000">DP&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">::&lt;/span>&lt;span style="color:#000">Ph1&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">::&lt;/span>&lt;span style="color:#000">Inductor&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">::&lt;/span>&lt;span style="color:#000">mnaAddPostStepDependencies&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#000">AttributeBase&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">::&lt;/span>&lt;span style="color:#000">List&lt;/span> &lt;span style="color:#ce5c00;font-weight:bold">&amp;amp;&lt;/span>&lt;span style="color:#000">prevStepDependencies&lt;/span>&lt;span style="color:#000;font-weight:bold">,&lt;/span> &lt;span style="color:#000">AttributeBase&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">::&lt;/span>&lt;span style="color:#000">List&lt;/span> &lt;span style="color:#ce5c00;font-weight:bold">&amp;amp;&lt;/span>&lt;span style="color:#000">attributeDependencies&lt;/span>&lt;span style="color:#000;font-weight:bold">,&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#000">AttributeBase&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">::&lt;/span>&lt;span style="color:#000">List&lt;/span> &lt;span style="color:#ce5c00;font-weight:bold">&amp;amp;&lt;/span>&lt;span style="color:#000">modifiedAttributes&lt;/span>&lt;span style="color:#000;font-weight:bold">,&lt;/span> &lt;span style="color:#000">Attribute&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">&amp;lt;&lt;/span>&lt;span style="color:#000">Matrix&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">&amp;gt;::&lt;/span>&lt;span style="color:#000">Ptr&lt;/span> &lt;span style="color:#ce5c00;font-weight:bold">&amp;amp;&lt;/span>&lt;span style="color:#000">leftVector&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#000;font-weight:bold">)&lt;/span> &lt;span style="color:#000;font-weight:bold">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#000">attributeDependencies&lt;/span>&lt;span style="color:#000;font-weight:bold">.&lt;/span>&lt;span style="color:#000">push_back&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#000">leftVector&lt;/span>&lt;span style="color:#000;font-weight:bold">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#000">modifiedAttributes&lt;/span>&lt;span style="color:#000;font-weight:bold">.&lt;/span>&lt;span style="color:#000">push_back&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#000">mIntfVoltage&lt;/span>&lt;span style="color:#000;font-weight:bold">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#000">modifiedAttributes&lt;/span>&lt;span style="color:#000;font-weight:bold">.&lt;/span>&lt;span style="color:#000">push_back&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#000">mIntfCurrent&lt;/span>&lt;span style="color:#000;font-weight:bold">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#000;font-weight:bold">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Here, the MNA post step depends on the solution vector of the system, &lt;code>leftVector&lt;/code>, and modifies &lt;code>mIntfVoltage&lt;/code> and &lt;code>mIntfCurrent&lt;/code>.
Therefore, this task needs to be scheduled after the system solution that computes &lt;code>leftVector&lt;/code> and before tasks that require the voltage and current interface vectors of the inductance, e.g. the task logging these values.&lt;/p></description></item><item><title>Docs: Powerflow</title><link>https://sogno.energy/dpsim/docs/concepts/powerflow/</link><pubDate>Wed, 18 Mar 2020 00:00:00 +0000</pubDate><guid>https://sogno.energy/dpsim/docs/concepts/powerflow/</guid><description>
&lt;p>The power flow problem is about the calculation of voltage magnitudes and angles for one set of buses.
The solution is obtained from a given set of voltage magnitudes and power levels for a specific model of the network configuration.
The power flow solution exhibits the voltages and angles at all buses and real and reactive flows can be deduced from the same.&lt;/p>
&lt;h2 id="power-system-model">Power System Model&lt;/h2>
&lt;p>Power systems are modeled as a network of buses (nodes) and branches (lines).
To a network bus, components such a generator, load, and transmission substation can be connected.
Each bus in the network is fully described by the following four electrical quantities:&lt;/p>
&lt;ul>
&lt;li>$\vert V_{k} \vert$: the voltage magnitude&lt;/li>
&lt;li>$\theta_{k}$: the voltage phase angle&lt;/li>
&lt;li>$P_{k}$: the active power&lt;/li>
&lt;li>$Q_{k}$: the reactive power&lt;/li>
&lt;/ul>
&lt;p>There are three types of networks buses: VD bus, PV bus and PQ bus.
Depending on the type of the bus, two of the four electrical quantities are specified as shown in the table below.&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Bus Type&lt;/th>
&lt;th>Known&lt;/th>
&lt;th>Unknown&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>$VD$&lt;/td>
&lt;td>$\vert V_{k} \vert, \theta_{k}$&lt;/td>
&lt;td>$P_{k}, Q_{k}$&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>$PV$&lt;/td>
&lt;td>$P_{k}, \vert V_{k} \vert$&lt;/td>
&lt;td>$Q_{k}, \theta_{k}$&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>$PQ$&lt;/td>
&lt;td>$P_{k}, Q_{k}$&lt;/td>
&lt;td>$\vert V_{k} \vert, \theta_{k}$&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;h2 id="single-phase-power-flow-problem">Single Phase Power Flow Problem&lt;/h2>
&lt;p>The power flow problem can be expressed by the goal to bring a mismatch function $\vec{f}$ to zero.
The value of the mismatch function depends on a solution vector $\vec{x}$:&lt;/p>
&lt;div class="math">$$\vec{f}(\vec{x}) = 0$$&lt;/div>&lt;p>As $\vec{f}(\vec{x})$ will be nonlinear, the equation system will be solved with Newton-Raphson:&lt;/p>
&lt;div class="math">$$-\textbf{J}(\vec{x}) \Delta \vec{x} = \vec{f} (\vec{x})$$&lt;/div>&lt;p>where $\Delta \vec{x}$ is the correction of the solution vector and $\textbf{J}(\vec{x})$ is the Jacobian matrix.
The solution vector $\vec{x}$ represents the voltage $\vec{V}$ by polar or cartesian quantities.
The mismatch function $\vec{f}$ will either represent the power mismatch $\Delta \vec{S}$ in terms of&lt;/p>
&lt;div class="math">$$\left [ \begin{array}{c} \Delta \vec{P} \\ \Delta \vec{Q} \end{array} \right ]$$&lt;/div>&lt;p>or the current mismatch $\Delta \vec{I}$ in terms of&lt;/p>
&lt;div class="math">$$\left [ \begin{array}{c} \Delta \vec{I_{real}} \\ \Delta \vec{I_{imag}} \end{array} \right ]$$&lt;/div>&lt;p>where the vectors split the complex quantities into real and imaginary parts.
Futhermore, the solution vector $\vec{x}$ will represent $\vec{V}$ either by polar coordinates&lt;/p>
&lt;div class="math">$$\left [ \begin{array}{c} \vec{\delta} \\ \vert \vec{V} \vert \end{array} \right ]$$&lt;/div>&lt;p>or rectangular coordinates&lt;/p>
&lt;div class="math">$$\left [ \begin{array}{c} \vec{V_{real}} \\ \vec{V_{imag}} \end{array} \right ]$$&lt;/div>&lt;p>This results in four different formulations of the powerflow problem:&lt;/p>
&lt;ul>
&lt;li>with power mismatch function and polar coordinates&lt;/li>
&lt;li>with power mismatch function and rectangular coordinates&lt;/li>
&lt;li>with current mismatch function and polar coordinates&lt;/li>
&lt;li>with current mismatch function and rectangular coordinates&lt;/li>
&lt;/ul>
&lt;p>To solve the problem using NR, we need to formulate $\textbf{J} (\vec{x})$ and $\vec{f} (\vec{x})$ for each powerflow problem formulation.&lt;/p>
&lt;p>Of these four, DPsim currently implements the &lt;strong>power mismatch function with polar
coordinates&lt;/strong>, detailed below. It is the formulation used by both the dense
(&lt;code>PFSolverPowerPolar&lt;/code>) and sparse (&lt;code>PFSolverPowerPolarSparse&lt;/code>) solvers; the other three
are not implemented.&lt;/p>
&lt;h3 id="powerflow-problem-with-power-mismatch-function-and-polar-coordinates">Powerflow Problem with Power Mismatch Function and Polar Coordinates&lt;/h3>
&lt;h4 id="formulation-of-mismatch-function">Formulation of Mismatch Function&lt;/h4>
&lt;p>The injected power at a node $k$ is given by:&lt;/p>
&lt;div class="math">$$S_{k} = V_{k} I _{k}^{*}$$&lt;/div>&lt;p>The current injection into any bus $k$ may be expressed as:&lt;/p>
&lt;div class="math">$$I_{k} = \sum_{j=1}^{N} Y_{kj} V_{j}$$&lt;/div>&lt;p>Substitution yields:&lt;/p>
&lt;div class="math">$$\begin{align}
S_{k} &amp;= V_{k} \left ( \sum_{j=1}^{N} Y_{kj} V_{j} \right )^{*} \nonumber \\
&amp;= V_{k} \sum_{j=1}^{N} Y_{kj}^{*} V_{j} ^{*} \nonumber
\end{align}$$&lt;/div>&lt;p>We may define $G_{kj}$ and $B_{kj}$ as the real and imaginary parts of the admittance matrix element $Y_{kj}$ respectively, so that $Y_{kj} = G_{kj} + jB_{kj}$.
Then we may rewrite the last equation:&lt;/p>
&lt;div class="math">$$\begin{align}
S_{k} &amp;= V_{k} \sum_{j=1}^{N} Y_{kj}^{*} V_{j}^{*} \nonumber \\
&amp;= \vert V_{k} \vert \angle \theta_{k} \sum_{j=1}^{N} (G_{kj} + jB_{kj})^{*} ( \vert V_{j} \vert \angle \theta_{j})^{*} \nonumber \\
&amp;= \vert V_{k} \vert \angle \theta_{k} \sum_{j=1}^{N} (G_{kj} - jB_{kj}) ( \vert V_{j} \vert \angle - \theta_{j}) \nonumber \\
&amp;= \sum_{j=1} ^{N} \vert V_{k} \vert \angle \theta_{k} ( \vert V_{j} \vert \angle - \theta_{j}) (G_{kj} - jB_{kj}) \nonumber \\
&amp;= \sum_{j=1} ^{N} \left ( \vert V_{k} \vert \vert V_{j} \vert \angle (\theta_{k} - \theta_{j}) \right ) (G_{kj} - jB_{kj}) \nonumber \\
&amp;= \sum_{j=1} ^{N} \vert V_{k} \vert \vert V_{j} \vert \left ( cos(\theta_{k} - \theta_{j}) + jsin(\theta_{k} - \theta_{j}) \right ) (G_{kj} - jB_{kj})
\end{align}$$&lt;/div>&lt;p>If we now perform the algebraic multiplication of the two terms inside the parentheses, and collect real and imaginary parts, and recall that $S_{k} = P_{k} + jQ_{k}$, we can express (1) as two equations: one for the real part, $P_{k}$, and one for the imaginary part, $Q_{k}$, according to:&lt;/p>
&lt;div class="math">$$\begin{align}
{P}_{k} = \sum_{j=1}^{N} \vert V_{k} \vert \vert V_{j} \vert \left ( G_{kj}cos(\theta_{k} - \theta_{j}) + B_{kj} sin(\theta_{k} - \theta_{j}) \right ) \\
{Q}_{k} = \sum_{j=1}^{N} \vert V_{k} \vert \vert V_{j} \vert \left ( G_{kj}sin(\theta_{k} - \theta_{j}) - B_{kj} cos(\theta_{k} - \theta_{j}) \right )
\end{align}$$&lt;/div>&lt;p>These equations are called the power flow equations, and they form the fundamental building block from which we solve the power flow problem.&lt;/p>
&lt;p>We consider a power system network having $N$ buses. We assume one VD bus, $N_{PV}-1$ PV buses and $N-N_{PV}$ PQ buses.
We assume that the VD bus is numbered bus $1$, the PV buses are numbered $2,&amp;hellip;,N_{PV}$, and the PQ buses are numbered $N_{PV}+1,&amp;hellip;,N$.
We define the vector of unknown as the composite vector of unknown angles $\vec{\theta}$ and voltage magnitudes $\vert \vec{V} \vert$:&lt;/p>
&lt;div class="math">$$\begin{align}
\vec{x} = \left[ \begin{array}{c} \vec{\theta} \\ \vert \vec{V} \vert \\ \end{array} \right ]
= \left[ \begin{array}{c} \theta_{2} \\ \theta_{3} \\ \vdots \\ \theta_{N} \\ \vert V_{N_{PV+1}} \vert \\ \vert V_{N_{PV+2}} \vert \\ \vdots \\ \vert V_{N} \vert \end{array} \right]
\end{align}$$&lt;/div>&lt;p>The right-hand sides of equations (2) and (3) depend on the elements of the unknown vector $\vec{x}$.
Expressing this dependency more explicitly, we rewrite these equations as:&lt;/p>
&lt;div class="math">$$\begin{align}
P_{k} = P_{k} (\vec{x}) \Rightarrow P_{k}(\vec{x}) - P_{k} &amp;= 0 \quad \quad k = 2,...,N \\
Q_{k} = Q_{k} (\vec{x}) \Rightarrow Q_{k} (\vec{x}) - Q_{k} &amp;= 0 \quad \quad k = N_{PV}+1,...,N
\end{align}$$&lt;/div>&lt;p>We now define the mismatch vector $\vec{f} (\vec{x})$ as:&lt;/p>
&lt;div class="math">$$\begin{align}
\vec{f} (\vec{x}) = \left [ \begin{array}{c} f_{1}(\vec{x}) \\ \vdots \\ f_{N-1}(\vec{x}) \\ ------ \\ f_{N}(\vec{x}) \\ \vdots \\ f_{2N-N_{PV} -1}(\vec{x}) \end{array} \right ]
= \left [ \begin{array}{c} P_{2}(\vec{x}) - P_{2} \\ \vdots \\ P_{N}(\vec{x}) - P_{N} \\ --------- \\ Q_{N_{PV}+1}(\vec{x}) - Q_{N_{PV}+1} \\ \vdots \\ Q_{N}(\vec{x}) - Q_{N} \end{array} \right]
= \left [ \begin{array}{c} \Delta P_{2} \\ \vdots \\ \Delta P_{N} \\ ------ \\ \Delta Q_{N_{PV}+1} \\ \vdots \\ \Delta Q_{N} \end{array} \right ]
= \vec{0}
\end{align}$$&lt;/div>&lt;p>That is a system of nonlinear equations.
This nonlinearity comes from the fact that $P_{k}$ and $Q_{k}$ have terms containing products of some of the unknowns and also terms containing trigonometric functions of some the unknowns.&lt;/p>
&lt;h4 id="formulation-of-jacobian">Formulation of Jacobian&lt;/h4>
&lt;p>As discussed in the previous section, the power flow problem will be solved using the Newton-Raphson method. Here, the Jacobian matrix is obtained by taking all first-order partial derivates of the power mismatch functions with respect to the voltage angles $\theta_{k}$ and magnitudes $\vert V_{k} \vert$ as:&lt;/p>
&lt;div class="math">$$\begin{align}
J_{jk}^{P \theta} &amp;= \frac{\partial P_{j} (\vec{x} ) } {\partial \theta_{k}} = \vert V_{j} \vert \vert V_{k} \vert \left ( G_{jk} sin(\theta_{j} - \theta_{k}) - B_{jk} cos(\theta_{j} - \theta_{k} ) \right ) \\
J_{jj}^{P \theta} &amp;= \frac{\partial P_{j}(\vec{x})}{\partial \theta_{j}} = -Q_{j} (\vec{x} ) - B_{jj} \vert V_{j} \vert ^{2} \\
J_{jk}^{Q \theta} &amp;= \frac{\partial Q_{j}(\vec{x})}{\partial \theta_{k}} = - \vert V_{j} \vert \vert V_{k} \vert \left ( G_{jk} cos(\theta_{j} - \theta_{k}) + B_{jk} sin(\theta_{j} - \theta_{k}) \right ) \\
J_{jj}^{Q \theta} &amp;= \frac{\partial Q_{j}(\vec{x})}{\partial \theta_{k}} = P_{j} (\vec{x} ) - G_{jj} \vert V_{j} \vert ^{2} \\
J_{jk}^{PV} &amp;= \frac{\partial P_{j} (\vec{x} ) } {\partial \vert V_{k} \vert } = \vert V_{j} \vert \left ( G_{jk} cos(\theta_{j} - \theta_{k}) + B_{jk} sin(\theta_{j} - \theta_{k}) \right ) \\
J_{jj}^{PV} &amp;= \frac{\partial P_{j}(\vec{x})}{\partial \vert V_{j} \vert } = \frac{P_{j} (\vec{x} )}{\vert V_{j} \vert} + G_{jj} \vert V_{j} \vert \\
J_{jk}^{QV} &amp;= \frac{\partial Q_{j} (\vec{x} ) } {\partial \vert V_{k} \vert } = \vert V_{j} \vert \left ( G_{jk} sin(\theta_{j} - \theta_{k}) + B_{jk} cos(\theta_{j} - \theta_{k}) \right ) \\
J_{jj}^{QV} &amp;= \frac{\partial Q_{j}(\vec{x})}{\partial \vert V_{j} \vert } = \frac{Q_{j} (\vec{x} )}{\vert V_{j} \vert} - B_{jj} \vert V_{j} \vert \\
\end{align}$$&lt;/div>&lt;p>The formulas above use the voltage magnitude $\vert V_k \vert$ as the unknown. The DPsim
implementation instead uses the &lt;em>relative&lt;/em> voltage increment $\Delta \vert V_k \vert / \vert V_k \vert$,
which scales every voltage-magnitude column ($J^{PV}$, $J^{QV}$) by $\vert V_k \vert$. For
example $J_{jj}^{PV}$ becomes $P_j(\vec{x}) + G_{jj} \vert V_j \vert^2$. This is paired with
the multiplicative voltage update $\vert V_k \vert \leftarrow \vert V_k \vert (1 + \Delta \vert V_k \vert / \vert V_k \vert)$,
so the solution is identical; only the scaling of the voltage columns differs.&lt;/p>
&lt;p>The linear system of equations that is solved in every Newton iteration can be written in matrix form as follows:&lt;/p>
&lt;div class="math">$$\begin{align}
-\left [ \begin{array}{cccccc}
\frac{\partial \Delta P_{2} }{\partial \theta_{2}} &amp; \cdots &amp; \frac{\partial \Delta P_{2} }{\partial \theta_{N}} &amp;
\frac{\partial \Delta P_{2} }{\partial \vert V_{N_{G+1}} \vert} &amp; \cdots &amp; \frac{\partial \Delta P_{2} }{\partial \vert V_{N} \vert} \\
\vdots &amp; \ddots &amp; \vdots &amp; \vdots &amp; \ddots &amp; \vdots \\
\frac{\partial \Delta P_{N} }{\partial \theta_{2}} &amp; \cdots &amp; \frac{\partial \Delta P_{N}}{\partial \theta_{N}} &amp;
\frac{\partial \Delta P_{N}}{\partial \vert V_{N_{G+1}} \vert } &amp; \cdots &amp; \frac{\partial \Delta P_{N}}{\partial \vert V_{N} \vert} \\
\frac{\partial \Delta Q_{N_{G+1}} }{\partial \theta_{2}} &amp; \cdots &amp; \frac{\partial \Delta Q_{N_{G+1}} }{\partial \theta_{N}} &amp;
\frac{\partial \Delta Q_{N_{G+1}} }{\partial \vert V_{N_{G+1}} \vert } &amp; \cdots &amp; \frac{\partial \Delta Q_{N_{G+1}} }{\partial \vert V_{N} \vert} \\
\vdots &amp; \ddots &amp; \vdots &amp; \vdots &amp; \ddots &amp; \vdots \\
\frac{\partial \Delta Q_{N}}{\partial \theta_{2}} &amp; \cdots &amp; \frac{\partial \Delta Q_{N}}{\partial \theta_{N}} &amp;
\frac{\partial \Delta Q_{N}}{\partial \vert V_{N_{G+1}} \vert } &amp; \cdots &amp; \frac{\partial \Delta Q_{N}}{\partial \vert V_{N} \vert}
\end{array} \right ]
\left [ \begin{array}{c} \Delta \theta_{2} \\ \vdots \\ \Delta \theta_{N} \\ \Delta \vert V_{N_{G+1}} \vert \\ \vdots \\ \Delta \vert V_{N} \vert \end{array} \right ]
= \left [ \begin{array}{c} \Delta P_{2} \\ \vdots \\ \Delta P_{N} \\ \Delta Q_{N_{G+1}} \\ \vdots \\ \Delta Q_{N} \end{array} \right ]
\end{align}$$&lt;/div>&lt;h3 id="solution-of-the-problem">Solution of the Problem&lt;/h3>
&lt;p>The solution update formula is given by:&lt;/p>
&lt;div class="math">$$\begin{align}
\vec{x}^{(i+1)} = \vec{x}^{(i)} + \Delta \vec{x}^{(i)} = \vec{x}^{(i)} - \textbf{J}^{-1} \vec{f} (\vec{x}^{(i)})
\end{align}$$&lt;/div>&lt;p>To sum up, the NR algorithm, for application to the power flow problem is:&lt;/p>
&lt;ol>
&lt;li>Set the iteration counter to $i=1$. Use the initial solution $V_{i} = 1 \angle 0^{\circ}$&lt;/li>
&lt;li>Compute the mismatch vector $\vec{f}({\vec{x}})$ using the power flow equations&lt;/li>
&lt;li>Perform the following stopping criterion tests:
&lt;ul>
&lt;li>If $\vert \Delta P_{i} \vert &amp;lt; \epsilon_{P}$ for all type PQ and PV buses and&lt;/li>
&lt;li>If $\vert \Delta Q_{i} \vert &amp;lt; \epsilon_{Q}$ for all type PQ&lt;/li>
&lt;li>Then go to step 6&lt;/li>
&lt;li>Otherwise, go to step 4.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>Evaluate the Jacobian matrix $\textbf{J}^{(i)}$ and compute $\Delta \vec{x}^{(i)}$.&lt;/li>
&lt;li>Compute the update solution vector $\vec{x}^{(i+1)}$. Return to step 3.&lt;/li>
&lt;li>Stop.&lt;/li>
&lt;/ol>
&lt;h3 id="convergence-and-step-control">Convergence and Step Control&lt;/h3>
&lt;p>The iteration is governed by two parameters:&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Tolerance&lt;/strong> (default $10^{-8}$): the run is converged once every entry of the
mismatch vector satisfies $\vert f_{i}(\vec{x}) \vert &amp;lt;$ tolerance (an infinity-norm
test over all $\Delta P$ and $\Delta Q$ components).&lt;/li>
&lt;li>&lt;strong>Maximum iterations&lt;/strong> (default $20$): an upper bound on the number of Newton steps
per power flow solve.&lt;/li>
&lt;/ul>
&lt;p>To improve robustness far from the solution, the full Newton step is &lt;strong>scaled by a
single factor&lt;/strong> $\alpha \in (0, 1]$ rather than damped component-wise. The factor is the
largest value that keeps the per-step changes within fixed bounds:&lt;/p>
&lt;div class="math">$$\alpha = \min \left( 1,\ \frac{\Delta\theta_{max}}{\max_k \vert \Delta\theta_k \vert},\ \frac{\Delta V_{max}}{\max_k \vert \Delta V_k / V_k \vert} \right)$$&lt;/div>&lt;p>with $\Delta\theta_{max} = 0.2\ \text{rad}$ and $\Delta V_{max} = 0.1\ \text{pu}$. Because
the whole step is scaled by one factor, the Newton search direction is preserved, so
$\alpha = 1$ near the solution and quadratic convergence is retained; $\alpha &amp;lt; 1$ only
bounds large early steps. Voltage magnitudes are updated multiplicatively
($V_k \leftarrow V_k (1 + \alpha, \Delta V_k / V_k)$), consistent with the relative
voltage increment used in the Jacobian.&lt;/p>
&lt;p>The formulation above is independent of DPsim. For the solvers it actually ships, their
convergence controls and the reactive power limit handling, see
&lt;a href="https://sogno.energy/dpsim/docs/developer-guide/solvers/powerflow-solvers/">power flow solvers&lt;/a>.&lt;/p></description></item><item><title>Docs: Coding Conventions</title><link>https://sogno.energy/dpsim/docs/developer-guide/architecture-and-conventions/conventions/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://sogno.energy/dpsim/docs/developer-guide/architecture-and-conventions/conventions/</guid><description>
&lt;p>Conventions that apply across the codebase. For the process of getting a change merged, see
&lt;a href="https://sogno.energy/dpsim/docs/contributing/">contributing&lt;/a>.&lt;/p>
&lt;p>This is a summary of general guidelines for the development of DPsim.&lt;/p>
&lt;h2 id="scaling-of-voltages-and-currents">Scaling of Voltages and Currents&lt;/h2>
&lt;p>Voltage quantities are expressed either as phase-to-phase RMS values (denominated as &lt;code>RMS3PH&lt;/code>) or as phase-to-ground peak values (denominated as &lt;code>PEAK1PH&lt;/code>):&lt;/p>
&lt;ul>
&lt;li>Initialisation quantities (e.g. &lt;code>initialSingleVoltage&lt;/code> of &lt;code>SimPowerComp&lt;/code>) as &lt;code>RMS3PH&lt;/code> values&lt;/li>
&lt;li>Simulation quantities in both &lt;code>SP&lt;/code> and &lt;code>DP&lt;/code> domain (e.g. &lt;code>mIntfVoltage&lt;/code> of &lt;code>DP::Ph1::PiLine&lt;/code>) as &lt;code>RMS3PH values&lt;/code>&lt;/li>
&lt;li>Simulation quantities in the &lt;code>EMT&lt;/code> domain (e.g. &lt;code>mIntfVoltage&lt;/code> of &lt;code>EMT::Ph3::Transformer&lt;/code>) as &lt;code>PEAK1PH&lt;/code> values&lt;/li>
&lt;/ul>
&lt;p>Current quantities are expressed either as &lt;code>RMS&lt;/code> or as &lt;code>PEAK&lt;/code> values:&lt;/p>
&lt;ul>
&lt;li>Simulation quantities in both &lt;code>SP&lt;/code> and &lt;code>DP&lt;/code> domain (e.g. &lt;code>mIntfCurrent&lt;/code> of &lt;code>DP::Ph1::PiLine&lt;/code>) as &lt;code>RMS&lt;/code> values&lt;/li>
&lt;li>Simulation quantities in the &lt;code>EMT&lt;/code> domain (e.g. &lt;code>mIntfCurrent&lt;/code> of &lt;code>EMT::Ph3::Transformer&lt;/code>) as &lt;code>PEAK&lt;/code> values&lt;/li>
&lt;/ul>
&lt;h2 id="logging">Logging&lt;/h2>
&lt;p>Debug or trace should be the default log level for information that might be nice to have but not necessary for every simulation case.&lt;/p>
&lt;p>Calls to the logger that might occur during simulation must use spdlog macros, like &lt;code>SPDLOG_LOGGER_INFO&lt;/code>.&lt;/p></description></item><item><title>Docs: DP Ph3 Averaged Voltage Source Inverter</title><link>https://sogno.energy/dpsim/docs/concepts/models/power-electronics/dp-ph3-averaged-vsi/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://sogno.energy/dpsim/docs/concepts/models/power-electronics/dp-ph3-averaged-vsi/</guid><description>
&lt;p>This model extends the single-phase grid-following averaged inverter to the three-phase dynamic-phasor (DP) domain.
Each phase of the LC filter is represented by an independent complex envelope, $V_{c,a/b/c}$ and $I_{f,a/b/c}$, in contrast to the single positive-sequence envelope of the single-phase model, whereas the controller retains a single positive-sequence $dq$ frame shared by the PLL, the power filter, and the outer and inner control loops.
As in the single-phase case, the control states are baseband quantities and remain real-valued; only the six per-phase filter envelopes are carrier-band quantities, and each carries the $-j\omega_n$ frequency shift introduced in &lt;a href="https://sogno.energy/dpsim/docs/concepts/state-space-nodal/">State-Space Nodal&lt;/a>.&lt;/p>
&lt;p>The terminal input is the PCC voltage envelope of the three phases,&lt;/p>
&lt;div class="math">$$u = \begin{bmatrix} U_a &amp; U_b &amp; U_c \end{bmatrix}^\top ,$$&lt;/div>&lt;p>and the state vector concatenates the 6 complex per-phase envelopes ahead of the 8 real control states, keeping the carrier-band and baseband blocks separate,&lt;/p>
&lt;div class="math">$$\mathbf{x} =
\big[\,
V_{c,a} \;\;
V_{c,b} \;\;
V_{c,c} \;\;
I_{f,a} \;\;
I_{f,b} \;\;
I_{f,c} \;\;
\psi \;\;
\phi_{\mathrm{PLL}} \;\;
P \;\;
Q \;\;
\phi_d \;\;
\phi_q \;\;
\gamma_d \;\;
\gamma_q
\,\big]^\top ,$$&lt;/div>&lt;p>where $\psi := \theta_{\mathrm{PLL}} - \omega_n t$ again denotes the deviation of the PLL angle from the nominal carrier phase, retained as a state to preserve relinearization accuracy. Each per-phase envelope contributes its real and imaginary parts to the packed real vector, yielding 20 real states in total, or 22 with the optional negative-sequence loop described below.&lt;/p>
&lt;p>The model output is the per-phase interface current injected into the MNA system,&lt;/p>
&lt;div class="math">$$y_p = \frac{U_p - V_{c,p}}{R_c}, \qquad p \in \{a, b, c\}.$$&lt;/div>&lt;h2 id="model-equations">Model equations&lt;/h2>
&lt;p>The main extension relative to &lt;code>DP::Ph1&lt;/code> is the per-phase projection onto, and redistribution from, the single positive-sequence $dq$ control frame.
The three capacitor-voltage envelopes are projected onto a single positive-sequence phasor,&lt;/p>
&lt;div class="math">$$\underline{V}_c = V_{c,a} + a\, V_{c,b} + a^2 V_{c,c},
\qquad a = e^{\,j 2\pi/3},$$&lt;/div>&lt;p>and the PCC input $\underline{U}$ is projected identically, so that the coupling-current envelope seen by the controller is $\underline{I}_{rc} = (\underline{V}_c - \underline{U})/R_c$, with positive current again denoting injection from the inverter into the grid.
The $dq$ quantities are obtained by rotating the projected envelopes by $\psi$,&lt;/p>
&lt;div class="math">$$V_{c,dq} = \tfrac{1}{2}\sqrt{\tfrac{2}{3}}\, e^{-j\psi}\, \underline{V}_c,
\qquad
I_{rc,dq} = \tfrac{1}{2}\sqrt{\tfrac{2}{3}}\, e^{-j\psi}\, \underline{I}_{rc},$$&lt;/div>&lt;p>with $v_{c,d} = \operatorname{Re}{V_{c,dq}}$, $v_{c,q} = \operatorname{Im}{V_{c,dq}}$, and analogously for $i_{rc,d}$ and $i_{rc,q}$.
Taken together, the $1\times 3$ projection, the scalar $dq$ rotation, and the $3\times 1$ redistribution defined below constitute a rank-one $3\times 3$ Park mapping on the envelope triple, which reduces to the single-envelope relation of &lt;code>DP::Ph1&lt;/code> under balanced operation.&lt;/p>
&lt;p>The positive-sequence active and reactive power measurements used by the controller are&lt;/p>
&lt;div class="math">$$p = v_{c,d} i_{rc,d} + v_{c,q} i_{rc,q},
\qquad
q = -v_{c,d} i_{rc,q} + v_{c,q} i_{rc,d},$$&lt;/div>&lt;p>with the projection scaling chosen so that $p$ and $q$ match the total three-phase active and reactive powers under balanced operation; under unbalanced operation they are the positive-sequence components seen by the single-frame controller.&lt;/p>
&lt;p>The control chain from the PLL through the inner current loop is identical in form to that of &lt;code>DP::Ph1&lt;/code> and operates on the single positive-sequence $dq$ pair. The PLL and power-filter dynamics read&lt;/p>
&lt;div class="math">$$\dot{\psi}
=
K_{p,\mathrm{PLL}} v_{c,q} +
K_{i,\mathrm{PLL}} \phi_{\mathrm{PLL}},
\qquad
\dot{\phi}_{\mathrm{PLL}} = v_{c,q},$$&lt;/div>
&lt;div class="math">$$\dot{P} = \omega_c(p - P),
\qquad
\dot{Q} = \omega_c(q - Q).$$&lt;/div>&lt;p>The outer power-control integrators and current references are&lt;/p>
&lt;div class="math">$$\dot{\phi}_d = P_{\mathrm{ref}} - P,
\qquad
\dot{\phi}_q = Q - Q_{\mathrm{ref}},$$&lt;/div>
&lt;div class="math">$$i_{d,\mathrm{ref}}
=
K_{p,P}(P_{\mathrm{ref}} - P) + K_{i,P}\phi_d,
\qquad
i_{q,\mathrm{ref}}
=
K_{p,P}(Q - Q_{\mathrm{ref}}) + K_{i,P}\phi_q,$$&lt;/div>&lt;p>and the inner current-control integrators and voltage references are&lt;/p>
&lt;div class="math">$$\dot{\gamma}_d = i_{d,\mathrm{ref}} - i_{rc,d},
\qquad
\dot{\gamma}_q = i_{q,\mathrm{ref}} - i_{rc,q},$$&lt;/div>
&lt;div class="math">$$v_{d,\mathrm{ref}}
=
K_{p,I}(i_{d,\mathrm{ref}} - i_{rc,d}) +
K_{i,I}\gamma_d,
\qquad
v_{q,\mathrm{ref}}
=
K_{p,I}(i_{q,\mathrm{ref}} - i_{rc,q}) +
K_{i,I}\gamma_q.$$&lt;/div>&lt;p>The single $dq$ voltage reference $V_{\mathrm{ref},dq} = v_{d,\mathrm{ref}} + j v_{q,\mathrm{ref}}$ is redistributed to the per-phase bridge-voltage envelopes through the inverse projection,&lt;/p>
&lt;div class="math">$$V_{\mathrm{ref},p} = \bar{a}_p \sqrt{\tfrac{2}{3}}\, V_{\mathrm{ref},dq}\, e^{j\psi},
\qquad
\bar{a}_{a/b/c} = \{1,\; a^2,\; a\},$$&lt;/div>&lt;p>so that all three phases are driven by the same positive-sequence command.&lt;/p>
&lt;p>The LC-filter dynamics are decoupled per phase within the plant and carry the carrier shift of the envelope explicitly,&lt;/p>
&lt;div class="math">$$\dot{V}_{c,p}
=
\frac{1}{C_f} I_{f,p}
+
\frac{1}{C_f R_c}(U_p - V_{c,p})
- j\omega_n V_{c,p},$$&lt;/div>
&lt;div class="math">$$\dot{I}_{f,p}
=
\frac{1}{L_f}
\left(
V_{\mathrm{ref},p}
-
V_{c,p}
-
R_f I_{f,p}
\right)
- j\omega_n I_{f,p},$$&lt;/div>&lt;p>the phases being coupled only through the shared control chain, that is, through $V_{\mathrm{ref},p}$.&lt;/p>
&lt;p>At each simulation step the nonlinear model is linearized about the current operating point into the affine state-space form, with the real and imaginary parts of the 6 complex per-phase envelopes and the 8 real control states packed into a single real 20-vector,&lt;/p>
&lt;div class="math">$$\dot{\mathbf{x}}
\approx
\mathbf{A}\mathbf{x}
+
\mathbf{B}\mathbf{u}
+
\mathbf{E},
\qquad
\mathbf{y}
\approx
\mathbf{C}\mathbf{x}
+
\mathbf{D}\mathbf{u}
+
\mathbf{F},$$&lt;/div>&lt;p>which is subsequently discretized and stamped into the DP MNA system.&lt;/p>
&lt;p>In this default configuration the controller operates in a single positive-sequence $dq$ frame, so only the positive-sequence component of an unbalanced terminal is regulated. The negative-sequence response is present in the per-phase filter envelopes but is not itself a control state, and the $2\omega_n$ ripple it would otherwise induce in the $dq$ frame is therefore not represented.&lt;/p>
&lt;h2 id="optional-negative-sequence-current-control">Optional negative-sequence current control&lt;/h2>
&lt;p>A second, negative-sequence current-control loop can be added alongside the positive-sequence one, giving the dual-sequence structure of Yazdani and Iravani, chapter 8. The two configurations answer different questions: without the loop the model has the same 20 states and the same eigenvalue count as its &lt;code>EMT::Ph3&lt;/code> counterpart, which is what a cross-domain comparison requires, while with it the model gains 2 states and can regulate an unbalanced terminal.&lt;/p>
&lt;p>The negative-sequence quantities are obtained by projecting the same three envelopes onto the conjugate sequence set,&lt;/p>
&lt;div class="math">$$\underline{V}_c^- = V_{c,a} + a^2 V_{c,b} + a\, V_{c,c},
\qquad
\underline{I}_{rc}^- = \frac{\underline{V}_c^- - \underline{U}^-}{R_c}.$$&lt;/div>&lt;p>A negative-sequence component rotates backwards relative to the PLL frame, so in envelope terms its $dq$ image follows from conjugating the projected phasor and rotating by $+\psi$ rather than $-\psi$,&lt;/p>
&lt;div class="math">$$I_{rc,dq}^- = \tfrac{1}{2}\sqrt{\tfrac{2}{3}}\, e^{\,j\psi}\, \overline{\underline{I}_{rc}^-} .$$&lt;/div>
&lt;div class="alert alert-info" role="alert">
&lt;h4 class="alert-heading">Both sequence images are baseband&lt;/h4>
This is what keeps the extension cheap. The negative-sequence loop costs only the two real integrator states $\gamma_{nd}$ and $\gamma_{nq}$, with no second carrier and no $2\omega_n$ term anywhere in the model.
&lt;/div>
&lt;p>The loop itself is the same PI structure as the positive-sequence inner loop,&lt;/p>
&lt;div class="math">$$\dot{\gamma}_{nd} = i_{nd,\mathrm{ref}} - i_{rc,nd},
\qquad
\dot{\gamma}_{nq} = i_{nq,\mathrm{ref}} - i_{rc,nq},$$&lt;/div>
&lt;div class="math">$$v_{nd,\mathrm{ref}}
=
K_{p,I}(i_{nd,\mathrm{ref}} - i_{rc,nd}) +
K_{i,I}\gamma_{nd},
\qquad
v_{nq,\mathrm{ref}}
=
K_{p,I}(i_{nq,\mathrm{ref}} - i_{rc,nq}) +
K_{i,I}\gamma_{nq},$$&lt;/div>&lt;p>reusing the inner-loop gains $K_{p,I}$ and $K_{i,I}$. Its output is redistributed to the per-phase bridge voltages through the sequence-orthogonal set, and adds to the positive-sequence command of the previous section,&lt;/p>
&lt;div class="math">$$V_{\mathrm{ref},p}
=
\bar{a}_p \sqrt{\tfrac{2}{3}}\, V_{\mathrm{ref},dq}\, e^{j\psi}
+
a_p \sqrt{\tfrac{2}{3}}\, \overline{V_{\mathrm{ref},dq}^-}\, e^{j\psi},
\qquad
a_{a/b/c} = \{1,\; a,\; a^2\}.$$&lt;/div>&lt;p>The two references $i_{nd,\mathrm{ref}}$ and $i_{nq,\mathrm{ref}}$ default to zero, which makes the loop a negative-sequence suppressor. A non-zero pair commands a deliberate negative-sequence injection instead, as required by some unbalanced fault ride-through grid codes.&lt;/p>
&lt;p>The state vector grows to 22 by appending the two integrators after the control block, so that the envelope and positive-sequence control indices are unaffected. Under a single-line-to-ground fault, enabling the loop suppresses the negative-sequence component of the injected current by about 40 percent while moving the positive-sequence component by less than 0.1 percent.&lt;/p>
&lt;h2 id="references">References&lt;/h2>
&lt;ul>
&lt;li>M. Mirz, S. Vogel, G. Reinke, and A. Monti, “DPsim: A dynamic phasor real-time simulator for power systems,” &lt;em>SoftwareX&lt;/em>, vol. 10, art. 100253, 2019. &lt;a href="https://doi.org/10.1016/j.softx.2019.100253">https://doi.org/10.1016/j.softx.2019.100253&lt;/a>&lt;/li>
&lt;li>A. Yazdani and R. Iravani, &lt;em>Voltage-Sourced Converters in Power Systems: Modeling, Control, and Applications&lt;/em>. Hoboken, NJ: Wiley-IEEE Press, 2010. &lt;a href="https://ieeexplore.ieee.org/book/5237659">https://ieeexplore.ieee.org/book/5237659&lt;/a>&lt;/li>
&lt;li>X. Gao, D. Zhou, A. Anvari-Moghaddam, and F. Blaabjerg, “Stability Analysis of Grid-Following and Grid-Forming Converters Based on State-Space Model,” in &lt;em>Proc. 2022 International Power Electronics Conference (IPEC-Himeji 2022, ECCE Asia)&lt;/em>, pp. 422–428. &lt;a href="https://ieeexplore.ieee.org/document/9806927">https://ieeexplore.ieee.org/document/9806927&lt;/a>&lt;/li>
&lt;/ul>
&lt;p>How this is arranged in code, together with the source and the runnable examples, is covered under
&lt;a href="https://sogno.energy/dpsim/docs/developer-guide/model-implementations/dp-ph3-averaged-vsi-implementation/">DP Ph3 averaged VSI implementation&lt;/a>.&lt;/p></description></item><item><title>Docs: Examples</title><link>https://sogno.energy/dpsim/docs/user-guide/examples/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://sogno.energy/dpsim/docs/user-guide/examples/</guid><description>
&lt;p>If you are starting out, start with the &lt;a href="https://sogno.energy/dpsim/docs/tutorials/">tutorials&lt;/a> instead. They
work through one idea at a time in order, each as a complete script. This page is the inventory of
what else the repository carries, which is the right thing once you know what you are looking for.&lt;/p>
&lt;p>The examples come in both languages. The Python notebooks run a scenario and plot the result in one
place, so they are the better way to see a complete study. The C++ examples are the better reference
for using DPsim as a library, and are what the real time and co-simulation scenarios are written in.&lt;/p>
&lt;h2 id="where-each-kind-fits">Where each kind fits&lt;/h2>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>If you want to&lt;/th>
&lt;th>Go to&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>Learn how a simulation is put together&lt;/td>
&lt;td>&lt;a href="https://sogno.energy/dpsim/docs/tutorials/">Tutorials&lt;/a>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>See a complete study with plots&lt;/td>
&lt;td>Notebooks, below&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Use DPsim from an application&lt;/td>
&lt;td>C++ examples, below&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Look up what a model does&lt;/td>
&lt;td>&lt;a href="https://sogno.energy/dpsim/docs/concepts/">Concepts&lt;/a>&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;h2 id="notebooks">Notebooks&lt;/h2>
&lt;p>Run them in the browser with no local installation:&lt;/p>
&lt;p>&lt;a href="https://2i2c.mybinder.org/v2/gh/sogno-platform/dpsim/HEAD?urlpath=%2Fdoc%2Ftree%2Fexamples%2FIndex.ipynb">&lt;img src="https://2i2c.mybinder.org/badge_logo.svg" alt="Binder">&lt;/a>&lt;/p>
&lt;p>Locally, they live under
&lt;a href="https://github.com/sogno-platform/dpsim/tree/master/examples/Notebooks">examples/Notebooks&lt;/a> and
need the Python package on the path, as described in the
&lt;a href="https://sogno.energy/dpsim/docs/developer-guide/architecture-and-conventions/build/">build&lt;/a> section.&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Category&lt;/th>
&lt;th>Contents&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>Quickstart Guide&lt;/td>
&lt;td>A single notebook covering a first simulation end to end&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Circuits&lt;/td>
&lt;td>Small networks exercising one modelling aspect at a time&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Components&lt;/td>
&lt;td>One component at a time, often comparing domains against each other&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Grids&lt;/td>
&lt;td>Published test systems such as the WSCC 9 bus and CIGRE networks&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Features&lt;/td>
&lt;td>Cross-cutting capabilities rather than a specific network&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Performance&lt;/td>
&lt;td>Timing and scaling comparisons&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>StateSpace&lt;/td>
&lt;td>State-space extraction from a running simulation&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>&lt;code>Understanding_DP.ipynb&lt;/code> is worth reading early if dynamic phasors are new to you, since it
builds the intuition the &lt;a href="https://sogno.energy/dpsim/docs/concepts/">concepts&lt;/a> section then formalises.&lt;/p>
&lt;h3 id="continuing-from-a-tutorial">Continuing from a tutorial&lt;/h3>
&lt;p>Each tutorial ends at the point where the notebooks take over, so a category is usually the natural
next step from the rung that introduced the idea.&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>After this tutorial&lt;/th>
&lt;th>These notebooks go further&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;a href="https://sogno.energy/dpsim/docs/tutorials/python/adding-dynamics/">Adding dynamics&lt;/a>&lt;/td>
&lt;td>Circuits, for larger passive networks&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;a href="https://sogno.energy/dpsim/docs/tutorials/python/two-bus-network/">Two-bus network&lt;/a>&lt;/td>
&lt;td>Grids, for published test systems solved the same way&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;a href="https://sogno.energy/dpsim/docs/tutorials/python/comparing-domains/">Comparing domains&lt;/a>&lt;/td>
&lt;td>Components, which compare one model across domains&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;a href="https://sogno.energy/dpsim/docs/tutorials/python/a-machine/">Adding a machine&lt;/a>&lt;/td>
&lt;td>Circuits, for the SMIB and multi-machine fault studies&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;a href="https://sogno.energy/dpsim/docs/tutorials/python/exchanging-data/">Exchanging data&lt;/a>&lt;/td>
&lt;td>The co-simulation examples below&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;h2 id="c-examples">C++ examples&lt;/h2>
&lt;p>Under &lt;a href="https://github.com/sogno-platform/dpsim/tree/master/dpsim/examples/cxx">dpsim/examples/cxx&lt;/a>,
built as part of a normal build and produced as executables in the build directory.&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Directory&lt;/th>
&lt;th>Contents&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>Circuits&lt;/td>
&lt;td>Networks assembled directly in C++&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Components&lt;/td>
&lt;td>Single component scenarios&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>CIM&lt;/td>
&lt;td>Reading network data from CIM and CGMES files&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>StateSpace&lt;/td>
&lt;td>State-space extraction&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>RealTime&lt;/td>
&lt;td>Scenarios run against the wall clock&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>DAE, signals, timer&lt;/td>
&lt;td>Smaller scenarios for the DAE solver, signal models and timing&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>The target name is the source file name without its extension, regardless of which directory the
source sits in, and the executables are written flat into the build tree. So to build and run a
single example from your build directory:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-shell" data-lang="shell">&lt;span style="display:flex;">&lt;span>cmake --build . --target DP_VS_RL1
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>./dpsim/examples/cxx/DP_VS_RL1
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="co-simulation">Co-simulation&lt;/h2>
&lt;p>The &lt;a href="https://github.com/sogno-platform/dpsim/tree/master/dpsim-villas/examples/cxx">dpsim-villas&lt;/a>
examples exchange data with other simulators or with hardware through VILLASnode. These need
&lt;code>WITH_VILLAS&lt;/code> enabled and are therefore not available on Windows. See
&lt;a href="https://sogno.energy/dpsim/docs/user-guide/co-simulation/">interfaces&lt;/a> for how the coupling works and
&lt;a href="https://sogno.energy/dpsim/docs/user-guide/real-time/">real time&lt;/a> for running them against the wall
clock.&lt;/p></description></item><item><title>Docs: Model Availability</title><link>https://sogno.energy/dpsim/docs/reference/model-availability/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://sogno.energy/dpsim/docs/reference/model-availability/</guid><description>
&lt;p>Which model exists in which domain. A tick means the domain has an implementation, a dash means it
does not.&lt;/p>
&lt;p>The table below is generated from the headers under &lt;code>dpsim-models/include/dpsim-models&lt;/code> by
&lt;code>scripts/docs/generate_model_availability.py&lt;/code>. Do not edit it by hand; run the script with &lt;code>--write&lt;/code>
instead. A model class the script does not recognise makes it fail rather than silently drop the
model, so the table cannot fall behind the code. For the equations behind a model, see
&lt;a href="https://sogno.energy/dpsim/docs/concepts/models/">models&lt;/a>.&lt;/p>
&lt;!-- BEGIN GENERATED -->
&lt;h2 id="passive-elements-and-sources">Passive elements and sources&lt;/h2>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Model&lt;/th>
&lt;th style="text-align:center">SP::Ph1&lt;/th>
&lt;th style="text-align:center">SP::Ph3&lt;/th>
&lt;th style="text-align:center">DP::Ph1&lt;/th>
&lt;th style="text-align:center">DP::Ph3&lt;/th>
&lt;th style="text-align:center">EMT::Ph1&lt;/th>
&lt;th style="text-align:center">EMT::Ph3&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>Resistor&lt;/td>
&lt;td style="text-align:center">✓&lt;/td>
&lt;td style="text-align:center">✓&lt;/td>
&lt;td style="text-align:center">✓&lt;/td>
&lt;td style="text-align:center">✓&lt;/td>
&lt;td style="text-align:center">✓&lt;/td>
&lt;td style="text-align:center">✓&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Inductor&lt;/td>
&lt;td style="text-align:center">✓&lt;/td>
&lt;td style="text-align:center">✓&lt;/td>
&lt;td style="text-align:center">✓&lt;/td>
&lt;td style="text-align:center">✓&lt;/td>
&lt;td style="text-align:center">✓&lt;/td>
&lt;td style="text-align:center">✓&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Capacitor&lt;/td>
&lt;td style="text-align:center">✓&lt;/td>
&lt;td style="text-align:center">✓&lt;/td>
&lt;td style="text-align:center">✓&lt;/td>
&lt;td style="text-align:center">✓&lt;/td>
&lt;td style="text-align:center">✓&lt;/td>
&lt;td style="text-align:center">✓&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>VoltageSource&lt;/td>
&lt;td style="text-align:center">✓&lt;/td>
&lt;td style="text-align:center">✓&lt;/td>
&lt;td style="text-align:center">✓&lt;/td>
&lt;td style="text-align:center">✓&lt;/td>
&lt;td style="text-align:center">✓&lt;/td>
&lt;td style="text-align:center">✓&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>CurrentSource&lt;/td>
&lt;td style="text-align:center">–&lt;/td>
&lt;td style="text-align:center">–&lt;/td>
&lt;td style="text-align:center">✓&lt;/td>
&lt;td style="text-align:center">✓&lt;/td>
&lt;td style="text-align:center">✓&lt;/td>
&lt;td style="text-align:center">✓&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>VoltageSourceNorton&lt;/td>
&lt;td style="text-align:center">–&lt;/td>
&lt;td style="text-align:center">–&lt;/td>
&lt;td style="text-align:center">✓&lt;/td>
&lt;td style="text-align:center">–&lt;/td>
&lt;td style="text-align:center">✓&lt;/td>
&lt;td style="text-align:center">✓&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>VoltageSourceRamp&lt;/td>
&lt;td style="text-align:center">–&lt;/td>
&lt;td style="text-align:center">–&lt;/td>
&lt;td style="text-align:center">✓&lt;/td>
&lt;td style="text-align:center">–&lt;/td>
&lt;td style="text-align:center">✓&lt;/td>
&lt;td style="text-align:center">–&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>ProfileVoltageSource&lt;/td>
&lt;td style="text-align:center">–&lt;/td>
&lt;td style="text-align:center">–&lt;/td>
&lt;td style="text-align:center">✓&lt;/td>
&lt;td style="text-align:center">–&lt;/td>
&lt;td style="text-align:center">–&lt;/td>
&lt;td style="text-align:center">–&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>ControlledVoltageSource&lt;/td>
&lt;td style="text-align:center">✓&lt;/td>
&lt;td style="text-align:center">–&lt;/td>
&lt;td style="text-align:center">✓&lt;/td>
&lt;td style="text-align:center">–&lt;/td>
&lt;td style="text-align:center">–&lt;/td>
&lt;td style="text-align:center">✓&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>ControlledCurrentSource&lt;/td>
&lt;td style="text-align:center">✓&lt;/td>
&lt;td style="text-align:center">–&lt;/td>
&lt;td style="text-align:center">✓&lt;/td>
&lt;td style="text-align:center">–&lt;/td>
&lt;td style="text-align:center">–&lt;/td>
&lt;td style="text-align:center">✓&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>NetworkInjection&lt;/td>
&lt;td style="text-align:center">✓&lt;/td>
&lt;td style="text-align:center">–&lt;/td>
&lt;td style="text-align:center">✓&lt;/td>
&lt;td style="text-align:center">✓&lt;/td>
&lt;td style="text-align:center">–&lt;/td>
&lt;td style="text-align:center">✓&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;h2 id="branches">Branches&lt;/h2>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Model&lt;/th>
&lt;th style="text-align:center">SP::Ph1&lt;/th>
&lt;th style="text-align:center">SP::Ph3&lt;/th>
&lt;th style="text-align:center">DP::Ph1&lt;/th>
&lt;th style="text-align:center">DP::Ph3&lt;/th>
&lt;th style="text-align:center">EMT::Ph1&lt;/th>
&lt;th style="text-align:center">EMT::Ph3&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>PiLine&lt;/td>
&lt;td style="text-align:center">✓&lt;/td>
&lt;td style="text-align:center">–&lt;/td>
&lt;td style="text-align:center">✓&lt;/td>
&lt;td style="text-align:center">✓&lt;/td>
&lt;td style="text-align:center">✓&lt;/td>
&lt;td style="text-align:center">✓&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>RxLine&lt;/td>
&lt;td style="text-align:center">–&lt;/td>
&lt;td style="text-align:center">✓&lt;/td>
&lt;td style="text-align:center">✓&lt;/td>
&lt;td style="text-align:center">–&lt;/td>
&lt;td style="text-align:center">–&lt;/td>
&lt;td style="text-align:center">✓&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>RXLine&lt;/td>
&lt;td style="text-align:center">✓&lt;/td>
&lt;td style="text-align:center">–&lt;/td>
&lt;td style="text-align:center">–&lt;/td>
&lt;td style="text-align:center">–&lt;/td>
&lt;td style="text-align:center">–&lt;/td>
&lt;td style="text-align:center">–&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>SeriesResistor&lt;/td>
&lt;td style="text-align:center">–&lt;/td>
&lt;td style="text-align:center">–&lt;/td>
&lt;td style="text-align:center">–&lt;/td>
&lt;td style="text-align:center">✓&lt;/td>
&lt;td style="text-align:center">–&lt;/td>
&lt;td style="text-align:center">✓&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>ResIndSeries&lt;/td>
&lt;td style="text-align:center">–&lt;/td>
&lt;td style="text-align:center">–&lt;/td>
&lt;td style="text-align:center">✓&lt;/td>
&lt;td style="text-align:center">–&lt;/td>
&lt;td style="text-align:center">–&lt;/td>
&lt;td style="text-align:center">–&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Transformer&lt;/td>
&lt;td style="text-align:center">✓&lt;/td>
&lt;td style="text-align:center">–&lt;/td>
&lt;td style="text-align:center">✓&lt;/td>
&lt;td style="text-align:center">–&lt;/td>
&lt;td style="text-align:center">–&lt;/td>
&lt;td style="text-align:center">✓&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>SolidStateTransformer&lt;/td>
&lt;td style="text-align:center">✓&lt;/td>
&lt;td style="text-align:center">–&lt;/td>
&lt;td style="text-align:center">–&lt;/td>
&lt;td style="text-align:center">–&lt;/td>
&lt;td style="text-align:center">–&lt;/td>
&lt;td style="text-align:center">–&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;h2 id="switches-and-loads">Switches and loads&lt;/h2>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Model&lt;/th>
&lt;th style="text-align:center">SP::Ph1&lt;/th>
&lt;th style="text-align:center">SP::Ph3&lt;/th>
&lt;th style="text-align:center">DP::Ph1&lt;/th>
&lt;th style="text-align:center">DP::Ph3&lt;/th>
&lt;th style="text-align:center">EMT::Ph1&lt;/th>
&lt;th style="text-align:center">EMT::Ph3&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>Switch&lt;/td>
&lt;td style="text-align:center">✓&lt;/td>
&lt;td style="text-align:center">–&lt;/td>
&lt;td style="text-align:center">✓&lt;/td>
&lt;td style="text-align:center">✓&lt;/td>
&lt;td style="text-align:center">✓&lt;/td>
&lt;td style="text-align:center">✓&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>SeriesSwitch&lt;/td>
&lt;td style="text-align:center">–&lt;/td>
&lt;td style="text-align:center">–&lt;/td>
&lt;td style="text-align:center">–&lt;/td>
&lt;td style="text-align:center">✓&lt;/td>
&lt;td style="text-align:center">–&lt;/td>
&lt;td style="text-align:center">✓&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>varResSwitch&lt;/td>
&lt;td style="text-align:center">✓&lt;/td>
&lt;td style="text-align:center">–&lt;/td>
&lt;td style="text-align:center">✓&lt;/td>
&lt;td style="text-align:center">–&lt;/td>
&lt;td style="text-align:center">–&lt;/td>
&lt;td style="text-align:center">–&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>RXLoad&lt;/td>
&lt;td style="text-align:center">–&lt;/td>
&lt;td style="text-align:center">–&lt;/td>
&lt;td style="text-align:center">✓&lt;/td>
&lt;td style="text-align:center">–&lt;/td>
&lt;td style="text-align:center">–&lt;/td>
&lt;td style="text-align:center">✓&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>RXLoadSwitch&lt;/td>
&lt;td style="text-align:center">–&lt;/td>
&lt;td style="text-align:center">–&lt;/td>
&lt;td style="text-align:center">✓&lt;/td>
&lt;td style="text-align:center">–&lt;/td>
&lt;td style="text-align:center">–&lt;/td>
&lt;td style="text-align:center">–&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>PQLoadCS&lt;/td>
&lt;td style="text-align:center">–&lt;/td>
&lt;td style="text-align:center">–&lt;/td>
&lt;td style="text-align:center">✓&lt;/td>
&lt;td style="text-align:center">–&lt;/td>
&lt;td style="text-align:center">–&lt;/td>
&lt;td style="text-align:center">–&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Load&lt;/td>
&lt;td style="text-align:center">✓&lt;/td>
&lt;td style="text-align:center">–&lt;/td>
&lt;td style="text-align:center">–&lt;/td>
&lt;td style="text-align:center">–&lt;/td>
&lt;td style="text-align:center">–&lt;/td>
&lt;td style="text-align:center">–&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Shunt&lt;/td>
&lt;td style="text-align:center">✓&lt;/td>
&lt;td style="text-align:center">–&lt;/td>
&lt;td style="text-align:center">✓&lt;/td>
&lt;td style="text-align:center">–&lt;/td>
&lt;td style="text-align:center">–&lt;/td>
&lt;td style="text-align:center">✓&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>SVC&lt;/td>
&lt;td style="text-align:center">–&lt;/td>
&lt;td style="text-align:center">–&lt;/td>
&lt;td style="text-align:center">✓&lt;/td>
&lt;td style="text-align:center">–&lt;/td>
&lt;td style="text-align:center">–&lt;/td>
&lt;td style="text-align:center">–&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;h2 id="synchronous-generators">Synchronous generators&lt;/h2>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Model&lt;/th>
&lt;th style="text-align:center">SP::Ph1&lt;/th>
&lt;th style="text-align:center">SP::Ph3&lt;/th>
&lt;th style="text-align:center">DP::Ph1&lt;/th>
&lt;th style="text-align:center">DP::Ph3&lt;/th>
&lt;th style="text-align:center">EMT::Ph1&lt;/th>
&lt;th style="text-align:center">EMT::Ph3&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>SynchronGenerator&lt;/td>
&lt;td style="text-align:center">✓&lt;/td>
&lt;td style="text-align:center">–&lt;/td>
&lt;td style="text-align:center">–&lt;/td>
&lt;td style="text-align:center">–&lt;/td>
&lt;td style="text-align:center">–&lt;/td>
&lt;td style="text-align:center">–&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>SynchronGeneratorDQ&lt;/td>
&lt;td style="text-align:center">–&lt;/td>
&lt;td style="text-align:center">–&lt;/td>
&lt;td style="text-align:center">–&lt;/td>
&lt;td style="text-align:center">✓&lt;/td>
&lt;td style="text-align:center">–&lt;/td>
&lt;td style="text-align:center">✓&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>SynchronGeneratorDQODE&lt;/td>
&lt;td style="text-align:center">–&lt;/td>
&lt;td style="text-align:center">–&lt;/td>
&lt;td style="text-align:center">–&lt;/td>
&lt;td style="text-align:center">✓&lt;/td>
&lt;td style="text-align:center">–&lt;/td>
&lt;td style="text-align:center">✓&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>SynchronGeneratorDQTrapez&lt;/td>
&lt;td style="text-align:center">–&lt;/td>
&lt;td style="text-align:center">–&lt;/td>
&lt;td style="text-align:center">–&lt;/td>
&lt;td style="text-align:center">✓&lt;/td>
&lt;td style="text-align:center">–&lt;/td>
&lt;td style="text-align:center">✓&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>SynchronGeneratorVBR&lt;/td>
&lt;td style="text-align:center">–&lt;/td>
&lt;td style="text-align:center">–&lt;/td>
&lt;td style="text-align:center">–&lt;/td>
&lt;td style="text-align:center">–&lt;/td>
&lt;td style="text-align:center">–&lt;/td>
&lt;td style="text-align:center">✓&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>SynchronGenerator3OrderVBR&lt;/td>
&lt;td style="text-align:center">✓&lt;/td>
&lt;td style="text-align:center">–&lt;/td>
&lt;td style="text-align:center">✓&lt;/td>
&lt;td style="text-align:center">–&lt;/td>
&lt;td style="text-align:center">–&lt;/td>
&lt;td style="text-align:center">✓&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>SynchronGenerator4OrderVBR&lt;/td>
&lt;td style="text-align:center">✓&lt;/td>
&lt;td style="text-align:center">–&lt;/td>
&lt;td style="text-align:center">✓&lt;/td>
&lt;td style="text-align:center">–&lt;/td>
&lt;td style="text-align:center">–&lt;/td>
&lt;td style="text-align:center">✓&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>SynchronGenerator5OrderVBR&lt;/td>
&lt;td style="text-align:center">✓&lt;/td>
&lt;td style="text-align:center">–&lt;/td>
&lt;td style="text-align:center">✓&lt;/td>
&lt;td style="text-align:center">–&lt;/td>
&lt;td style="text-align:center">–&lt;/td>
&lt;td style="text-align:center">✓&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>SynchronGenerator6aOrderVBR&lt;/td>
&lt;td style="text-align:center">✓&lt;/td>
&lt;td style="text-align:center">–&lt;/td>
&lt;td style="text-align:center">✓&lt;/td>
&lt;td style="text-align:center">–&lt;/td>
&lt;td style="text-align:center">–&lt;/td>
&lt;td style="text-align:center">✓&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>SynchronGenerator6bOrderVBR&lt;/td>
&lt;td style="text-align:center">✓&lt;/td>
&lt;td style="text-align:center">–&lt;/td>
&lt;td style="text-align:center">✓&lt;/td>
&lt;td style="text-align:center">–&lt;/td>
&lt;td style="text-align:center">–&lt;/td>
&lt;td style="text-align:center">✓&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>SynchronGenerator4OrderPCM&lt;/td>
&lt;td style="text-align:center">–&lt;/td>
&lt;td style="text-align:center">–&lt;/td>
&lt;td style="text-align:center">✓&lt;/td>
&lt;td style="text-align:center">–&lt;/td>
&lt;td style="text-align:center">–&lt;/td>
&lt;td style="text-align:center">✓&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>SynchronGenerator6OrderPCM&lt;/td>
&lt;td style="text-align:center">–&lt;/td>
&lt;td style="text-align:center">–&lt;/td>
&lt;td style="text-align:center">✓&lt;/td>
&lt;td style="text-align:center">–&lt;/td>
&lt;td style="text-align:center">–&lt;/td>
&lt;td style="text-align:center">–&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>SynchronGenerator4OrderTPM&lt;/td>
&lt;td style="text-align:center">–&lt;/td>
&lt;td style="text-align:center">–&lt;/td>
&lt;td style="text-align:center">✓&lt;/td>
&lt;td style="text-align:center">–&lt;/td>
&lt;td style="text-align:center">–&lt;/td>
&lt;td style="text-align:center">–&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>SynchronGeneratorIdeal&lt;/td>
&lt;td style="text-align:center">–&lt;/td>
&lt;td style="text-align:center">–&lt;/td>
&lt;td style="text-align:center">✓&lt;/td>
&lt;td style="text-align:center">–&lt;/td>
&lt;td style="text-align:center">–&lt;/td>
&lt;td style="text-align:center">✓&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>SynchronGeneratorIter&lt;/td>
&lt;td style="text-align:center">–&lt;/td>
&lt;td style="text-align:center">–&lt;/td>
&lt;td style="text-align:center">✓&lt;/td>
&lt;td style="text-align:center">–&lt;/td>
&lt;td style="text-align:center">–&lt;/td>
&lt;td style="text-align:center">–&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>SynchronGeneratorTrStab&lt;/td>
&lt;td style="text-align:center">✓&lt;/td>
&lt;td style="text-align:center">–&lt;/td>
&lt;td style="text-align:center">✓&lt;/td>
&lt;td style="text-align:center">–&lt;/td>
&lt;td style="text-align:center">–&lt;/td>
&lt;td style="text-align:center">✓&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;h2 id="power-electronics">Power electronics&lt;/h2>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Model&lt;/th>
&lt;th style="text-align:center">SP::Ph1&lt;/th>
&lt;th style="text-align:center">SP::Ph3&lt;/th>
&lt;th style="text-align:center">DP::Ph1&lt;/th>
&lt;th style="text-align:center">DP::Ph3&lt;/th>
&lt;th style="text-align:center">EMT::Ph1&lt;/th>
&lt;th style="text-align:center">EMT::Ph3&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>AvVoltageSourceInverterDQ&lt;/td>
&lt;td style="text-align:center">✓&lt;/td>
&lt;td style="text-align:center">–&lt;/td>
&lt;td style="text-align:center">✓&lt;/td>
&lt;td style="text-align:center">–&lt;/td>
&lt;td style="text-align:center">–&lt;/td>
&lt;td style="text-align:center">✓&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>AvVoltSourceInverterStateSpace&lt;/td>
&lt;td style="text-align:center">–&lt;/td>
&lt;td style="text-align:center">–&lt;/td>
&lt;td style="text-align:center">✓&lt;/td>
&lt;td style="text-align:center">✓&lt;/td>
&lt;td style="text-align:center">–&lt;/td>
&lt;td style="text-align:center">✓&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Inverter&lt;/td>
&lt;td style="text-align:center">–&lt;/td>
&lt;td style="text-align:center">–&lt;/td>
&lt;td style="text-align:center">✓&lt;/td>
&lt;td style="text-align:center">–&lt;/td>
&lt;td style="text-align:center">–&lt;/td>
&lt;td style="text-align:center">–&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>VoltageSourceInverter&lt;/td>
&lt;td style="text-align:center">✓&lt;/td>
&lt;td style="text-align:center">–&lt;/td>
&lt;td style="text-align:center">–&lt;/td>
&lt;td style="text-align:center">–&lt;/td>
&lt;td style="text-align:center">–&lt;/td>
&lt;td style="text-align:center">–&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>VSIVoltageControlVCO&lt;/td>
&lt;td style="text-align:center">–&lt;/td>
&lt;td style="text-align:center">–&lt;/td>
&lt;td style="text-align:center">–&lt;/td>
&lt;td style="text-align:center">–&lt;/td>
&lt;td style="text-align:center">–&lt;/td>
&lt;td style="text-align:center">✓&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>SSN_GFM&lt;/td>
&lt;td style="text-align:center">–&lt;/td>
&lt;td style="text-align:center">–&lt;/td>
&lt;td style="text-align:center">–&lt;/td>
&lt;td style="text-align:center">–&lt;/td>
&lt;td style="text-align:center">–&lt;/td>
&lt;td style="text-align:center">✓&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;h2 id="state-space-nodal-components">State-space nodal components&lt;/h2>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Model&lt;/th>
&lt;th style="text-align:center">SP::Ph1&lt;/th>
&lt;th style="text-align:center">SP::Ph3&lt;/th>
&lt;th style="text-align:center">DP::Ph1&lt;/th>
&lt;th style="text-align:center">DP::Ph3&lt;/th>
&lt;th style="text-align:center">EMT::Ph1&lt;/th>
&lt;th style="text-align:center">EMT::Ph3&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>SSN_Full_Serial_RLC&lt;/td>
&lt;td style="text-align:center">–&lt;/td>
&lt;td style="text-align:center">–&lt;/td>
&lt;td style="text-align:center">✓&lt;/td>
&lt;td style="text-align:center">✓&lt;/td>
&lt;td style="text-align:center">✓&lt;/td>
&lt;td style="text-align:center">✓&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>SSN_Variable_Serial_RLC&lt;/td>
&lt;td style="text-align:center">–&lt;/td>
&lt;td style="text-align:center">–&lt;/td>
&lt;td style="text-align:center">✓&lt;/td>
&lt;td style="text-align:center">–&lt;/td>
&lt;td style="text-align:center">–&lt;/td>
&lt;td style="text-align:center">–&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>SSN_Capacitor&lt;/td>
&lt;td style="text-align:center">–&lt;/td>
&lt;td style="text-align:center">–&lt;/td>
&lt;td style="text-align:center">–&lt;/td>
&lt;td style="text-align:center">–&lt;/td>
&lt;td style="text-align:center">–&lt;/td>
&lt;td style="text-align:center">✓&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>SSN_Inductor&lt;/td>
&lt;td style="text-align:center">–&lt;/td>
&lt;td style="text-align:center">–&lt;/td>
&lt;td style="text-align:center">–&lt;/td>
&lt;td style="text-align:center">–&lt;/td>
&lt;td style="text-align:center">–&lt;/td>
&lt;td style="text-align:center">✓&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>SSNTypeV2T&lt;/td>
&lt;td style="text-align:center">✓&lt;/td>
&lt;td style="text-align:center">–&lt;/td>
&lt;td style="text-align:center">–&lt;/td>
&lt;td style="text-align:center">–&lt;/td>
&lt;td style="text-align:center">✓&lt;/td>
&lt;td style="text-align:center">–&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>SSNTypeI2T&lt;/td>
&lt;td style="text-align:center">✓&lt;/td>
&lt;td style="text-align:center">–&lt;/td>
&lt;td style="text-align:center">–&lt;/td>
&lt;td style="text-align:center">–&lt;/td>
&lt;td style="text-align:center">✓&lt;/td>
&lt;td style="text-align:center">–&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>PiecewiseLinearInductor&lt;/td>
&lt;td style="text-align:center">–&lt;/td>
&lt;td style="text-align:center">–&lt;/td>
&lt;td style="text-align:center">–&lt;/td>
&lt;td style="text-align:center">–&lt;/td>
&lt;td style="text-align:center">–&lt;/td>
&lt;td style="text-align:center">✓&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>GenericTwoTerminalVTypeSSN&lt;/td>
&lt;td style="text-align:center">–&lt;/td>
&lt;td style="text-align:center">–&lt;/td>
&lt;td style="text-align:center">✓&lt;/td>
&lt;td style="text-align:center">✓&lt;/td>
&lt;td style="text-align:center">–&lt;/td>
&lt;td style="text-align:center">✓&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>GenericTwoTerminalITypeSSN&lt;/td>
&lt;td style="text-align:center">–&lt;/td>
&lt;td style="text-align:center">–&lt;/td>
&lt;td style="text-align:center">✓&lt;/td>
&lt;td style="text-align:center">✓&lt;/td>
&lt;td style="text-align:center">–&lt;/td>
&lt;td style="text-align:center">✓&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>GenericFourTerminalVTypeSSN&lt;/td>
&lt;td style="text-align:center">–&lt;/td>
&lt;td style="text-align:center">–&lt;/td>
&lt;td style="text-align:center">–&lt;/td>
&lt;td style="text-align:center">–&lt;/td>
&lt;td style="text-align:center">–&lt;/td>
&lt;td style="text-align:center">✓&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;h2 id="excitation-and-stabilizers">Excitation and stabilizers&lt;/h2>
&lt;ul>
&lt;li>&lt;code>ExciterDC1&lt;/code>&lt;/li>
&lt;li>&lt;code>ExciterDC1Simp&lt;/code>&lt;/li>
&lt;li>&lt;code>ExciterST1Simp&lt;/code>&lt;/li>
&lt;li>&lt;code>ExciterStatic&lt;/code>&lt;/li>
&lt;li>&lt;code>PSS1A&lt;/code>&lt;/li>
&lt;/ul>
&lt;h2 id="turbines-and-governors">Turbines and governors&lt;/h2>
&lt;ul>
&lt;li>&lt;code>SteamTurbine&lt;/code>&lt;/li>
&lt;li>&lt;code>SteamTurbineGovernor&lt;/code>&lt;/li>
&lt;li>&lt;code>HydroTurbine&lt;/code>&lt;/li>
&lt;li>&lt;code>HydroTurbineGovernor&lt;/code>&lt;/li>
&lt;li>&lt;code>TurbineGovernor&lt;/code>&lt;/li>
&lt;li>&lt;code>TurbineGovernorType1&lt;/code>&lt;/li>
&lt;/ul>
&lt;h2 id="converter-control">Converter control&lt;/h2>
&lt;ul>
&lt;li>&lt;code>PowerControllerVSI&lt;/code>&lt;/li>
&lt;li>&lt;code>VoltageControllerVSI&lt;/code>&lt;/li>
&lt;li>&lt;code>PLL&lt;/code>&lt;/li>
&lt;li>&lt;code>VCO&lt;/code>&lt;/li>
&lt;/ul>
&lt;h2 id="signal-sources-and-filters">Signal sources and filters&lt;/h2>
&lt;ul>
&lt;li>&lt;code>SignalGenerator&lt;/code>&lt;/li>
&lt;li>&lt;code>SineWaveGenerator&lt;/code>&lt;/li>
&lt;li>&lt;code>CosineFMGenerator&lt;/code>&lt;/li>
&lt;li>&lt;code>DCGenerator&lt;/code>&lt;/li>
&lt;li>&lt;code>FrequencyRampGenerator&lt;/code>&lt;/li>
&lt;li>&lt;code>FIRFilter&lt;/code>&lt;/li>
&lt;li>&lt;code>Integrator&lt;/code>&lt;/li>
&lt;/ul>
&lt;h2 id="decoupling-components">Decoupling components&lt;/h2>
&lt;p>These are network components: they connect to nodes and own their own sources. They are declared in the &lt;code>Signal&lt;/code> namespace for historical reasons, which is why their domain appears in the class name rather than in the namespace.&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Model&lt;/th>
&lt;th style="text-align:center">SP::Ph1&lt;/th>
&lt;th style="text-align:center">SP::Ph3&lt;/th>
&lt;th style="text-align:center">DP::Ph1&lt;/th>
&lt;th style="text-align:center">DP::Ph3&lt;/th>
&lt;th style="text-align:center">EMT::Ph1&lt;/th>
&lt;th style="text-align:center">EMT::Ph3&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>DecouplingLine&lt;/td>
&lt;td style="text-align:center">–&lt;/td>
&lt;td style="text-align:center">–&lt;/td>
&lt;td style="text-align:center">✓&lt;/td>
&lt;td style="text-align:center">–&lt;/td>
&lt;td style="text-align:center">–&lt;/td>
&lt;td style="text-align:center">–&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>DecouplingLineEMT&lt;/td>
&lt;td style="text-align:center">–&lt;/td>
&lt;td style="text-align:center">–&lt;/td>
&lt;td style="text-align:center">–&lt;/td>
&lt;td style="text-align:center">–&lt;/td>
&lt;td style="text-align:center">✓&lt;/td>
&lt;td style="text-align:center">–&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>DecouplingLineEMT_Ph3&lt;/td>
&lt;td style="text-align:center">–&lt;/td>
&lt;td style="text-align:center">–&lt;/td>
&lt;td style="text-align:center">–&lt;/td>
&lt;td style="text-align:center">–&lt;/td>
&lt;td style="text-align:center">–&lt;/td>
&lt;td style="text-align:center">✓&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>DecouplingIdealTransformer_SP_Ph1&lt;/td>
&lt;td style="text-align:center">✓&lt;/td>
&lt;td style="text-align:center">–&lt;/td>
&lt;td style="text-align:center">–&lt;/td>
&lt;td style="text-align:center">–&lt;/td>
&lt;td style="text-align:center">–&lt;/td>
&lt;td style="text-align:center">–&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>DecouplingIdealTransformer_DP_Ph1&lt;/td>
&lt;td style="text-align:center">–&lt;/td>
&lt;td style="text-align:center">–&lt;/td>
&lt;td style="text-align:center">✓&lt;/td>
&lt;td style="text-align:center">–&lt;/td>
&lt;td style="text-align:center">–&lt;/td>
&lt;td style="text-align:center">–&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>DecouplingIdealTransformer_EMT_Ph1&lt;/td>
&lt;td style="text-align:center">–&lt;/td>
&lt;td style="text-align:center">–&lt;/td>
&lt;td style="text-align:center">–&lt;/td>
&lt;td style="text-align:center">–&lt;/td>
&lt;td style="text-align:center">✓&lt;/td>
&lt;td style="text-align:center">–&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>DecouplingIdealTransformer_EMT_Ph3&lt;/td>
&lt;td style="text-align:center">–&lt;/td>
&lt;td style="text-align:center">–&lt;/td>
&lt;td style="text-align:center">–&lt;/td>
&lt;td style="text-align:center">–&lt;/td>
&lt;td style="text-align:center">–&lt;/td>
&lt;td style="text-align:center">✓&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;!-- END GENERATED --></description></item><item><title>Docs: Applying a Fault</title><link>https://sogno.energy/dpsim/docs/tutorials/python/applying-a-fault/</link><pubDate>Fri, 31 Jul 2026 00:00:00 +0000</pubDate><guid>https://sogno.energy/dpsim/docs/tutorials/python/applying-a-fault/</guid><description>
&lt;p>The network from &lt;a href="https://sogno.energy/dpsim/docs/tutorials/python/two-bus-network/">the previous tutorial&lt;/a> starts in steady state,
so anything that happens to it now is a response to the event rather than to startup. This tutorial
applies a fault at the load bus, clears it, and looks at what the clearing does.&lt;/p>
&lt;p>&lt;img src="circuit.svg" alt="The same network with a switched fault branch at the load bus.">&lt;/p>
&lt;h2 id="scheduling-an-event">Scheduling an event&lt;/h2>
&lt;p>A switch is an ordinary component. What makes it a fault is that its state is changed partway
through the run by an event.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-python" data-lang="python">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#000">fault&lt;/span> &lt;span style="color:#ce5c00;font-weight:bold">=&lt;/span> &lt;span style="color:#000">dpsimpy&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">dp&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">ph1&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">Switch&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#4e9a06">&amp;#34;fault&amp;#34;&lt;/span>&lt;span style="color:#000;font-weight:bold">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#000">fault&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">set_parameters&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#000">open_resistance&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">=&lt;/span>&lt;span style="color:#0000cf;font-weight:bold">1e9&lt;/span>&lt;span style="color:#000;font-weight:bold">,&lt;/span> &lt;span style="color:#000">closed_resistance&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">=&lt;/span>&lt;span style="color:#0000cf;font-weight:bold">10.0&lt;/span>&lt;span style="color:#000;font-weight:bold">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#000">fault&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">open&lt;/span>&lt;span style="color:#000;font-weight:bold">()&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#000">fault&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">connect&lt;/span>&lt;span style="color:#000;font-weight:bold">([&lt;/span>&lt;span style="color:#000">gnd&lt;/span>&lt;span style="color:#000;font-weight:bold">,&lt;/span> &lt;span style="color:#000">n2&lt;/span>&lt;span style="color:#000;font-weight:bold">])&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#8f5902;font-style:italic"># ... build the topology including `fault` ...&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#000">sim&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">add_event&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#000">dpsimpy&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">event&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">SwitchEvent&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#0000cf;font-weight:bold">0.1&lt;/span>&lt;span style="color:#000;font-weight:bold">,&lt;/span> &lt;span style="color:#000">fault&lt;/span>&lt;span style="color:#000;font-weight:bold">,&lt;/span> &lt;span style="color:#204a87;font-weight:bold">True&lt;/span>&lt;span style="color:#000;font-weight:bold">))&lt;/span> &lt;span style="color:#8f5902;font-style:italic"># apply&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#000">sim&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">add_event&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#000">dpsimpy&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">event&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">SwitchEvent&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#0000cf;font-weight:bold">0.2&lt;/span>&lt;span style="color:#000;font-weight:bold">,&lt;/span> &lt;span style="color:#000">fault&lt;/span>&lt;span style="color:#000;font-weight:bold">,&lt;/span> &lt;span style="color:#204a87;font-weight:bold">False&lt;/span>&lt;span style="color:#000;font-weight:bold">))&lt;/span> &lt;span style="color:#8f5902;font-style:italic"># clear&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>The switch is created open and connected between the load bus and ground, so closing it puts a
10 Ω path to ground at that bus. &lt;code>SwitchEvent&lt;/code> takes the time, the switch, and the state to move to:
&lt;code>True&lt;/code> closes, &lt;code>False&lt;/code> opens.&lt;/p>
&lt;p>The switch must be in the topology&amp;rsquo;s component list like anything else. A switch that is created,
connected and given events but left out of the list produces a run with no fault and no error.&lt;/p>
&lt;p>Set a time step small enough to resolve the event. At 0.1 ms the fault instant is captured within
one step; a millisecond step would smear it.&lt;/p>
&lt;h2 id="what-happens">What happens&lt;/h2>
&lt;p>The load bus sits at 20 080 V, drops to 19 090 V while the fault is on, and recovers afterwards.
The drop is modest because a 10 Ω fault on a 20 kV bus is not a solid short and the source is stiff.&lt;/p>
&lt;p>The interesting part is the instant of clearing:&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Time&lt;/th>
&lt;th>Bus voltage&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>0.1999 s&lt;/td>
&lt;td>19 090 V&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>0.2000 s&lt;/td>
&lt;td>22 684 V&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>0.2001 s&lt;/td>
&lt;td>29 036 V&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>0.2002 s&lt;/td>
&lt;td>33 103 V&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>0.2003 s&lt;/td>
&lt;td>&lt;strong>34 011 V&lt;/strong>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>0.2005 s&lt;/td>
&lt;td>26 726 V&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>0.2007 s&lt;/td>
&lt;td>14 642 V&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>0.2009 s&lt;/td>
&lt;td>9 081 V&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>The bus voltage rings between 34 kV and 9 kV within a millisecond, a 70% overshoot on a network that
was in steady state a moment earlier.&lt;/p>
&lt;h2 id="why-and-what-to-do-about-it">Why, and what to do about it&lt;/h2>
&lt;p>This is not the physical response of the circuit. Opening the switch asks the simulation to
interrupt the current flowing through the line inductance within one time step, and an inductor
current cannot change instantaneously. With the trapezoidal companion model the result is a
numerical oscillation that decays slowly, as explained under
&lt;a href="https://sogno.energy/dpsim/docs/concepts/models/switches/">switches&lt;/a>.&lt;/p>
&lt;p>A real breaker does not do this, because an arc forms across the opening contacts and dissipates the
stored energy over a short but finite interval. The variable-resistance switch reproduces that: it
raises its resistance over several steps rather than in one.&lt;/p>
&lt;p>Two changes are needed, and the second is easy to forget:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-python" data-lang="python">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#000">fault&lt;/span> &lt;span style="color:#ce5c00;font-weight:bold">=&lt;/span> &lt;span style="color:#000">dpsimpy&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">dp&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">ph1&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">varResSwitch&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#4e9a06">&amp;#34;fault&amp;#34;&lt;/span>&lt;span style="color:#000;font-weight:bold">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#000">fault&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">set_parameters&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#000">open_resistance&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">=&lt;/span>&lt;span style="color:#0000cf;font-weight:bold">1e9&lt;/span>&lt;span style="color:#000;font-weight:bold">,&lt;/span> &lt;span style="color:#000">closed_resistance&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">=&lt;/span>&lt;span style="color:#0000cf;font-weight:bold">10.0&lt;/span>&lt;span style="color:#000;font-weight:bold">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#000">fault&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">open&lt;/span>&lt;span style="color:#000;font-weight:bold">()&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#000">fault&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">set_init_parameters&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#0000cf;font-weight:bold">1e-4&lt;/span>&lt;span style="color:#000;font-weight:bold">)&lt;/span> &lt;span style="color:#8f5902;font-style:italic"># must match the simulation time step&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>
&lt;div class="alert alert-warning" role="alert">
&lt;h4 class="alert-heading">Watch out: set_init_parameters must match your time step&lt;/h4>
&lt;code>set_init_parameters&lt;/code> takes the time step and derives the rate at which the resistance is raised
from it. Without the call the component keeps a default rate that is correct only for a 1 ms step,
so a simulation at any other step size gets a transition of the wrong duration. Nothing warns you.
&lt;/div>
&lt;p>With the same fault at the same instant:&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Time&lt;/th>
&lt;th>Plain switch&lt;/th>
&lt;th>Variable-resistance switch&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>0.1999 s&lt;/td>
&lt;td>19 090 V&lt;/td>
&lt;td>19 090 V&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>0.2001 s&lt;/td>
&lt;td>29 036 V&lt;/td>
&lt;td>19 595 V&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>0.2003 s&lt;/td>
&lt;td>34 011 V&lt;/td>
&lt;td>20 432 V&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>0.2006 s&lt;/td>
&lt;td>20 511 V&lt;/td>
&lt;td>&lt;strong>20 943 V&lt;/strong>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>0.2009 s&lt;/td>
&lt;td>9 081 V&lt;/td>
&lt;td>20 580 V&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>The oscillation is gone. The voltage rises smoothly to a 20 943 V peak, a 4% overshoot rather than
70%, and settles back to its pre-fault value.&lt;/p>
&lt;p>Use the plain switch for switching that does not interrupt inductive current, and the
variable-resistance switch for faults, particularly at a machine terminal or a transformer winding.
The cost is that the system matrix changes on every step of the transition rather than once, so each
of those steps needs a refactorisation.&lt;/p>
&lt;h2 id="the-script">The script&lt;/h2>
&lt;p>The complete script for this page is &lt;a href="https://github.com/sogno-platform/dpsim/blob/master/examples/Python/Tutorials/04_applying_a_fault.py">&lt;code>04_applying_a_fault.py&lt;/code>&lt;/a> under &lt;code>examples/Python/Tutorials&lt;/code>. The numbers quoted above are the numbers it prints, so if the two ever disagree the page is the one that is wrong.&lt;/p>
&lt;h2 id="next">Next&lt;/h2>
&lt;p>The results so far have been envelopes. Next is running the same circuit as instantaneous waveforms
and comparing the two, which is where the domains stop being an abstraction.&lt;/p></description></item><item><title>Docs: Logging Results</title><link>https://sogno.energy/dpsim/docs/user-guide/logging/</link><pubDate>Fri, 31 Jul 2026 00:00:00 +0000</pubDate><guid>https://sogno.energy/dpsim/docs/user-guide/logging/</guid><description>
&lt;p>A simulation records nothing unless asked. Every quantity you want afterwards has to be named before
the run, because the solver keeps only what the current step needs and discards the rest.&lt;/p>
&lt;p>&lt;code>dpsimpy.Logger&lt;/code> is the one to reach for. It is the CSV data logger and it is what nearly every
example and notebook uses. There is also a real-time data logger, which is the right choice under a
real-time timer for the reason given below, and an interface for sending results somewhere other
than a file.&lt;/p>
&lt;h2 id="only-attributes-can-be-logged">Only attributes can be logged&lt;/h2>
&lt;div class="alert alert-warning" role="alert">
&lt;h4 class="alert-heading">Watch out: only attributes can be logged&lt;/h4>
&lt;p>A logger records an &lt;strong>attribute&lt;/strong> and nothing else. It cannot record an arbitrary expression, a
plain member variable, or a quantity a component computes internally without publishing.&lt;/p>
&lt;p>The same constraint governs co-simulation and task scheduling, so a value that is not an attribute
cannot be logged, exchanged with another tool, or depended on by another task. Making it one is a
change to the model, not to the call site.&lt;/p>
&lt;/div>
&lt;p>So the question is never &amp;ldquo;how do I log this value&amp;rdquo; but &amp;ldquo;is this value an attribute&amp;rdquo;. If it is, one
line records it. If it is not, no logger option will reach it, and the answer is to expose it as an
attribute in the model, which is a code change described under
&lt;a href="https://sogno.energy/dpsim/docs/developer-guide/attributes-and-scheduling/attributes/">attributes&lt;/a>.&lt;/p>
&lt;p>&lt;code>print_attribute_list()&lt;/code> on any object prints what it publishes, which is the reliable way to find
out. Anything absent from that list cannot be logged.&lt;/p>
&lt;p>The same fact explains a convenience: because attributes are the unit, a derived quantity that is
itself an attribute is logged the same way as a terminal voltage, with no special handling.&lt;/p>
&lt;h2 id="registering-an-attribute">Registering an attribute&lt;/h2>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-python" data-lang="python">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#000">logger&lt;/span> &lt;span style="color:#ce5c00;font-weight:bold">=&lt;/span> &lt;span style="color:#000">dpsimpy&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">Logger&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#4e9a06">&amp;#34;my_simulation&amp;#34;&lt;/span>&lt;span style="color:#000;font-weight:bold">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#000">logger&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">log_attribute&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#4e9a06">&amp;#34;n1.v&amp;#34;&lt;/span>&lt;span style="color:#000;font-weight:bold">,&lt;/span> &lt;span style="color:#4e9a06">&amp;#34;v&amp;#34;&lt;/span>&lt;span style="color:#000;font-weight:bold">,&lt;/span> &lt;span style="color:#000">n1&lt;/span>&lt;span style="color:#000;font-weight:bold">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#000">logger&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">log_attribute&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#4e9a06">&amp;#34;load.i_intf&amp;#34;&lt;/span>&lt;span style="color:#000;font-weight:bold">,&lt;/span> &lt;span style="color:#4e9a06">&amp;#34;i_intf&amp;#34;&lt;/span>&lt;span style="color:#000;font-weight:bold">,&lt;/span> &lt;span style="color:#000">load&lt;/span>&lt;span style="color:#000;font-weight:bold">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#000">sim&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">add_logger&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#000">logger&lt;/span>&lt;span style="color:#000;font-weight:bold">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>The three arguments are the column name you want in the output, the name of the attribute on the
object, and the object itself. The first is yours to choose and is what you will key on when reading
the file back; the second must match an attribute the object actually publishes.&lt;/p>
&lt;p>Common attribute names are &lt;code>v&lt;/code> on a node, and &lt;code>i_intf&lt;/code> and &lt;code>v_intf&lt;/code> on a component for the current
through it and the voltage across it. Components publish their own states as well: a machine offers
&lt;code>w_r&lt;/code>, &lt;code>delta&lt;/code> and &lt;code>Te&lt;/code>, a converter its control states.&lt;/p>
&lt;p>Naming an attribute that does not exist fails when the logger is set up, not at the end of the run,
so a typo costs a second rather than a simulation.&lt;/p>
&lt;h2 id="where-the-file-goes">Where the file goes&lt;/h2>
&lt;p>By default the file is &lt;code>logs/&amp;lt;logger name&amp;gt;.csv&lt;/code> under the working directory, where the name is the
one given to the &lt;code>Logger&lt;/code> constructor.&lt;/p>
&lt;p>&lt;code>Logger.set_log_dir&lt;/code> changes that directory and &lt;code>Logger.get_log_dir&lt;/code> reports it. Calling it is what
produces the nested &lt;code>logs/&amp;lt;something&amp;gt;/&amp;lt;name&amp;gt;.csv&lt;/code> layout the example notebooks use, so a script that
does not call it gets the flat form. Setting it is worth doing when one script runs several
simulations, since two loggers with the same name otherwise write to the same file.&lt;/p>
&lt;p>The same setting governs the diagnostic text log, which is why the two land side by side.&lt;/p>
&lt;p>The file is plain CSV with a &lt;code>time&lt;/code> column first and one column per logged attribute, in the order
they were registered. A three-phase quantity becomes three columns suffixed &lt;code>_0&lt;/code>, &lt;code>_1&lt;/code>, &lt;code>_2&lt;/code>, and a
complex quantity in an envelope domain is written as a complex value that the reading side parses
back.&lt;/p>
&lt;h2 id="reading-it-back">Reading it back&lt;/h2>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-python" data-lang="python">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#204a87;font-weight:bold">import&lt;/span> &lt;span style="color:#000">villas.dataprocessing.readtools&lt;/span> &lt;span style="color:#204a87;font-weight:bold">as&lt;/span> &lt;span style="color:#000">rt&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#000">results&lt;/span> &lt;span style="color:#ce5c00;font-weight:bold">=&lt;/span> &lt;span style="color:#000">rt&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">read_timeseries_dpsim&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#4e9a06">&amp;#34;logs/my_simulation.csv&amp;#34;&lt;/span>&lt;span style="color:#000;font-weight:bold">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#000">voltage&lt;/span> &lt;span style="color:#ce5c00;font-weight:bold">=&lt;/span> &lt;span style="color:#000">results&lt;/span>&lt;span style="color:#000;font-weight:bold">[&lt;/span>&lt;span style="color:#4e9a06">&amp;#34;n1.v&amp;#34;&lt;/span>&lt;span style="color:#000;font-weight:bold">]&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>The keys are the column names you chose. Each value carries &lt;code>time&lt;/code> and &lt;code>values&lt;/code> arrays. In an
envelope domain the values are complex, so &lt;code>abs()&lt;/code> gives the magnitude, and
&lt;code>frequency_shift_list&lt;/code> recovers the waveform as shown in
&lt;a href="https://sogno.energy/dpsim/docs/tutorials/python/comparing-domains/">comparing domains&lt;/a>.&lt;/p>
&lt;p>The first row is written before the first solve, so it holds the state the simulation started from
rather than a result. A plot that appears to begin at zero usually begins at that row.&lt;/p>
&lt;h2 id="what-it-costs">What it costs&lt;/h2>
&lt;p>Logging is opt-in and costs nothing for what you do not ask for, but what you do ask for is written
every step. A run of 100 000 steps logging 50 attributes writes five million values, and on a large
network the file, not the solve, becomes the slow part.&lt;/p>
&lt;p>Three things help. Log the attributes you will actually look at rather than everything available.
Prefer a specific attribute over a whole matrix; the &lt;code>rows_max&lt;/code> and &lt;code>cols_max&lt;/code> arguments to
&lt;code>log_attribute&lt;/code> cap how much of a matrix quantity is written. And note that a large time step
reduces the file in exact proportion, so a study that only needs the envelope does not need the
step of one that needs the waveform.&lt;/p>
&lt;div class="alert alert-primary" role="alert">
&lt;h4 class="alert-heading">Note: down-sampling is not reachable from Python&lt;/h4>
The C++ &lt;code>DataLogger&lt;/code> additionally accepts a down-sampling factor, writing every n-th step. That
argument is not exposed on the Python &lt;code>Logger&lt;/code>, which takes only a name, so from Python the step
size is the only control over how many rows you get.
&lt;/div>
&lt;h2 id="data-logging-against-diagnostic-logging">Data logging against diagnostic logging&lt;/h2>
&lt;p>The word covers two unrelated things and they are easy to confuse.&lt;/p>
&lt;p>What this page describes is the &lt;strong>data logger&lt;/strong>: numerical results, CSV, opt-in per attribute. The
other is the &lt;strong>diagnostic log&lt;/strong>, the text file recording what the solver did, controlled by
&lt;code>dpsimpy.LogLevel&lt;/code> and passed to component constructors. Raising a component&amp;rsquo;s log level makes it
describe its own initialization and stamping in prose; it has no effect on the CSV.&lt;/p>
&lt;p>They land in the same &lt;code>logs/&lt;/code> directory side by side, one as &lt;code>.csv&lt;/code> and the other as &lt;code>.log&lt;/code>, which
is why they get mistaken for each other. A component constructed with &lt;code>LogLevel.debug&lt;/code> produces a
great deal of text and no additional results.&lt;/p>
&lt;p>The real-time data logger mentioned at the top is a data logger like the first, not a third kind of
log. It records the same results and differs only in buffering them in memory and writing at the
end, because a disk write inside a real-time step has no bound on how long it takes. See
&lt;a href="https://sogno.energy/dpsim/docs/user-guide/real-time/">real-time&lt;/a>.&lt;/p></description></item><item><title>Docs: Ideal Transformer Model</title><link>https://sogno.energy/dpsim/docs/concepts/models/ideal-transformer-model/</link><pubDate>Fri, 26 Jun 2026 00:00:00 +0000</pubDate><guid>https://sogno.energy/dpsim/docs/concepts/models/ideal-transformer-model/</guid><description>
&lt;p>The Ideal Transformer Model (ITM) is a signal component that splits a circuit into two subcircuits, using a common node as a Point of Common Coupling (PCC), in such a way that a copy of this node is found in the two subcircuits, as shown in Fig. 1, where the copies of the node are denoted as $n$ and $m$. Moreover, the circuits are coupled using a controlled voltage source and a controlled current source, which exchange their interface currents and voltages, respectively, namely the interface signals. This exchange takes place using a ring buffer, on top of which a second ring buffer has been implemented to emulate a co-simualtion using a macro-step, which means that the exchange of interface signals can be made at an interval larger than the simulation&amp;rsquo;s step size. This second ring buffer is used to implement Zero- and First-Order hold extrapolation methods, while the first ring buffer allows to linearly interpolate the value of the signal at the current time step, in case the delay between both subcircuits is not an integer multiple of the step size.&lt;/p>
&lt;center>
&lt;figure margin=30%>
&lt;img src="./images/ITM.svg" alt="ITM">
&lt;figcaption>Fig. 1: Ideal Transformer Model Circuit diagram.
&lt;/figcaption>
&lt;/figure>
&lt;/center>
&lt;p>To add an ITM, users must split the cirtuit and create the copies of the PCC node. An example of this process can be found in the Notebook &lt;code>ITM.ipynb&lt;/code>.&lt;/p>
&lt;p>To avoid connections of the controlled voltage source with a capacitor, or the controlled current source with an inductor, the resistors $R_{\mathrm{series}}$ and $R_{\mathrm{parallel}}$ are included.&lt;/p>
&lt;h2 id="why-the-resistors-are-necessary">Why the resistors are necessary&lt;/h2>
&lt;p>The two failure cases they prevent are the same one seen twice. A voltage source directly across a
capacitor over-determines that node: both impose a voltage, and the capacitor&amp;rsquo;s companion model and
the source&amp;rsquo;s constraint row describe the same quantity. A current source in series with an inductor
under-determines the branch in the dual way, since both impose a current. In each case the system
matrix becomes singular rather than merely ill-conditioned, so the resistors are a condition for the
method to work at all and not a refinement of it.&lt;/p>
&lt;p>Their values are a compromise of the kind described under &lt;a href="https://sogno.energy/dpsim/docs/concepts/models/switches/">switches&lt;/a>.
Small enough to be electrically negligible, large enough not to dominate the condition number.&lt;/p>
&lt;h2 id="what-the-delay-costs">What the delay costs&lt;/h2>
&lt;p>The exchanged signals are always at least one step old, because each side computes from what the
other produced previously. That delay is the reason the two subcircuits can be solved separately at
all, and it is also the entire error of the method: the coupled system is not the original circuit
but the original circuit with a transport delay inserted at the point of common coupling.&lt;/p>
&lt;p>The consequence is that accuracy is governed by how much the interface signals change within one
exchange interval, not by how accurately either side is solved internally. Refining the step inside
a subcircuit while holding the macro-step fixed improves nothing at the interface.&lt;/p>
&lt;p>A macro-step larger than the simulation step makes this explicit, which is the point of the second
ring buffer: it is the co-simulation case, where the two sides may be different tools exchanging at
a rate neither controls. Zero-order hold holds the last received value for the whole interval;
first-order hold extrapolates linearly from the last two. The first is safe and lags; the second
tracks a smoothly varying signal better and overshoots at a discontinuity, which is exactly what a
fault produces.&lt;/p>
&lt;h2 id="contrast-with-the-alternatives">Contrast with the alternatives&lt;/h2>
&lt;p>Three ways of splitting a network appear in this documentation and they differ in what they cost.&lt;/p>
&lt;p>Tearing, described under
&lt;a href="https://sogno.energy/dpsim/docs/concepts/alternative-solvers/">alternative solution methods&lt;/a>, is exact: the
removed branches are restored within the same step, so the answer matches the intact network. It
does not allow the parts to be advanced independently.&lt;/p>
&lt;p>The travelling-wave line under &lt;a href="https://sogno.energy/dpsim/docs/concepts/models/branches/">branches&lt;/a> is exact for the lossless
line it derives from, and its delay is physical rather than introduced. It requires that a real line
with a travel time longer than the step exists at the splitting point.&lt;/p>
&lt;p>The ideal transformer model requires no such line and can split anywhere, and pays for that with a
delay that has no physical counterpart. It is the general method and the least accurate of the
three.&lt;/p></description></item><item><title>Docs: State-Space Nodal</title><link>https://sogno.energy/dpsim/docs/concepts/state-space-nodal/</link><pubDate>Tue, 23 Jun 2026 00:00:00 +0000</pubDate><guid>https://sogno.energy/dpsim/docs/concepts/state-space-nodal/</guid><description>
&lt;p>The state-space nodal (SSN) method represents a component by its own continuous
state-space model and couples it to the network through the nodal admittance
matrix. A component is described by&lt;/p>
&lt;div class="math">$$\frac{d\mathbf{x}}{dt} = \mathbf{A}\mathbf{x} + \mathbf{B}\mathbf{u},
\qquad
\mathbf{y} = \mathbf{C}\mathbf{x} + \mathbf{D}\mathbf{u},$$&lt;/div>&lt;p>where $\mathbf{x}$ is the internal state, and the input $\mathbf{u}$ and output
$\mathbf{y}$ are the terminal quantities exchanged with the network (a voltage and
the corresponding current). Trapezoidal discretisation of $(\mathbf{A}, \mathbf{B})$
yields a discrete model $(\mathbf{A}_d, \mathbf{B}_d)$ and a Norton equivalent: a
constant conductance $\mathbf{W}$ stamped into the system matrix plus a history
current source recomputed each step from the previous state and input. Because the
component is solved simultaneously with the network in the same nodal system, SSN
is numerically robust without the parasitic snubbers that delayed
current-injection schemes require.&lt;/p>
&lt;p>This builds directly on &lt;a href="https://sogno.energy/dpsim/docs/concepts/nodal-analysis/">Nodal Analysis&lt;/a> and is
the companion of
&lt;a href="https://sogno.energy/dpsim/docs/concepts/state-space-extraction-theory/">State-Space Extraction&lt;/a>, which
recovers a state-space model from an MNA simulation rather than starting from one.&lt;/p>
&lt;h2 id="shift-to-the-dynamic-phasor-envelope">Shift to the Dynamic-Phasor Envelope&lt;/h2>
&lt;p>The same component model is discretised differently in an envelope domain: the operator becomes
$\frac{d}{dt} + j\omega_s$, so what is discretised is $\mathbf{A} - j\omega_s\mathbf{I}$ rather than
$\mathbf{A}$, and splitting the envelope into real and imaginary parts turns that into a real system
of twice the size. This is derived in full under
&lt;a href="https://sogno.energy/dpsim/docs/concepts/ssn-domain-formulation/">SSN across domains&lt;/a>, together with why the equivalent
admittance is complex in an envelope domain and real in an instantaneous one.&lt;/p>
&lt;p>The real-augmented form matches how the rest of the dynamic phasor system is already assembled: a
complex admittance $g = g_r + j g_i$ is stamped as the real block
$\left[\begin{smallmatrix} g_r &amp;amp; -g_i \ g_i &amp;amp; g_r \end{smallmatrix}\right]$, with real and
imaginary node parts in separate halves of a real-valued system. The SSN component therefore needs
no complex assembly of its own, and the trapezoidal discretisation used by the instantaneous models
applies unchanged.&lt;/p>
&lt;h2 id="components">Components&lt;/h2>
&lt;p>The single-phase dynamic-phasor SSN models are:&lt;/p>
&lt;ul>
&lt;li>&lt;code>Full_Serial_RLC&lt;/code>, a series resistor-inductor-capacitor one-port with a
hand-derived state-space model, used as the reference component.&lt;/li>
&lt;li>&lt;code>GenericTwoTerminalVTypeSSN&lt;/code> and &lt;code>GenericTwoTerminalITypeSSN&lt;/code>, which accept a
user-supplied $(\mathbf{A}, \mathbf{B}, \mathbf{C}, \mathbf{D})$ and build the
V-type (voltage input, current output) or I-type (current input, voltage
output) stamping accordingly.&lt;/li>
&lt;/ul>
&lt;p>All three reproduce the classical dynamic-phasor stamping of the same circuit,
and the reconstructed time-domain waveform matches the EMT and EMT-SSN results
within discretisation error.&lt;/p>
&lt;h2 id="three-phase-components">Three-Phase Components&lt;/h2>
&lt;p>The same real-augmented model extends per phase to &lt;code>DP::Ph3&lt;/code>. The
$3 \times 3$ $\mathbf{A}$, $\mathbf{B}$, $\mathbf{C}$, $\mathbf{D}$ matrices
are general, so off-diagonal entries can couple the phases together:&lt;/p>
&lt;ul>
&lt;li>&lt;code>Full_Serial_RLC&lt;/code>, the three-phase series RLC one-port.&lt;/li>
&lt;li>&lt;code>GenericTwoTerminalVTypeSSN&lt;/code> and &lt;code>GenericTwoTerminalITypeSSN&lt;/code>, the
three-phase generic V-type and I-type components.&lt;/li>
&lt;/ul>
&lt;p>As in the single-phase case, all three reproduce the classical three-phase
dynamic-phasor stamping exactly, and the reconstructed time-domain waveform
matches the EMT and EMT-SSN results within discretisation error once
corrected for the RMS-to-peak scaling that &lt;code>EMT::Ph3&lt;/code> sources apply and
&lt;code>DP::Ph3&lt;/code> sources do not, since the DP envelope already carries the complex
amplitude directly. The notebooks below only exercise the symmetrical,
diagonal case; coupling between phases is not covered by existing tests.&lt;/p>
&lt;h2 id="variable-components">Variable Components&lt;/h2>
&lt;p>The components above are fixed-parameter LTI systems: $(\mathbf{A}, \mathbf{B},
\mathbf{C}, \mathbf{D})$ are built once from the component&amp;rsquo;s parameters and never
change. Some SSN components instead depend on the operating point and are
relinearized and re-stamped every step: the same simultaneous nodal solve, but around
a Jacobian frozen at the previous step&amp;rsquo;s converged state rather than a constant
matrix. The averaged grid-following inverter is one such component, and its
three-phase analogue carries a complex envelope per phase while sharing a single
positive-sequence dq control frame; see
&lt;a href="https://sogno.energy/dpsim/docs/concepts/models/power-electronics/">Power Electronics&lt;/a> for their state vectors and
model equations.&lt;/p>
&lt;h2 id="validation-and-examples">Validation and Examples&lt;/h2>
&lt;p>Two notebooks accompany the single-phase models, both on a single-carrier
series RLC one-port. &lt;code>examples/Notebooks/Circuits/DP_generalizedSSN_RLC.ipynb&lt;/code>
validates the DP-SSN models against the classical dynamic-phasor stamping and
the EMT and EMT-SSN waveforms. &lt;code>examples/Notebooks/Circuits/DP_SSN_RLC_accuracy.ipynb&lt;/code>
studies the time-step and frequency-dependent accuracy against a small-step
EMT reference.&lt;/p>
&lt;p>The three-phase analogues,
&lt;code>examples/Notebooks/Circuits/DP_Ph3_generalizedSSN_RLC.ipynb&lt;/code> and
&lt;code>examples/Notebooks/Circuits/DP_Ph3_SSN_RLC_accuracy.ipynb&lt;/code>, repeat both
studies on &lt;code>DP::Ph3&lt;/code> circuits, including a current-driven network with generic
V-type and I-type components and a three-phase fault transient.&lt;/p>
&lt;h2 id="further-reading">Further Reading&lt;/h2>
&lt;ul>
&lt;li>C. Dufour, J. Mahseredjian, and J. Bélanger, &lt;em>A Combined State-Space Nodal Method for the Simulation of Power System Transients&lt;/em>, &lt;em>IEEE Transactions on Power Delivery&lt;/em>, vol. 26, no. 2, pp. 928–935, 2011. &lt;a href="https://doi.org/10.1109/TPWRD.2010.2090364">https://doi.org/10.1109/TPWRD.2010.2090364&lt;/a>&lt;/li>
&lt;li>C. Dufour and D. S. Nasrallah, &lt;em>State-space-nodal rotating machine models with improved numerical stability&lt;/em>, &lt;em>IECON 2016 – 42nd Annual Conference of the IEEE Industrial Electronics Society&lt;/em>, 2016. &lt;a href="https://doi.org/10.1109/IECON.2016.7793690">https://doi.org/10.1109/IECON.2016.7793690&lt;/a>&lt;/li>
&lt;li>A. A. Kida, A. C. S. Lima, F. A. Moreira, J. R. Martí, and J. Tarazona, &lt;em>Inaccuracies due to the frequency warping in simulation of electrical systems using combined state–space nodal analysis&lt;/em>, &lt;em>Electric Power Systems Research&lt;/em>, vol. 223, art. 109657, 2023. &lt;a href="https://doi.org/10.1016/j.epsr.2023.109657">https://doi.org/10.1016/j.epsr.2023.109657&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>Docs: State-Space Extraction</title><link>https://sogno.energy/dpsim/docs/developer-guide/solvers/state-space-extraction/</link><pubDate>Thu, 28 May 2026 00:00:00 +0000</pubDate><guid>https://sogno.energy/dpsim/docs/developer-guide/solvers/state-space-extraction/</guid><description>
&lt;p>The method itself, what the extracted model means and where it is valid, is derived under
&lt;a href="https://sogno.energy/dpsim/docs/concepts/state-space-extraction-theory/">state-space extraction&lt;/a>.
This page covers enabling it and reading the result.&lt;/p>
&lt;p>State-space extraction is optional and can be enabled through the &lt;code>Simulation&lt;/code> API. During simulation setup, the MNA solver creates an &lt;code>MNAStateSpaceExtractor&lt;/code>. During the solver task flow, a state-space extraction task uses the active direct linear solver to update the extracted discrete-time state matrix.&lt;/p>
&lt;h2 id="main-classes">Main classes&lt;/h2>
&lt;p>The implementation is organized around three main parts:&lt;/p>
&lt;ul>
&lt;li>&lt;code>MNAStateSpaceExtractor&lt;/code> assembles and stores the extracted discrete-time state matrix.&lt;/li>
&lt;li>&lt;code>MNAStateSpaceContributor&lt;/code> represents the state-space contribution of one supported component.&lt;/li>
&lt;li>&lt;code>MNAStateSpaceContributorFactory&lt;/code> creates contributors for supported MNA components.&lt;/li>
&lt;/ul>
&lt;p>The extractor is owned by the MNA solver. Component contributors are created during solver initialization and are used to stamp the local matrices needed for the MNA-coupled state-space formulation.&lt;/p>
&lt;p>For the components that support extraction in each domain, see
&lt;a href="https://sogno.energy/dpsim/docs/reference/state-space-extraction-support/">state-space extraction support&lt;/a>.&lt;/p>
&lt;h2 id="usage">Usage&lt;/h2>
&lt;p>In C++, state-space extraction can be enabled as follows. The example below
uses EMT Ph3; replace &lt;code>Domain::EMT&lt;/code> with &lt;code>Domain::DP&lt;/code> for DP Ph1:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-cpp" data-lang="cpp">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#000">Simulation&lt;/span> &lt;span style="color:#000">sim&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#4e9a06">&amp;#34;Example&amp;#34;&lt;/span>&lt;span style="color:#000;font-weight:bold">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#000">sim&lt;/span>&lt;span style="color:#000;font-weight:bold">.&lt;/span>&lt;span style="color:#000">setDomain&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#000">Domain&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">::&lt;/span>&lt;span style="color:#000">EMT&lt;/span>&lt;span style="color:#000;font-weight:bold">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#000">sim&lt;/span>&lt;span style="color:#000;font-weight:bold">.&lt;/span>&lt;span style="color:#000">setSolverType&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#000">Solver&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">::&lt;/span>&lt;span style="color:#000">Type&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">::&lt;/span>&lt;span style="color:#000">MNA&lt;/span>&lt;span style="color:#000;font-weight:bold">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#000">sim&lt;/span>&lt;span style="color:#000;font-weight:bold">.&lt;/span>&lt;span style="color:#000">doStateSpaceExtraction&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#204a87">true&lt;/span>&lt;span style="color:#000;font-weight:bold">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#000">sim&lt;/span>&lt;span style="color:#000;font-weight:bold">.&lt;/span>&lt;span style="color:#000">run&lt;/span>&lt;span style="color:#000;font-weight:bold">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#204a87;font-weight:bold">const&lt;/span> &lt;span style="color:#204a87;font-weight:bold">auto&lt;/span> &lt;span style="color:#ce5c00;font-weight:bold">&amp;amp;&lt;/span>&lt;span style="color:#000">extractor&lt;/span> &lt;span style="color:#ce5c00;font-weight:bold">=&lt;/span> &lt;span style="color:#000">sim&lt;/span>&lt;span style="color:#000;font-weight:bold">.&lt;/span>&lt;span style="color:#000">getStateSpaceExtractor&lt;/span>&lt;span style="color:#000;font-weight:bold">();&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#204a87;font-weight:bold">const&lt;/span> &lt;span style="color:#000">Matrix&lt;/span> &lt;span style="color:#ce5c00;font-weight:bold">&amp;amp;&lt;/span>&lt;span style="color:#000">Ad&lt;/span> &lt;span style="color:#ce5c00;font-weight:bold">=&lt;/span> &lt;span style="color:#000">extractor&lt;/span>&lt;span style="color:#000;font-weight:bold">.&lt;/span>&lt;span style="color:#000">getDiscreteStateMatrix&lt;/span>&lt;span style="color:#000;font-weight:bold">();&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>In Python, the corresponding API is shown below. Replace
&lt;code>dpsimpy.Domain.EMT&lt;/code> with &lt;code>dpsimpy.Domain.DP&lt;/code> for DP Ph1:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-python" data-lang="python">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#000">sim&lt;/span> &lt;span style="color:#ce5c00;font-weight:bold">=&lt;/span> &lt;span style="color:#000">dpsimpy&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">Simulation&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#4e9a06">&amp;#34;Example&amp;#34;&lt;/span>&lt;span style="color:#000;font-weight:bold">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#000">sim&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">set_domain&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#000">dpsimpy&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">Domain&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">EMT&lt;/span>&lt;span style="color:#000;font-weight:bold">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#000">sim&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">set_solver&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#000">dpsimpy&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">Solver&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">MNA&lt;/span>&lt;span style="color:#000;font-weight:bold">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#000">sim&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">do_state_space_extraction&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#204a87;font-weight:bold">True&lt;/span>&lt;span style="color:#000;font-weight:bold">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#000">sim&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">run&lt;/span>&lt;span style="color:#000;font-weight:bold">()&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#000">extractor&lt;/span> &lt;span style="color:#ce5c00;font-weight:bold">=&lt;/span> &lt;span style="color:#000">sim&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">get_state_space_extractor&lt;/span>&lt;span style="color:#000;font-weight:bold">()&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#000">Ad&lt;/span> &lt;span style="color:#ce5c00;font-weight:bold">=&lt;/span> &lt;span style="color:#000">extractor&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">get_discrete_state_matrix&lt;/span>&lt;span style="color:#000;font-weight:bold">()&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="modal-analysis-of-the-extracted-model">Modal analysis of the extracted model&lt;/h2>
&lt;p>&lt;code>StateSpaceModalAnalysis&lt;/code> is constructed from an &lt;code>MNAStateSpaceExtractor&lt;/code> and computes the modes of
whatever the extractor last produced. The method is described under
&lt;a href="https://sogno.energy/dpsim/docs/concepts/modal-analysis/">modal analysis&lt;/a>.&lt;/p>
&lt;p>&lt;code>update()&lt;/code> runs &lt;code>Eigen::EigenSolver&lt;/code> on the discrete state matrix and throws if it does not converge.
It then maps each discrete eigenvalue to the continuous plane with &lt;code>2 / dt * (z - 1) / (z + 1)&lt;/code> and
keeps both sets, retrievable through &lt;code>getDiscreteEigenvalues&lt;/code> and &lt;code>getContinuousEigenvalues&lt;/code>.&lt;/p>
&lt;p>Participation factors are the elementwise product of the right eigenvectors with the transpose of the
left ones. They require inverting the right eigenvector matrix, so &lt;code>update()&lt;/code> throws with an explicit
message when that matrix is singular. That happens for a defective state matrix, which is a property
of the system rather than a numerical problem; the eigenvalues are still valid in that case, only the
participation factors are unavailable.&lt;/p>
&lt;p>&lt;code>setAnalysisFrame&lt;/code> selects between &lt;code>StateSpaceAnalysisFrame::Native&lt;/code>, which analyses the states as the
components hold them, and &lt;code>GlobalDQ0&lt;/code>, which transforms into one common frame first. The second needs
&lt;code>setGlobalDq0Frame(omega, theta0)&lt;/code>. &lt;code>getStateNames&lt;/code> returns names matching the frame in use, so a
participation factor can be attributed to a named state rather than to an index.&lt;/p>
&lt;h2 id="examples">Examples&lt;/h2>
&lt;p>The feature is demonstrated in:&lt;/p>
&lt;ul>
&lt;li>&lt;a href="https://github.com/sogno-platform/dpsim/blob/master/dpsim/examples/cxx/StateSpace/EMT_Ph3_RLC_StateSpaceExtraction.cpp">EMT Ph3 RLC extraction&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://github.com/sogno-platform/dpsim/blob/master/dpsim/examples/cxx/StateSpace/EMT_Ph3_Composite_StateSpaceExtraction.cpp">EMT Ph3 composite extraction&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://github.com/sogno-platform/dpsim/blob/master/dpsim/examples/cxx/StateSpace/DP_Ph1_RLC_StateSpaceExtraction.cpp">DP Ph1 RLC extraction&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://github.com/sogno-platform/dpsim/blob/master/dpsim/examples/cxx/StateSpace/DP_Ph1_Composite_StateSpaceExtraction.cpp">DP Ph1 composite extraction&lt;/a>&lt;/li>
&lt;/ul>
&lt;p>Equivalent Python notebooks are available in
&lt;a href="https://github.com/sogno-platform/dpsim/tree/master/examples/Notebooks/StateSpace">&lt;code>examples/Notebooks/StateSpace&lt;/code>&lt;/a>.&lt;/p></description></item><item><title>Docs: Attribute Usage Guidelines</title><link>https://sogno.energy/dpsim/docs/developer-guide/attributes-and-scheduling/attribute-usage/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://sogno.energy/dpsim/docs/developer-guide/attributes-and-scheduling/attribute-usage/</guid><description>
&lt;p>This page gives practical rules for deciding when a model variable should be a DPsim attribute. For details on the attribute mechanism itself, see &lt;a href="https://sogno.energy/dpsim/docs/developer-guide/attributes-and-scheduling/attributes/">attributes&lt;/a>.&lt;/p>
&lt;h2 id="rule-of-thumb">Rule of Thumb&lt;/h2>
&lt;p>Use an attribute if the value must be visible to DPsim infrastructure, for example logging, interfaces, Python access, string-based lookup, or scheduling.&lt;/p>
&lt;p>Otherwise, prefer a normal C++ member variable or a local variable.&lt;/p>
&lt;h2 id="quick-decision-checklist">Quick Decision Checklist&lt;/h2>
&lt;p>Before adding a new attribute, ask:&lt;/p>
&lt;ol>
&lt;li>Does it need to be logged?&lt;/li>
&lt;li>Does it need to be imported or exported?&lt;/li>
&lt;li>Does it need to be accessed from Python or by name?&lt;/li>
&lt;li>Is it used as a scheduler dependency?&lt;/li>
&lt;li>Is it an externally relevant model input, output, state, or setpoint?&lt;/li>
&lt;li>Is a normal C++ variable insufficient?&lt;/li>
&lt;/ol>
&lt;p>If the answer to all questions is no, do not make it an attribute.&lt;/p>
&lt;h2 id="use-an-attribute-for">Use an Attribute For&lt;/h2>
&lt;p>Use an attribute if the value:&lt;/p>
&lt;ul>
&lt;li>should be logged&lt;/li>
&lt;li>should be imported or exported through an interface&lt;/li>
&lt;li>should be accessed from Python or generic code by name&lt;/li>
&lt;li>is read or modified by scheduled tasks&lt;/li>
&lt;li>is an externally relevant model input, output, state, or setpoint&lt;/li>
&lt;li>is a derived view of another attribute, for example one matrix coefficient&lt;/li>
&lt;/ul>
&lt;p>Typical examples are interface voltages and currents, source references, controller setpoints, and values exchanged through VILLASnode.&lt;/p>
&lt;h2 id="do-not-use-an-attribute-for">Do Not Use an Attribute For&lt;/h2>
&lt;p>Prefer a normal C++ variable if the value:&lt;/p>
&lt;ul>
&lt;li>is only used inside one method&lt;/li>
&lt;li>is a temporary intermediate result&lt;/li>
&lt;li>is a cached coefficient or solver helper&lt;/li>
&lt;li>is a fixed implementation detail&lt;/li>
&lt;li>duplicates another existing attribute&lt;/li>
&lt;li>never needs logging, interface access, Python access, or scheduling&lt;/li>
&lt;/ul>
&lt;p>Do not create attributes for every variable in the model equations.&lt;/p>
&lt;h2 id="choose-the-simplest-attribute-type">Choose the Simplest Attribute Type&lt;/h2>
&lt;p>If decided that a value should be an attribute, choose the simplest suitable attribute type.&lt;/p>
&lt;p>Prefer a static attribute when the value is stored directly by the component:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-cpp" data-lang="cpp">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#204a87;font-weight:bold">const&lt;/span> &lt;span style="color:#000">Attribute&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">&amp;lt;&lt;/span>&lt;span style="color:#000">Real&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">&amp;gt;::&lt;/span>&lt;span style="color:#000">Ptr&lt;/span> &lt;span style="color:#000">mPower&lt;/span>&lt;span style="color:#000;font-weight:bold">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#000">MyComponent&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">::&lt;/span>&lt;span style="color:#000">MyComponent&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#204a87;font-weight:bold">const&lt;/span> &lt;span style="color:#000">String&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">&amp;amp;&lt;/span> &lt;span style="color:#000">name&lt;/span>&lt;span style="color:#000;font-weight:bold">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#ce5c00;font-weight:bold">:&lt;/span> &lt;span style="color:#000">IdentifiedObject&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#000">name&lt;/span>&lt;span style="color:#000;font-weight:bold">),&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#000">mPower&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#000">mAttributes&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">-&amp;gt;&lt;/span>&lt;span style="color:#000">create&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">&amp;lt;&lt;/span>&lt;span style="color:#000">Real&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">&amp;gt;&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#4e9a06">&amp;#34;P&amp;#34;&lt;/span>&lt;span style="color:#000;font-weight:bold">,&lt;/span> &lt;span style="color:#0000cf;font-weight:bold">0.0&lt;/span>&lt;span style="color:#000;font-weight:bold">))&lt;/span> &lt;span style="color:#000;font-weight:bold">{}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Use dynamic, referenced, or derived attributes only when the value must depend on another attribute.&lt;/p>
&lt;p>For example, use a derived attribute when exporting one coefficient of a matrix or vector attribute:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-cpp" data-lang="cpp">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#000">intf&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">-&amp;gt;&lt;/span>&lt;span style="color:#000">exportAttribute&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#000">component&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">-&amp;gt;&lt;/span>&lt;span style="color:#000">mIntfCurrent&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">-&amp;gt;&lt;/span>&lt;span style="color:#000">deriveCoeff&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">&amp;lt;&lt;/span>&lt;span style="color:#000">Complex&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">&amp;gt;&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#0000cf;font-weight:bold">0&lt;/span>&lt;span style="color:#000;font-weight:bold">,&lt;/span> &lt;span style="color:#0000cf;font-weight:bold">0&lt;/span>&lt;span style="color:#000;font-weight:bold">),&lt;/span> &lt;span style="color:#0000cf;font-weight:bold">0&lt;/span>&lt;span style="color:#000;font-weight:bold">,&lt;/span> &lt;span style="color:#204a87">true&lt;/span>&lt;span style="color:#000;font-weight:bold">);&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Avoid long chains of dynamic, referenced, or derived attributes unless they are really needed.&lt;/p>
&lt;h2 id="access-attributes-in-c">Access Attributes in C++&lt;/h2>
&lt;p>In model code, prefer typed attribute members:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-cpp" data-lang="cpp">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#000">mPower&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">-&amp;gt;&lt;/span>&lt;span style="color:#000">set&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#000">power&lt;/span>&lt;span style="color:#000;font-weight:bold">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#204a87;font-weight:bold">const&lt;/span> &lt;span style="color:#000">Real&lt;/span> &lt;span style="color:#000">power&lt;/span> &lt;span style="color:#ce5c00;font-weight:bold">=&lt;/span> &lt;span style="color:#ce5c00;font-weight:bold">**&lt;/span>&lt;span style="color:#000">mPower&lt;/span>&lt;span style="color:#000;font-weight:bold">;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Use string-based lookup mainly in generic code, logging, interfaces, tests, or Python-style access:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-cpp" data-lang="cpp">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#000">logger&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">-&amp;gt;&lt;/span>&lt;span style="color:#000">logAttribute&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#4e9a06">&amp;#34;P&amp;#34;&lt;/span>&lt;span style="color:#000;font-weight:bold">,&lt;/span> &lt;span style="color:#000">component&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">-&amp;gt;&lt;/span>&lt;span style="color:#000">mPower&lt;/span>&lt;span style="color:#000;font-weight:bold">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#000">intf&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">-&amp;gt;&lt;/span>&lt;span style="color:#000">exportAttribute&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#000">component&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">-&amp;gt;&lt;/span>&lt;span style="color:#000">attribute&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#4e9a06">&amp;#34;P&amp;#34;&lt;/span>&lt;span style="color:#000;font-weight:bold">),&lt;/span> &lt;span style="color:#0000cf;font-weight:bold">0&lt;/span>&lt;span style="color:#000;font-weight:bold">,&lt;/span> &lt;span style="color:#204a87">true&lt;/span>&lt;span style="color:#000;font-weight:bold">);&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>When assigning a new value, prefer &lt;code>set()&lt;/code> if update tasks should be triggered or if the assignment should be explicit. Direct mutable access through &lt;code>**attribute&lt;/code> can be used for simple static attributes, but it does not express this intent as clearly.&lt;/p>
&lt;h2 id="examples">Examples&lt;/h2>
&lt;h3 id="private-member-variable-no-need-to-use-an-attribute">Private member variable: no need to use an attribute&lt;/h3>
&lt;p>This value is stored as a member because it is used by several functions of the class. It is still internal to the implementation: it does not need to be logged, imported or exported, used by the scheduler, or accessed by name.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-cpp" data-lang="cpp">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#204a87;font-weight:bold">class&lt;/span> &lt;span style="color:#000">MyComponent&lt;/span> &lt;span style="color:#ce5c00;font-weight:bold">:&lt;/span> &lt;span style="color:#204a87;font-weight:bold">public&lt;/span> &lt;span style="color:#000">IdentifiedObject&lt;/span> &lt;span style="color:#000;font-weight:bold">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#204a87;font-weight:bold">private&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">:&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#000">Real&lt;/span> &lt;span style="color:#000">mConductance&lt;/span> &lt;span style="color:#ce5c00;font-weight:bold">=&lt;/span> &lt;span style="color:#0000cf;font-weight:bold">0.0&lt;/span>&lt;span style="color:#000;font-weight:bold">;&lt;/span> &lt;span style="color:#8f5902;font-style:italic">// used internally by several methods
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#8f5902;font-style:italic">&lt;/span>&lt;span style="color:#000;font-weight:bold">};&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="externally-visible-output-use-an-attribute">Externally visible output: use an attribute&lt;/h3>
&lt;p>This value is a model output. It may be useful for logging, plotting, interfaces, tests, or Python access, so it should be registered as an attribute.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-cpp" data-lang="cpp">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#204a87;font-weight:bold">class&lt;/span> &lt;span style="color:#000">MyComponent&lt;/span> &lt;span style="color:#ce5c00;font-weight:bold">:&lt;/span> &lt;span style="color:#204a87;font-weight:bold">public&lt;/span> &lt;span style="color:#000">IdentifiedObject&lt;/span> &lt;span style="color:#000;font-weight:bold">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#204a87;font-weight:bold">public&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">:&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#204a87;font-weight:bold">const&lt;/span> &lt;span style="color:#000">Attribute&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">&amp;lt;&lt;/span>&lt;span style="color:#000">Real&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">&amp;gt;::&lt;/span>&lt;span style="color:#000">Ptr&lt;/span> &lt;span style="color:#000">mPower&lt;/span>&lt;span style="color:#000;font-weight:bold">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#204a87;font-weight:bold">explicit&lt;/span> &lt;span style="color:#000">MyComponent&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#204a87;font-weight:bold">const&lt;/span> &lt;span style="color:#000">String&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">&amp;amp;&lt;/span> &lt;span style="color:#000">name&lt;/span>&lt;span style="color:#000;font-weight:bold">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#ce5c00;font-weight:bold">:&lt;/span> &lt;span style="color:#000">IdentifiedObject&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#000">name&lt;/span>&lt;span style="color:#000;font-weight:bold">),&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#000">mPower&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#000">mAttributes&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">-&amp;gt;&lt;/span>&lt;span style="color:#000">create&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">&amp;lt;&lt;/span>&lt;span style="color:#000">Real&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">&amp;gt;&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#4e9a06">&amp;#34;P&amp;#34;&lt;/span>&lt;span style="color:#000;font-weight:bold">,&lt;/span> &lt;span style="color:#0000cf;font-weight:bold">0.0&lt;/span>&lt;span style="color:#000;font-weight:bold">))&lt;/span> &lt;span style="color:#000;font-weight:bold">{}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#204a87;font-weight:bold">void&lt;/span> &lt;span style="color:#000">updatePower&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#000">Real&lt;/span> &lt;span style="color:#000">power&lt;/span>&lt;span style="color:#000;font-weight:bold">)&lt;/span> &lt;span style="color:#000;font-weight:bold">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#000">mPower&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">-&amp;gt;&lt;/span>&lt;span style="color:#000">set&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#000">power&lt;/span>&lt;span style="color:#000;font-weight:bold">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#000;font-weight:bold">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#000;font-weight:bold">};&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="runtime-setpoint-use-an-attribute">Runtime setpoint: use an attribute&lt;/h3>
&lt;p>This value is a model input or setpoint. It may be changed from outside the component, for example through Python, an interface, or a test setup.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-cpp" data-lang="cpp">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#204a87;font-weight:bold">class&lt;/span> &lt;span style="color:#000">MySource&lt;/span> &lt;span style="color:#ce5c00;font-weight:bold">:&lt;/span> &lt;span style="color:#204a87;font-weight:bold">public&lt;/span> &lt;span style="color:#000">IdentifiedObject&lt;/span> &lt;span style="color:#000;font-weight:bold">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#204a87;font-weight:bold">public&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">:&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#204a87;font-weight:bold">const&lt;/span> &lt;span style="color:#000">Attribute&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">&amp;lt;&lt;/span>&lt;span style="color:#000">Real&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">&amp;gt;::&lt;/span>&lt;span style="color:#000">Ptr&lt;/span> &lt;span style="color:#000">mVoltageRef&lt;/span>&lt;span style="color:#000;font-weight:bold">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#204a87;font-weight:bold">explicit&lt;/span> &lt;span style="color:#000">MySource&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#204a87;font-weight:bold">const&lt;/span> &lt;span style="color:#000">String&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">&amp;amp;&lt;/span> &lt;span style="color:#000">name&lt;/span>&lt;span style="color:#000;font-weight:bold">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#ce5c00;font-weight:bold">:&lt;/span> &lt;span style="color:#000">IdentifiedObject&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#000">name&lt;/span>&lt;span style="color:#000;font-weight:bold">),&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#000">mVoltageRef&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#000">mAttributes&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">-&amp;gt;&lt;/span>&lt;span style="color:#000">create&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">&amp;lt;&lt;/span>&lt;span style="color:#000">Real&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">&amp;gt;&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#4e9a06">&amp;#34;V_ref&amp;#34;&lt;/span>&lt;span style="color:#000;font-weight:bold">,&lt;/span> &lt;span style="color:#0000cf;font-weight:bold">0.0&lt;/span>&lt;span style="color:#000;font-weight:bold">))&lt;/span> &lt;span style="color:#000;font-weight:bold">{}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#204a87;font-weight:bold">void&lt;/span> &lt;span style="color:#000">setParameters&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#000">Real&lt;/span> &lt;span style="color:#000">voltageRef&lt;/span>&lt;span style="color:#000;font-weight:bold">)&lt;/span> &lt;span style="color:#000;font-weight:bold">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#000">mVoltageRef&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">-&amp;gt;&lt;/span>&lt;span style="color:#000">set&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#000">voltageRef&lt;/span>&lt;span style="color:#000;font-weight:bold">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#000;font-weight:bold">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#000;font-weight:bold">};&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="derived-scalar-value-use-a-derived-attribute">Derived scalar value: use a derived attribute&lt;/h3>
&lt;p>This value is not stored separately. It is a scalar view of an existing vector or matrix attribute, which avoids duplicating data manually.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-cpp" data-lang="cpp">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#000">intf&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">-&amp;gt;&lt;/span>&lt;span style="color:#000">exportAttribute&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#000">component&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">-&amp;gt;&lt;/span>&lt;span style="color:#000">mIntfCurrent&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">-&amp;gt;&lt;/span>&lt;span style="color:#000">deriveCoeff&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">&amp;lt;&lt;/span>&lt;span style="color:#000">Complex&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">&amp;gt;&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#0000cf;font-weight:bold">0&lt;/span>&lt;span style="color:#000;font-weight:bold">,&lt;/span> &lt;span style="color:#0000cf;font-weight:bold">0&lt;/span>&lt;span style="color:#000;font-weight:bold">),&lt;/span> &lt;span style="color:#0000cf;font-weight:bold">0&lt;/span>&lt;span style="color:#000;font-weight:bold">,&lt;/span> &lt;span style="color:#204a87">true&lt;/span>&lt;span style="color:#000;font-weight:bold">);&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div></description></item><item><title>Docs: EMT Ph3 Grid-Forming Inverter</title><link>https://sogno.energy/dpsim/docs/concepts/models/power-electronics/emt-ph3-grid-forming-vsi/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://sogno.energy/dpsim/docs/concepts/models/power-electronics/emt-ph3-grid-forming-vsi/</guid><description>
&lt;p>This model represents a grid-forming averaged voltage source inverter in the EMT domain.
The control structure follows the state-space grid-forming converter of &lt;a href="https://ieeexplore.ieee.org/document/9806927">Gao2022&lt;/a> (VSG algorithm loop, voltage loop, current loop with active damping), whose grid-following counterpart in the same paper is the basis for the averaged inverter above; the inner voltage/current control and LC filter modeling follow &lt;a href="https://ieeexplore.ieee.org/book/5237659">Yazdani2010&lt;/a>.
Like the grid-following inverter above it is a variable state-space nodal component stamped directly into the MNA system, but instead of a PLL that locks to the grid it carries its own virtual synchronous machine (VSG): the internal angle and voltage magnitude are states driven by active- and reactive-power balance, so the inverter imposes a voltage and can run islanded.
The model includes the VSG swing dynamics, a reactive-power/voltage excitation loop, filtered active/reactive power measurement, a cascaded voltage and current controller, a first-order converter/digital-delay approximation, and an LC filter with coupling resistance to the grid node.&lt;/p>
&lt;p>The terminal input is the PCC voltage vector&lt;/p>
&lt;div class="math">$$\mathbf{u} =
\begin{bmatrix}
u_a &amp; u_b &amp; u_c
\end{bmatrix}^\top ,$$&lt;/div>&lt;p>and the 17-element state vector is&lt;/p>
&lt;div class="math">$$\mathbf{x} =
\begin{bmatrix}
P &amp; Q &amp; \omega &amp; \theta &amp; E &amp;
\xi_{v,d} &amp; \xi_{v,q} &amp;
\xi_{i,d} &amp; \xi_{i,q} &amp;
v_{\mathrm{del},d} &amp; v_{\mathrm{del},q} &amp;
v_{c,a} &amp; v_{c,b} &amp; v_{c,c} &amp;
i_{f,a} &amp; i_{f,b} &amp; i_{f,c}
\end{bmatrix}^\top ,$$&lt;/div>&lt;p>where $\theta$ is the VSG angle (there is no PLL), $E$ is the excitation-controlled voltage magnitude, $\xi_{v}$, $\xi_{i}$ are the voltage- and current-loop integrators, and $v_{\mathrm{del}}$ are the two delay states.&lt;/p>
&lt;p>The model output is the interface current injected into the MNA system,&lt;/p>
&lt;div class="math">$$\mathbf{y} =
\frac{\mathbf{u} - \mathbf{v}_c}{R_c}.$$&lt;/div>&lt;h2 id="control-structure">Control structure&lt;/h2>
&lt;div class="mermaid">
graph LR
U["PCC voltage u"] --> FILT["LC filter&lt;br/>vc, if"]
FILT --> MEAS["Power measurement&lt;br/>p, q"]
MEAS --> PF["Measurement filters&lt;br/>P, Q"]
PF -->|P| SWING["VSG swing&lt;br/>omega, theta"]
PF -->|Q| EXC["Excitation /&lt;br/>Q-V droop -> E"]
EXC --> VZ["Virtual impedance&lt;br/>E - Zv*if"]
VZ --> VCTRL["Voltage controller&lt;br/>-> i_ref"]
VCTRL --> ICTRL["Current controller&lt;br/>-> v_conv"]
ICTRL --> DELAY["Converter delay"]
DELAY --> FILT
SWING -->|theta| VCTRL
FILT --> Y["Interface current y"]
&lt;/div>
&lt;p>The virtual synchronous machine sets the internal angle from the active-power balance and the internal magnitude from the reactive-power/voltage loop; the cascaded voltage and current controllers then track that internal reference through the LC filter. The dashed grid-connected extensions (virtual impedance, feed-forward scaling, Q-V droop) are described below.&lt;/p>
&lt;h2 id="model-equations">Model equations&lt;/h2>
&lt;p>The physical grid current, positive for injection into the grid, is&lt;/p>
&lt;div class="math">$$\mathbf{i}_g = \frac{\mathbf{v}_c - \mathbf{u}}{R_c}.$$&lt;/div>&lt;p>All dq quantities use the VSG angle $\theta$ (amplitude-invariant Park transform $\mathbf{T}(\theta)$),&lt;/p>
&lt;div class="math">$$\mathbf{v}_{c,dq} = \mathbf{T}(\theta)\mathbf{v}_c, \qquad
\mathbf{i}_{f,dq} = \mathbf{T}(\theta)\mathbf{i}_f, \qquad
\mathbf{i}_{g,dq} = \mathbf{T}(\theta)\mathbf{i}_g,$$&lt;/div>&lt;p>and the capacitor current is $\mathbf{i}&lt;em>{\mathrm{cap},dq} = \mathbf{i}&lt;/em>{f,dq} - \mathbf{i}_{g,dq}$.
Because the Park transform is amplitude invariant, three-phase instantaneous power carries the factor $3/2$,&lt;/p>
&lt;div class="math">$$p = \tfrac{3}{2}\,(v_{c,d} i_{g,d} + v_{c,q} i_{g,q}),
\qquad
q = \tfrac{3}{2}\,(v_{c,q} i_{g,d} - v_{c,d} i_{g,q}),$$&lt;/div>&lt;p>and the PCC voltage magnitude is $U_{\mathrm{pcc}} = \sqrt{v_{c,d}^2 + v_{c,q}^2}$.&lt;/p>
&lt;p>The measurement filters are first-order lags,&lt;/p>
&lt;div class="math">$$\dot{P} = \omega_c(p - P),
\qquad
\dot{Q} = \omega_c(q - Q).$$&lt;/div>&lt;p>The VSG swing equation sets the angle from the active-power balance,&lt;/p>
&lt;div class="math">$$J\dot{\omega} = \frac{P_{\mathrm{ref}} - P}{\omega} - D(\omega - \omega_n),
\qquad
\dot{\theta} = \omega,$$&lt;/div>&lt;p>with virtual inertia $J$ and damping $D$.
The reactive-power/voltage excitation controller sets the internal magnitude,&lt;/p>
&lt;div class="math">$$\dot{E} = K_q(Q_{\mathrm{ref}} - Q) + K_u(U_n - U_{\mathrm{pcc}}),$$&lt;/div>&lt;p>an integral law on the reactive error with a voltage-droop term.
The excitation defines the dq voltage reference; in the islanded model it is aligned with the d-axis,&lt;/p>
&lt;div class="math">$$v_{d,\mathrm{ref}} = E, \qquad v_{q,\mathrm{ref}} = 0 .$$&lt;/div>&lt;p>The voltage controller integrates the voltage error and forms the current reference with the capacitor-current feed-forward and dq decoupling,&lt;/p>
&lt;div class="math">$$\dot{\xi}_{v,d} = v_{d,\mathrm{ref}} - v_{c,d},
\qquad
\dot{\xi}_{v,q} = v_{q,\mathrm{ref}} - v_{c,q},$$&lt;/div>
&lt;div class="math">$$i_{d,\mathrm{ref}} = i_{g,d} - \omega C_f v_{c,q}
+ K_{p,V}(v_{d,\mathrm{ref}} - v_{c,d}) + K_{i,V}\xi_{v,d},$$&lt;/div>
&lt;div class="math">$$i_{q,\mathrm{ref}} = i_{g,q} + \omega C_f v_{c,d}
+ K_{p,V}(v_{q,\mathrm{ref}} - v_{c,q}) + K_{i,V}\xi_{v,q}.$$&lt;/div>&lt;p>The current controller integrates the current error and forms the converter voltage reference, with inductor decoupling and optional active damping on the capacitor current,&lt;/p>
&lt;div class="math">$$\dot{\xi}_{i,d} = i_{d,\mathrm{ref}} - i_{f,d},
\qquad
\dot{\xi}_{i,q} = i_{q,\mathrm{ref}} - i_{f,q},$$&lt;/div>
&lt;div class="math">$$v_{d,\mathrm{conv}} = v_{c,d} - \omega L_f i_{f,q}
+ K_{p,I}(i_{d,\mathrm{ref}} - i_{f,d}) + K_{i,I}\xi_{i,d} - K_{ad} i_{\mathrm{cap},d},$$&lt;/div>
&lt;div class="math">$$v_{q,\mathrm{conv}} = v_{c,q} + \omega L_f i_{f,d}
+ K_{p,I}(i_{q,\mathrm{ref}} - i_{f,q}) + K_{i,I}\xi_{i,q} - K_{ad} i_{\mathrm{cap},q}.$$&lt;/div>&lt;p>A first-order lag approximates the converter/digital delay,&lt;/p>
&lt;div class="math">$$\dot{v}_{\mathrm{del},d} = \omega_d(v_{d,\mathrm{conv}} - v_{\mathrm{del},d}),
\qquad
\dot{v}_{\mathrm{del},q} = \omega_d(v_{q,\mathrm{conv}} - v_{\mathrm{del},q}),$$&lt;/div>&lt;p>and its output, transformed back to abc as $\mathbf{v}&lt;em>{\mathrm{inv}} = \mathbf{T}^{-1}(\theta),[v&lt;/em>{\mathrm{del},d}\ v_{\mathrm{del},q}]^\top$, drives the LC filter,&lt;/p>
&lt;div class="math">$$\dot{\mathbf{v}}_c = \frac{1}{C_f}\left(\mathbf{i}_f + \frac{\mathbf{u} - \mathbf{v}_c}{R_c}\right),
\qquad
\dot{\mathbf{i}}_f = \frac{1}{L_f}\left(\mathbf{v}_{\mathrm{inv}} - \mathbf{v}_c - R_f\mathbf{i}_f\right).$$&lt;/div>&lt;h2 id="grid-connected-control-extensions">Grid-connected control extensions&lt;/h2>
&lt;p>The equations above describe the islanded inverter. Three opt-in extensions adapt it to a stiff grid; each defaults to the value that recovers the islanded model exactly, so the eigenstructure is unchanged unless a setter is called.&lt;/p>
&lt;p>&lt;strong>Virtual output impedance.&lt;/strong> A virtual impedance $Z_v = R_v + jX_v$ is subtracted from the excitation to form the voltage reference, using the filter current $\mathbf{i}_{f,dq}$,&lt;/p>
&lt;div class="math">$$v_{d,\mathrm{ref}} + j v_{q,\mathrm{ref}}
= E - Z_v\,(i_{f,d} + j i_{f,q}),$$&lt;/div>&lt;p>i.e.&lt;/p>
&lt;div class="math">$$v_{d,\mathrm{ref}} = E - (R_v i_{f,d} - X_v i_{f,q}),
\qquad
v_{q,\mathrm{ref}} = -(R_v i_{f,q} + X_v i_{f,d}).$$&lt;/div>&lt;p>$Z_v = 0$ recovers $v_{d,\mathrm{ref}} = E,\ v_{q,\mathrm{ref}} = 0$.
A finite $R_v$ adds a current-proportional term opposing motion, damping the power-synchronization loop on a stiff grid at the electrical timescale, an alternative to raising the mechanical damping $D$.
The drop is taken off the filter-current &lt;em>state&lt;/em> $\mathbf{i}_f$ rather than the algebraically reconstructed grid current $\mathbf{i}_g = (\mathbf{v}_c-\mathbf{u})/R_c$; the latter would multiply the reference by a factor $\propto 1/R_c$, amplifying state and linearization error.&lt;/p>
&lt;p>&lt;strong>Grid-current feed-forward scale.&lt;/strong> A scalar $\kappa$ scales the grid-current feed-forward in the current reference,&lt;/p>
&lt;div class="math">$$i_{d,\mathrm{ref}} = \kappa\, i_{g,d} - \omega C_f v_{c,q} + \dots,
\qquad
i_{q,\mathrm{ref}} = \kappa\, i_{g,q} + \omega C_f v_{c,d} + \dots,$$&lt;/div>&lt;p>with $\kappa = 1$ the default full feed-forward.&lt;/p>
&lt;p>&lt;strong>Proportional reactive-power droop.&lt;/strong> When a cutoff $\omega_q &amp;gt; 0$ is set, the integral excitation is replaced by a proportional Q-V droop,&lt;/p>
&lt;div class="math">$$\dot{E} = \omega_q\big(E_{\mathrm{set}} + D_q(Q_{\mathrm{ref}} - Q) - E\big),$$&lt;/div>&lt;p>a first-order lag with a stable fixed point $E^* = E_{\mathrm{set}} + D_q(Q_{\mathrm{ref}} - Q)$ and pole at $-\omega_q$.
On a stiff grid the network fixes $U_{\mathrm{pcc}}$, so the reactive error $Q_{\mathrm{ref}} - Q$ cannot be driven to zero and the integral law $\dot E = K_q(Q_{\mathrm{ref}} - Q) + K_u(U_n - U_{\mathrm{pcc}})$ has no reachable equilibrium (reactive windup); the proportional droop always has one.
The setpoint $E_{\mathrm{set}}$ is captured at initialization as the operating magnitude, so $\dot{E} = 0$ when $Q = Q_{\mathrm{ref}}$ at $t = 0$.&lt;/p>
&lt;h2 id="linearization-and-stamping">Linearization and stamping&lt;/h2>
&lt;p>The model is nonlinear (Park transforms with the moving angle $\theta$, the $1/\omega$ swing term, the power products). It is not linearized by hand; at each simulation step the state and output Jacobians are computed by central finite differences of the nonlinear functions $\mathbf{f}(\mathbf{x},\mathbf{u}) = \dot{\mathbf{x}}$ and $\mathbf{g}(\mathbf{x},\mathbf{u}) = \mathbf{y}$,&lt;/p>
&lt;div class="math">$$\mathbf{A} = \frac{\partial \mathbf{f}}{\partial \mathbf{x}},\quad
\mathbf{B} = \frac{\partial \mathbf{f}}{\partial \mathbf{u}},\quad
\mathbf{C} = \frac{\partial \mathbf{g}}{\partial \mathbf{x}},\quad
\mathbf{D} = \frac{\partial \mathbf{g}}{\partial \mathbf{u}},$$&lt;/div>&lt;p>each column $j$ evaluated as $[\mathbf{f}(\mathbf{x}+\delta_j\mathbf{e}_j,\mathbf{u}) - \mathbf{f}(\mathbf{x}-\delta_j\mathbf{e}_j,\mathbf{u})]/(2\delta_j)$ with a mixed relative/absolute step $\delta_j$.
Because the grid-connected extensions above all enter through $\mathbf{f}$, they are captured in $\mathbf{A}$, $\mathbf{B}$, $\mathbf{C}$ and $\mathbf{D}$ automatically.
The affine offsets fix the model to the current operating point,&lt;/p>
&lt;div class="math">$$\mathbf{E} = \mathbf{f}(\mathbf{x}_0,\mathbf{u}_0) - \mathbf{A}\mathbf{x}_0 - \mathbf{B}\mathbf{u}_0,
\qquad
\mathbf{F} = \mathbf{g}(\mathbf{x}_0,\mathbf{u}_0) - \mathbf{C}\mathbf{x}_0 - \mathbf{D}\mathbf{u}_0,$$&lt;/div>&lt;p>giving the affine state-space form&lt;/p>
&lt;div class="math">$$\dot{\mathbf{x}} \approx \mathbf{A}\mathbf{x} + \mathbf{B}\mathbf{u} + \mathbf{E},
\qquad
\mathbf{y} \approx \mathbf{C}\mathbf{x} + \mathbf{D}\mathbf{u} + \mathbf{F},$$&lt;/div>&lt;p>The dq/abc transformations and the nonlinear controls make this local model time varying, so it holds only in a neighbourhood of the operating point it was formed at.&lt;/p>
&lt;p>How the linearization is carried out and stamped, together with the source and the runnable examples, is covered under
&lt;a href="https://sogno.energy/dpsim/docs/developer-guide/model-implementations/emt-ph3-grid-forming-vsi-implementation/">EMT Ph3 grid-forming VSI implementation&lt;/a>.&lt;/p>
&lt;h2 id="references">References&lt;/h2>
&lt;ul>
&lt;li>&lt;a name="Gao2022">&lt;/a>[Gao2022] X. Gao, D. Zhou, A. Anvari-Moghaddam, and F. Blaabjerg, &amp;ldquo;Stability Analysis of Grid-Following and Grid-Forming Converters Based on State-Space Model,&amp;rdquo; in &lt;em>2022 International Power Electronics Conference (IPEC-Himeji 2022 - ECCE Asia)&lt;/em>, 2022, pp. 422-428. Source of both the grid-following and grid-forming state-space control structures. Its eigenvalue analysis finds grid-following control better suited to a stiff grid and grid-forming control to a weak grid; the grid-connected extensions above (virtual impedance, Q-V droop) are what let the grid-forming model stay stable when connected to a stiff grid.&lt;/li>
&lt;li>&lt;a name="Yazdani2010">&lt;/a>[Yazdani2010] A. Yazdani and R. Iravani, &lt;em>Voltage-Sourced Converters in Power Systems: Modeling, Control, and Applications&lt;/em>. Hoboken, NJ: Wiley-IEEE Press, 2010. Basis for the inner voltage/current control and LC-filter modeling of both inverters.&lt;/li>
&lt;/ul></description></item><item><title>Docs: Signal Models</title><link>https://sogno.energy/dpsim/docs/reference/signal-models/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://sogno.energy/dpsim/docs/reference/signal-models/</guid><description>
&lt;p>Signal models live in &lt;code>CPS::Signal&lt;/code> and are domain independent: the same controller drives a
dynamic phasor or an electromagnetic transient machine model, because it operates on scalar
signals rather than on network quantities. The exception is the decoupling group, which exists
per domain since it inserts real components into the network.&lt;/p>
&lt;h2 id="excitation-systems">Excitation systems&lt;/h2>
&lt;p>Regulate generator terminal voltage by acting on field voltage. Equations and block diagrams are on the
&lt;a href="https://sogno.energy/dpsim/docs/concepts/models/synchronous-generator-regulators/">regulators&lt;/a> page.&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Model&lt;/th>
&lt;th>Description&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;code>ExciterDC1&lt;/code>&lt;/td>
&lt;td>Standard IEEE type DC1 exciter&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>ExciterDC1Simp&lt;/code>&lt;/td>
&lt;td>Simplified version of the IEEE type DC1 exciter&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>ExciterST1Simp&lt;/code>&lt;/td>
&lt;td>Simplified static exciter&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>ExciterStatic&lt;/code>&lt;/td>
&lt;td>Static exciter, with an anti-windup strategy for the integral component&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;h2 id="power-system-stabiliser">Power system stabiliser&lt;/h2>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Model&lt;/th>
&lt;th>Description&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;code>PSS1A&lt;/code>&lt;/td>
&lt;td>Simplified IEEE PSS1A. Enhances damping of electromechanical oscillations, accepting rotor speed, active power and terminal voltage magnitude as optional inputs. Its output feeds the exciter&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;h2 id="turbines-and-governors">Turbines and governors&lt;/h2>
&lt;p>Governors set mechanical power from speed deviation; turbine models convert that into the torque
applied to the machine.&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Model&lt;/th>
&lt;th>Description&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;code>SteamTurbine&lt;/code>&lt;/td>
&lt;td>Steam turbine, used in series with its governor&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>SteamTurbineGovernor&lt;/code>&lt;/td>
&lt;td>Governor for the steam turbine, instantiated separately from it&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>HydroTurbine&lt;/code>&lt;/td>
&lt;td>Hydro turbine, used in series with its governor&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>HydroTurbineGovernor&lt;/code>&lt;/td>
&lt;td>Governor for the hydro turbine, instantiated separately from it&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>TurbineGovernorType1&lt;/code>&lt;/td>
&lt;td>Turbine and governor combined in one component&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>TurbineGovernor&lt;/code>&lt;/td>
&lt;td>Turbine and governor combined in one component&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;h2 id="converter-control">Converter control&lt;/h2>
&lt;p>Control loops for the averaged inverter models. See
&lt;a href="https://sogno.energy/dpsim/docs/concepts/models/power-electronics/">power electronics&lt;/a> for how these
attach to the converter.&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Model&lt;/th>
&lt;th>Description&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;code>PowerControllerVSI&lt;/code>&lt;/td>
&lt;td>Power control loop used by the averaged grid-following inverter models&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>VoltageControllerVSI&lt;/code>&lt;/td>
&lt;td>Voltage control loop used by the grid-forming inverter models&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>PLL&lt;/code>&lt;/td>
&lt;td>Phase-locked loop&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>VCO&lt;/code>&lt;/td>
&lt;td>Voltage-controlled oscillator&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;h2 id="signal-generators">Signal generators&lt;/h2>
&lt;p>Drive sources and setpoints from a prescribed waveform rather than a constant.&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Model&lt;/th>
&lt;th>Description&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;code>SignalGenerator&lt;/code>&lt;/td>
&lt;td>Base class for the generators below&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>SineWaveGenerator&lt;/code>&lt;/td>
&lt;td>Sine wave&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>CosineFMGenerator&lt;/code>&lt;/td>
&lt;td>Frequency-modulated cosine&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>FrequencyRampGenerator&lt;/code>&lt;/td>
&lt;td>Frequency ramp&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>DCGenerator&lt;/code>&lt;/td>
&lt;td>Constant value&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;h2 id="filters-and-maths">Filters and maths&lt;/h2>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Model&lt;/th>
&lt;th>Description&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;code>FIRFilter&lt;/code>&lt;/td>
&lt;td>Finite impulse response filter&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>Integrator&lt;/code>&lt;/td>
&lt;td>Integrator block used inside the control models&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;h2 id="decoupling">Decoupling&lt;/h2>
&lt;p>Split a network into parts that can be solved separately, either across solvers or across
simulators in a co-simulation. See
&lt;a href="https://sogno.energy/dpsim/docs/user-guide/co-simulation/">co-simulation&lt;/a>.&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Model&lt;/th>
&lt;th>Domains&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;code>DecouplingLine&lt;/code>&lt;/td>
&lt;td>&lt;code>DP::Ph1&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>DecouplingLineEMT&lt;/code>&lt;/td>
&lt;td>&lt;code>EMT::Ph1&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>DecouplingLineEMT_Ph3&lt;/code>&lt;/td>
&lt;td>&lt;code>EMT::Ph3&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>DecouplingIdealTransformer&lt;/code>&lt;/td>
&lt;td>&lt;code>DP::Ph1&lt;/code>, &lt;code>EMT::Ph1&lt;/code>, &lt;code>EMT::Ph3&lt;/code>, &lt;code>SP::Ph1&lt;/code>&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;h2 id="references">References&lt;/h2>
&lt;ul>
&lt;li>IEEE Std 421.5-2016, &amp;ldquo;IEEE Recommended Practice for Excitation System Models for Power System
Stability Studies&amp;rdquo;. &lt;a href="https://doi.org/10.1109/IEEESTD.2016.7553421">https://doi.org/10.1109/IEEESTD.2016.7553421&lt;/a>&lt;/li>
&lt;li>F. Milano, &lt;em>Power System Modelling and Scripting&lt;/em>. London: Springer-Verlag, 2010.
&lt;a href="https://doi.org/10.1007/978-3-642-13669-6">https://doi.org/10.1007/978-3-642-13669-6&lt;/a>&lt;/li>
&lt;li>M. Eremia and M. Shahidehpour, &lt;em>Handbook of Electrical Power System Dynamics: Modeling,
Stability, and Control&lt;/em>. &lt;a href="https://ieeexplore.ieee.org/book/6480471">https://ieeexplore.ieee.org/book/6480471&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>Docs: The Same Circuit in Two Domains</title><link>https://sogno.energy/dpsim/docs/tutorials/python/comparing-domains/</link><pubDate>Fri, 31 Jul 2026 00:00:00 +0000</pubDate><guid>https://sogno.energy/dpsim/docs/tutorials/python/comparing-domains/</guid><description>
&lt;p>Every result so far has been an envelope, and the pages have said that an envelope is not a
waveform without showing what the difference costs. This tutorial runs the same circuit both ways
and puts the two on one axis.&lt;/p>
&lt;p>The circuit is the RL branch from &lt;a href="https://sogno.energy/dpsim/docs/tutorials/python/adding-dynamics/">adding dynamics&lt;/a>, unchanged.&lt;/p>
&lt;h2 id="building-the-same-circuit-twice">Building the same circuit twice&lt;/h2>
&lt;p>Only the namespaces differ. &lt;code>dpsimpy.emt.ph1&lt;/code> instead of &lt;code>dpsimpy.dp.ph1&lt;/code>, and &lt;code>Domain.EMT&lt;/code> instead
of &lt;code>Domain.DP&lt;/code>:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-python" data-lang="python">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#204a87;font-weight:bold">def&lt;/span> &lt;span style="color:#000">build&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#000">domain&lt;/span>&lt;span style="color:#000;font-weight:bold">,&lt;/span> &lt;span style="color:#000">ns&lt;/span>&lt;span style="color:#000;font-weight:bold">,&lt;/span> &lt;span style="color:#000">ph1&lt;/span>&lt;span style="color:#000;font-weight:bold">,&lt;/span> &lt;span style="color:#000">name&lt;/span>&lt;span style="color:#000;font-weight:bold">,&lt;/span> &lt;span style="color:#000">dt&lt;/span>&lt;span style="color:#000;font-weight:bold">):&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#000">gnd&lt;/span> &lt;span style="color:#ce5c00;font-weight:bold">=&lt;/span> &lt;span style="color:#000">ns&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">SimNode&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">gnd&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#000">n1&lt;/span> &lt;span style="color:#ce5c00;font-weight:bold">=&lt;/span> &lt;span style="color:#000">ns&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">SimNode&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#4e9a06">&amp;#34;n1&amp;#34;&lt;/span>&lt;span style="color:#000;font-weight:bold">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#000">n2&lt;/span> &lt;span style="color:#ce5c00;font-weight:bold">=&lt;/span> &lt;span style="color:#000">ns&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">SimNode&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#4e9a06">&amp;#34;n2&amp;#34;&lt;/span>&lt;span style="color:#000;font-weight:bold">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#000">src&lt;/span> &lt;span style="color:#ce5c00;font-weight:bold">=&lt;/span> &lt;span style="color:#000">ph1&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">VoltageSource&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#4e9a06">&amp;#34;src&amp;#34;&lt;/span>&lt;span style="color:#000;font-weight:bold">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#000">src&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">set_parameters&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#000">V_ref&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">=&lt;/span>&lt;span style="color:#204a87">complex&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#0000cf;font-weight:bold">100&lt;/span>&lt;span style="color:#000;font-weight:bold">,&lt;/span> &lt;span style="color:#0000cf;font-weight:bold">0&lt;/span>&lt;span style="color:#000;font-weight:bold">),&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#000">f_src&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">=&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#0000cf;font-weight:bold">50.0&lt;/span> &lt;span style="color:#204a87;font-weight:bold">if&lt;/span> &lt;span style="color:#000">domain&lt;/span> &lt;span style="color:#ce5c00;font-weight:bold">==&lt;/span> &lt;span style="color:#000">dpsimpy&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">Domain&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">EMT&lt;/span> &lt;span style="color:#204a87;font-weight:bold">else&lt;/span> &lt;span style="color:#0000cf;font-weight:bold">0.0&lt;/span>&lt;span style="color:#000;font-weight:bold">),&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#000;font-weight:bold">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#000">r&lt;/span> &lt;span style="color:#ce5c00;font-weight:bold">=&lt;/span> &lt;span style="color:#000">ph1&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">Resistor&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#4e9a06">&amp;#34;r&amp;#34;&lt;/span>&lt;span style="color:#000;font-weight:bold">);&lt;/span> &lt;span style="color:#000">r&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">set_parameters&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#000">R&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">=&lt;/span>&lt;span style="color:#0000cf;font-weight:bold">10.0&lt;/span>&lt;span style="color:#000;font-weight:bold">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#000">l&lt;/span> &lt;span style="color:#ce5c00;font-weight:bold">=&lt;/span> &lt;span style="color:#000">ph1&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">Inductor&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#4e9a06">&amp;#34;l&amp;#34;&lt;/span>&lt;span style="color:#000;font-weight:bold">);&lt;/span> &lt;span style="color:#000">l&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">set_parameters&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#000">L&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">=&lt;/span>&lt;span style="color:#0000cf;font-weight:bold">0.05&lt;/span>&lt;span style="color:#000;font-weight:bold">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#000">src&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">connect&lt;/span>&lt;span style="color:#000;font-weight:bold">([&lt;/span>&lt;span style="color:#000">gnd&lt;/span>&lt;span style="color:#000;font-weight:bold">,&lt;/span> &lt;span style="color:#000">n1&lt;/span>&lt;span style="color:#000;font-weight:bold">]);&lt;/span> &lt;span style="color:#000">r&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">connect&lt;/span>&lt;span style="color:#000;font-weight:bold">([&lt;/span>&lt;span style="color:#000">n1&lt;/span>&lt;span style="color:#000;font-weight:bold">,&lt;/span> &lt;span style="color:#000">n2&lt;/span>&lt;span style="color:#000;font-weight:bold">]);&lt;/span> &lt;span style="color:#000">l&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">connect&lt;/span>&lt;span style="color:#000;font-weight:bold">([&lt;/span>&lt;span style="color:#000">n2&lt;/span>&lt;span style="color:#000;font-weight:bold">,&lt;/span> &lt;span style="color:#000">gnd&lt;/span>&lt;span style="color:#000;font-weight:bold">])&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#000">system&lt;/span> &lt;span style="color:#ce5c00;font-weight:bold">=&lt;/span> &lt;span style="color:#000">dpsimpy&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">SystemTopology&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#0000cf;font-weight:bold">50&lt;/span>&lt;span style="color:#000;font-weight:bold">,&lt;/span> &lt;span style="color:#000;font-weight:bold">[&lt;/span>&lt;span style="color:#000">gnd&lt;/span>&lt;span style="color:#000;font-weight:bold">,&lt;/span> &lt;span style="color:#000">n1&lt;/span>&lt;span style="color:#000;font-weight:bold">,&lt;/span> &lt;span style="color:#000">n2&lt;/span>&lt;span style="color:#000;font-weight:bold">],&lt;/span> &lt;span style="color:#000;font-weight:bold">[&lt;/span>&lt;span style="color:#000">src&lt;/span>&lt;span style="color:#000;font-weight:bold">,&lt;/span> &lt;span style="color:#000">r&lt;/span>&lt;span style="color:#000;font-weight:bold">,&lt;/span> &lt;span style="color:#000">l&lt;/span>&lt;span style="color:#000;font-weight:bold">])&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#000">logger&lt;/span> &lt;span style="color:#ce5c00;font-weight:bold">=&lt;/span> &lt;span style="color:#000">dpsimpy&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">Logger&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#000">name&lt;/span>&lt;span style="color:#000;font-weight:bold">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#000">logger&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">log_attribute&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#4e9a06">&amp;#34;i_l&amp;#34;&lt;/span>&lt;span style="color:#000;font-weight:bold">,&lt;/span> &lt;span style="color:#4e9a06">&amp;#34;i_intf&amp;#34;&lt;/span>&lt;span style="color:#000;font-weight:bold">,&lt;/span> &lt;span style="color:#000">l&lt;/span>&lt;span style="color:#000;font-weight:bold">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#000">sim&lt;/span> &lt;span style="color:#ce5c00;font-weight:bold">=&lt;/span> &lt;span style="color:#000">dpsimpy&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">Simulation&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#000">name&lt;/span>&lt;span style="color:#000;font-weight:bold">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#000">sim&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">set_domain&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#000">domain&lt;/span>&lt;span style="color:#000;font-weight:bold">);&lt;/span> &lt;span style="color:#000">sim&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">set_system&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#000">system&lt;/span>&lt;span style="color:#000;font-weight:bold">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#000">sim&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">set_time_step&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#000">dt&lt;/span>&lt;span style="color:#000;font-weight:bold">);&lt;/span> &lt;span style="color:#000">sim&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">set_final_time&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#0000cf;font-weight:bold">0.06&lt;/span>&lt;span style="color:#000;font-weight:bold">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#000">sim&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">add_logger&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#000">logger&lt;/span>&lt;span style="color:#000;font-weight:bold">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#000">sim&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">run&lt;/span>&lt;span style="color:#000;font-weight:bold">()&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#204a87;font-weight:bold">return&lt;/span> &lt;span style="color:#000">rt&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">read_timeseries_dpsim&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#4e9a06">&amp;#34;logs/&lt;/span>&lt;span style="color:#4e9a06">%s&lt;/span>&lt;span style="color:#4e9a06">.csv&amp;#34;&lt;/span> &lt;span style="color:#ce5c00;font-weight:bold">%&lt;/span> &lt;span style="color:#000">name&lt;/span>&lt;span style="color:#000;font-weight:bold">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#000">emt&lt;/span> &lt;span style="color:#ce5c00;font-weight:bold">=&lt;/span> &lt;span style="color:#000">build&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#000">dpsimpy&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">Domain&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">EMT&lt;/span>&lt;span style="color:#000;font-weight:bold">,&lt;/span> &lt;span style="color:#000">dpsimpy&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">emt&lt;/span>&lt;span style="color:#000;font-weight:bold">,&lt;/span> &lt;span style="color:#000">dpsimpy&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">emt&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">ph1&lt;/span>&lt;span style="color:#000;font-weight:bold">,&lt;/span> &lt;span style="color:#4e9a06">&amp;#34;cmp_emt&amp;#34;&lt;/span>&lt;span style="color:#000;font-weight:bold">,&lt;/span> &lt;span style="color:#0000cf;font-weight:bold">5e-5&lt;/span>&lt;span style="color:#000;font-weight:bold">)[&lt;/span>&lt;span style="color:#4e9a06">&amp;#34;i_l&amp;#34;&lt;/span>&lt;span style="color:#000;font-weight:bold">]&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#000">dp&lt;/span> &lt;span style="color:#ce5c00;font-weight:bold">=&lt;/span> &lt;span style="color:#000">build&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#000">dpsimpy&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">Domain&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">DP&lt;/span>&lt;span style="color:#000;font-weight:bold">,&lt;/span> &lt;span style="color:#000">dpsimpy&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">dp&lt;/span>&lt;span style="color:#000;font-weight:bold">,&lt;/span> &lt;span style="color:#000">dpsimpy&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">dp&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">ph1&lt;/span>&lt;span style="color:#000;font-weight:bold">,&lt;/span> &lt;span style="color:#4e9a06">&amp;#34;cmp_dp&amp;#34;&lt;/span>&lt;span style="color:#000;font-weight:bold">,&lt;/span> &lt;span style="color:#0000cf;font-weight:bold">1e-3&lt;/span>&lt;span style="color:#000;font-weight:bold">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#000">dp_shift&lt;/span> &lt;span style="color:#ce5c00;font-weight:bold">=&lt;/span> &lt;span style="color:#000">ts&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">frequency_shift_list&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#000">dp&lt;/span>&lt;span style="color:#000;font-weight:bold">,&lt;/span> &lt;span style="color:#0000cf;font-weight:bold">50&lt;/span>&lt;span style="color:#000;font-weight:bold">)[&lt;/span>&lt;span style="color:#4e9a06">&amp;#34;i_l_shift&amp;#34;&lt;/span>&lt;span style="color:#000;font-weight:bold">]&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="the-one-parameter-that-means-different-things">The one parameter that means different things&lt;/h2>
&lt;p>&lt;code>f_src&lt;/code> is &lt;strong>not&lt;/strong> the same quantity in the two domains, and this is the single easiest way to get a
wrong answer here.&lt;/p>
&lt;div class="alert alert-warning" role="alert">
&lt;h4 class="alert-heading">Watch out: f_src means different things per domain&lt;/h4>
&lt;p>In EMT it is the absolute frequency of the source, so 50 Hz means 50 Hz. In DP and SP it is an
&lt;strong>offset from the carrier&lt;/strong>, so passing 50 there gives a source at 100 Hz. Leave it at zero, or
omit it, when you want a source at the system frequency in an envelope domain.&lt;/p>
&lt;p>Getting this wrong is not obvious from the output: the simulation runs, and the current is simply
smaller than it should be because the inductive reactance has doubled. In this circuit the wrong
setting gives 3.02 A instead of 5.37 A, which looks like a plausible number rather than an error.&lt;/p>
&lt;/div>
&lt;h2 id="the-comparison">The comparison&lt;/h2>
&lt;p>&lt;img src="domains.svg" alt="EMT waveform and DP envelope for the same RL circuit">&lt;/p>
&lt;p>The dashed line is the dynamic phasor result shifted back onto the 50 Hz carrier. It lies on the EMT
waveform. The third curve is the envelope magnitude itself, which is what the DP simulation actually
computed: the smooth rise to 5.37 A that the oscillation is riding on.&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Quantity&lt;/th>
&lt;th>EMT&lt;/th>
&lt;th>DP shifted back&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>Time step&lt;/td>
&lt;td>50 µs&lt;/td>
&lt;td>1 ms&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Samples over 60 ms&lt;/td>
&lt;td>1201&lt;/td>
&lt;td>61&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Peak current, steady state&lt;/td>
&lt;td>5.3703 A&lt;/td>
&lt;td>5.3603 A&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Current at t = 60 ms&lt;/td>
&lt;td>−2.8839 A&lt;/td>
&lt;td>−2.8840 A&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>Twenty times fewer steps, and the same answer to four significant figures.&lt;/p>
&lt;h2 id="why-this-works-and-when-it-does-not">Why this works, and when it does not&lt;/h2>
&lt;p>The saving is not because the envelope model is coarser. For a single carrier the transform is
exact. The 50 Hz oscillation has been moved out of the integrated quantity and into a coefficient
handled analytically, so the step size is set by how fast the envelope changes rather than by the
carrier. Here the envelope settles with a 5 ms time constant, and 1 ms resolves it comfortably.&lt;/p>
&lt;p>What an envelope domain cannot represent is content outside the band it retains around the carrier.
A harmonic, a fast switching transient, or a wideband disturbance is simply absent. That is the
trade, and it is the reason both domains exist rather than one being better.&lt;/p>
&lt;p>The transform itself is derived under
&lt;a href="https://sogno.energy/dpsim/docs/concepts/dyn-phasors/">dynamic phasors&lt;/a>.&lt;/p>
&lt;h2 id="reading-it-back">Reading it back&lt;/h2>
&lt;p>&lt;code>frequency_shift_list&lt;/code> appends &lt;code>_shift&lt;/code> to every key, so the shifted series is &lt;code>i_l_shift&lt;/code> and not
&lt;code>i_l&lt;/code>. Asking for the original name after shifting raises a &lt;code>KeyError&lt;/code> that reads like a missing
signal.&lt;/p>
&lt;h2 id="the-script">The script&lt;/h2>
&lt;p>The complete script for this page is &lt;a href="https://github.com/sogno-platform/dpsim/blob/master/examples/Python/Tutorials/05_comparing_domains.py">&lt;code>05_comparing_domains.py&lt;/code>&lt;/a> under &lt;code>examples/Python/Tutorials&lt;/code>. The numbers quoted above are the numbers it prints, so if the two ever disagree the page is the one that is wrong.&lt;/p>
&lt;h2 id="next">Next&lt;/h2>
&lt;p>The circuits so far have been passive. Next is a synchronous machine, where the model order becomes
a choice and initialization from a powerflow stops being optional.&lt;/p></description></item><item><title>Docs: Interfaces</title><link>https://sogno.energy/dpsim/docs/developer-guide/attributes-and-scheduling/interface-tasks/</link><pubDate>Fri, 31 Jul 2026 00:00:00 +0000</pubDate><guid>https://sogno.energy/dpsim/docs/developer-guide/attributes-and-scheduling/interface-tasks/</guid><description>
&lt;p>Why you would exchange data at all, and what it costs, is under
&lt;a href="https://sogno.energy/dpsim/docs/user-guide/co-simulation/">co-simulation&lt;/a>. This page is how it is configured
and what it does to the task graph.&lt;/p>
&lt;h2 id="choosing-and-configuring-an-interface">Choosing and configuring an interface&lt;/h2>
&lt;div class="alert alert-info" role="alert">
&lt;h4 class="alert-heading">Requires a build with VILLASnode&lt;/h4>
This feature requires DPsim compiled with the &lt;code>WITH_VILLAS&lt;/code> flag. Using the interface from Python
additionally needs the &lt;code>dpsimpyvillas&lt;/code> target built alongside the normal &lt;code>dpsimpy&lt;/code> package.
&lt;/div>
&lt;p>The VILLASnode interface is designed to make use of the various node types and protocols supported by the &lt;a href="https://github.com/VILLASframework/node">VILLASframework&lt;/a>.
By utilizing the nodes provided by VILLASnode, it can be configured to import and export attributes using a wide range of protocols.
There are two interface implementations for VILLASnode: &lt;code>InterfaceVillas&lt;/code>, which is queued, and
&lt;code>InterfaceVillasQueueless&lt;/code>.&lt;/p>
&lt;p>
&lt;div class="alert alert-warning" role="alert">
&lt;h4 class="alert-heading">Watch out: only the queued interface is available from Python&lt;/h4>
&lt;code>dpsimpyvillas&lt;/code> exposes &lt;code>InterfaceVillas&lt;/code> and nothing else, so &lt;code>InterfaceVillasQueueless&lt;/code> can only be
used from C++. A Python script needing the unbuffered path has no way to reach it today.
&lt;/div>
&lt;code>InterfaceVillas&lt;/code> uses a ring buffer to store signal data between DPsim and VILLASnode to allow the protocol used in VILLASnode to operate at a different rate and non-synchronized to the DPsim time step.
&lt;code>InterfaceVillasQueueless&lt;/code> uses direct communication with a VILLASnode node type implementing a specific protocol without using a buffer, thus enabling significantly lower latency communication.
With &lt;code>InterfaceVillasQueueless&lt;/code>, the protocol operates at the time step of DPsim, i.e., an attribute update directly triggers a &lt;code>write()&lt;/code> call to the connected VILLASnode node type.
&lt;code>InterfaceVillas&lt;/code> should be used when using non- or soft real-time protocols or communication mediums, such as MQTT or connections via the internet.
&lt;code>InterfaceVillasQueueless&lt;/code> should be used when communicating using reliable, low latency, real-time protocols, e.g., with FPGAs, via dedicated fibre networks, or with local real-time applications.&lt;/p>
&lt;p>To create and configure one of the VILLASnode interface instance, create a new shared pointer of type &lt;code>InterfaceVillas&lt;/code> or &lt;code>InterfaceVillasQueueless&lt;/code> and supply it with a configuration string in the first constructor argument.
This configuration must be a valid JSON object containing the settings for the VILLASnode node type that should be used for data import and export.
This means that the JSON contains a &lt;code>type&lt;/code> key describing what node type to use, as well as any additional configuration options required for this node type.
The valid configuration keys can be found in the &lt;a href="https://villas.fein-aachen.org/doc/node-node-types.html">VILLASnode documentation&lt;/a>.&lt;/p>
&lt;div class="alert alert-warning" role="alert">
&lt;h4 class="alert-heading">Watch out: the queueless interface reserves the first signal&lt;/h4>
The queueless interface expects the first input signal in the VILLASnode configuration to be a
sequence number incremented every time step. If it does not increase by one between consecutive
steps, an overrun is detected. Because logging can cause large delays and overruns should not be
reported spuriously, the interface only warns once a large number of them occur.
&lt;/div>
&lt;p>After the object is created, the &lt;code>exportAttribute&lt;/code> and &lt;code>importAttribute&lt;/code> methods can be used to set up the data exchange between the DPsim simulation and the configured node.
The attributes given as the first parameter to these methods are attributes belonging to components in the simulation which should be read or updated by the interface.
As an example, for exporting and importing attributes via the MQTT protocol, the VILLASnode interfaces can be configured as follows:&lt;/p>
&lt;p>Using C++:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-cpp" data-lang="cpp">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#8f5902;font-style:italic">// JSON configuration adhering to the VILLASnode documentation
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#8f5902;font-style:italic">&lt;/span>&lt;span style="color:#000">std&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">::&lt;/span>&lt;span style="color:#000">string&lt;/span> &lt;span style="color:#000">mqttConfig&lt;/span> &lt;span style="color:#ce5c00;font-weight:bold">=&lt;/span> &lt;span style="color:#000">R&lt;/span>&lt;span style="color:#4e9a06">&amp;#34;STRING({&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#4e9a06">&amp;#34;type&amp;#34;&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">:&lt;/span> &lt;span style="color:#4e9a06">&amp;#34;mqtt&amp;#34;&lt;/span>&lt;span style="color:#000;font-weight:bold">,&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#4e9a06">&amp;#34;format&amp;#34;&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">:&lt;/span> &lt;span style="color:#4e9a06">&amp;#34;json&amp;#34;&lt;/span>&lt;span style="color:#000;font-weight:bold">,&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#4e9a06">&amp;#34;host&amp;#34;&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">:&lt;/span> &lt;span style="color:#4e9a06">&amp;#34;mqtt&amp;#34;&lt;/span>&lt;span style="color:#000;font-weight:bold">,&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#4e9a06">&amp;#34;in&amp;#34;&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">:&lt;/span> &lt;span style="color:#000;font-weight:bold">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#4e9a06">&amp;#34;subscribe&amp;#34;&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">:&lt;/span> &lt;span style="color:#4e9a06">&amp;#34;/mqtt-dpsim&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#000;font-weight:bold">},&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#4e9a06">&amp;#34;out&amp;#34;&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">:&lt;/span> &lt;span style="color:#000;font-weight:bold">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#4e9a06">&amp;#34;publish&amp;#34;&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">:&lt;/span> &lt;span style="color:#4e9a06">&amp;#34;/dpsim-mqtt&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#000;font-weight:bold">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#000;font-weight:bold">})&lt;/span>&lt;span style="color:#000">STRING&lt;/span>&lt;span style="color:#4e9a06">&amp;#34;;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#8f5902;font-style:italic">// Creating a new InterfaceVillas object
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#8f5902;font-style:italic">&lt;/span>&lt;span style="color:#000">std&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">::&lt;/span>&lt;span style="color:#000">shared_ptr&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">&amp;lt;&lt;/span>&lt;span style="color:#000">InterfaceVillas&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">&amp;gt;&lt;/span> &lt;span style="color:#000">intf&lt;/span> &lt;span style="color:#ce5c00;font-weight:bold">=&lt;/span> &lt;span style="color:#000">std&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">::&lt;/span>&lt;span style="color:#000">make_shared&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">&amp;lt;&lt;/span>&lt;span style="color:#000">InterfaceVillas&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">&amp;gt;&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#000">mqttConfig&lt;/span>&lt;span style="color:#000;font-weight:bold">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#8f5902;font-style:italic">// Configuring the InterfaceVillas to import and export attributes
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#8f5902;font-style:italic">&lt;/span>&lt;span style="color:#000">intf&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">-&amp;gt;&lt;/span>&lt;span style="color:#000">importAttribute&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#000">evs&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">-&amp;gt;&lt;/span>&lt;span style="color:#000">mVoltageRef&lt;/span>&lt;span style="color:#000;font-weight:bold">,&lt;/span> &lt;span style="color:#0000cf;font-weight:bold">0&lt;/span>&lt;span style="color:#000;font-weight:bold">,&lt;/span> &lt;span style="color:#204a87">true&lt;/span>&lt;span style="color:#000;font-weight:bold">,&lt;/span> &lt;span style="color:#204a87">true&lt;/span>&lt;span style="color:#000;font-weight:bold">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#000">intf&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">-&amp;gt;&lt;/span>&lt;span style="color:#000">exportAttribute&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#000">r12&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">-&amp;gt;&lt;/span>&lt;span style="color:#000">mIntfCurrent&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">-&amp;gt;&lt;/span>&lt;span style="color:#000">deriveCoeff&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">&amp;lt;&lt;/span>&lt;span style="color:#000">Complex&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">&amp;gt;&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#0000cf;font-weight:bold">0&lt;/span>&lt;span style="color:#000;font-weight:bold">,&lt;/span> &lt;span style="color:#0000cf;font-weight:bold">0&lt;/span>&lt;span style="color:#000;font-weight:bold">),&lt;/span> &lt;span style="color:#0000cf;font-weight:bold">1&lt;/span>&lt;span style="color:#000;font-weight:bold">,&lt;/span> &lt;span style="color:#204a87">true&lt;/span>&lt;span style="color:#000;font-weight:bold">,&lt;/span> &lt;span style="color:#4e9a06">&amp;#34;v_load&amp;#34;&lt;/span>&lt;span style="color:#000;font-weight:bold">);&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Using Python:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-python" data-lang="python">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#8f5902;font-style:italic"># JSON configuration adhering to the VILLASnode documentation&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#000">mqtt_config&lt;/span> &lt;span style="color:#ce5c00;font-weight:bold">=&lt;/span> &lt;span style="color:#4e9a06">&amp;#39;&amp;#39;&amp;#39;{
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4e9a06"> &amp;#34;type&amp;#34;: &amp;#34;mqtt&amp;#34;,
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4e9a06"> &amp;#34;format&amp;#34;: &amp;#34;json&amp;#34;,
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4e9a06"> &amp;#34;host&amp;#34;: &amp;#34;mqtt&amp;#34;,
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4e9a06"> &amp;#34;in&amp;#34;: {
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4e9a06"> &amp;#34;subscribe&amp;#34;: &amp;#34;/mqtt-dpsim&amp;#34;
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4e9a06"> },
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4e9a06"> &amp;#34;out&amp;#34;: {
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4e9a06"> &amp;#34;publish&amp;#34;: &amp;#34;/dpsim-mqtt&amp;#34;
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4e9a06"> }
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#4e9a06">}&amp;#39;&amp;#39;&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#8f5902;font-style:italic"># Creating a new InterfaceVillas object&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#000">intf&lt;/span> &lt;span style="color:#ce5c00;font-weight:bold">=&lt;/span> &lt;span style="color:#000">dpsimpyvillas&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">InterfaceVillas&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#000">name&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">=&lt;/span>&lt;span style="color:#4e9a06">&amp;#39;dpsim-mqtt&amp;#39;&lt;/span>&lt;span style="color:#000;font-weight:bold">,&lt;/span> &lt;span style="color:#000">config&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">=&lt;/span>&lt;span style="color:#000">mqtt_config&lt;/span>&lt;span style="color:#000;font-weight:bold">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#8f5902;font-style:italic"># Configuring the InterfaceVillas to import and export attributes&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#000">intf&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">import_attribute&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#000">evs&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">attr&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#4e9a06">&amp;#39;V_ref&amp;#39;&lt;/span>&lt;span style="color:#000;font-weight:bold">),&lt;/span> &lt;span style="color:#0000cf;font-weight:bold">0&lt;/span>&lt;span style="color:#000;font-weight:bold">,&lt;/span> &lt;span style="color:#204a87;font-weight:bold">True&lt;/span>&lt;span style="color:#000;font-weight:bold">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#000">intf&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">export_attribute&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#000">r12&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">attr&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#4e9a06">&amp;#39;i_intf&amp;#39;&lt;/span>&lt;span style="color:#000;font-weight:bold">)&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">derive_coeff&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#0000cf;font-weight:bold">0&lt;/span>&lt;span style="color:#000;font-weight:bold">,&lt;/span> &lt;span style="color:#0000cf;font-weight:bold">0&lt;/span>&lt;span style="color:#000;font-weight:bold">),&lt;/span> &lt;span style="color:#0000cf;font-weight:bold">0&lt;/span>&lt;span style="color:#000;font-weight:bold">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="adding-an-interface-to-the-simulation">Adding an Interface to the Simulation&lt;/h2>
&lt;p>After a new interface has been created and configured, it can be added to a simulation using the &lt;code>Simulation::addInterface&lt;/code> method:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-cpp" data-lang="cpp">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#8f5902;font-style:italic">// Create and configure simulation
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#8f5902;font-style:italic">&lt;/span>&lt;span style="color:#000">RealTimeSimulation&lt;/span> &lt;span style="color:#000">sim&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#000">simName&lt;/span>&lt;span style="color:#000;font-weight:bold">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#000">sim&lt;/span>&lt;span style="color:#000;font-weight:bold">.&lt;/span>&lt;span style="color:#000">setSystem&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#000">sys&lt;/span>&lt;span style="color:#000;font-weight:bold">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#000">sim&lt;/span>&lt;span style="color:#000;font-weight:bold">.&lt;/span>&lt;span style="color:#000">setTimeStep&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#000">timeStep&lt;/span>&lt;span style="color:#000;font-weight:bold">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#000">sim&lt;/span>&lt;span style="color:#000;font-weight:bold">.&lt;/span>&lt;span style="color:#000">setFinalTime&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#0000cf;font-weight:bold">10.0&lt;/span>&lt;span style="color:#000;font-weight:bold">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#8f5902;font-style:italic">// Create and configure interface
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#8f5902;font-style:italic">&lt;/span>&lt;span style="color:#204a87;font-weight:bold">auto&lt;/span> &lt;span style="color:#000">intf&lt;/span> &lt;span style="color:#ce5c00;font-weight:bold">=&lt;/span> &lt;span style="color:#8f5902;font-style:italic">//...
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#8f5902;font-style:italic">&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#8f5902;font-style:italic">// Add interface to simulation
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#8f5902;font-style:italic">&lt;/span>&lt;span style="color:#000">sim&lt;/span>&lt;span style="color:#000;font-weight:bold">.&lt;/span>&lt;span style="color:#000">addInterface&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#000">intf&lt;/span>&lt;span style="color:#000;font-weight:bold">);&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Adding an interface also adds two tasks to the simulation, one before the step and one after, so an
imported value is in place before anything reads it and an exported one is sent after everything
that could change it. The transfer itself happens on separate threads, so a slow far side does not
hold up the solver. How that is arranged, and why it matters, is under
&lt;a href="https://sogno.energy/dpsim/docs/developer-guide/attributes-and-scheduling/interface-tasks/">how an interface is scheduled&lt;/a>.&lt;/p>
&lt;h2 id="synchronizing-the-simulation-with-the-environment">Synchronizing the Simulation with the Environment&lt;/h2>
&lt;p>To allow for synchronizing the DPsim simulation with external services, the &lt;code>Interface&lt;/code> class provides some additional configuration options in the &lt;code>importAttribute&lt;/code> and &lt;code>exportAttribute&lt;/code> methods. For imports, setting the &lt;code>blockOnRead&lt;/code> parameter will completely halt the simulation at the start of
every time step until a new value for this attribute was read from the environment. Additionally, the &lt;code>syncOnSimulationStart&lt;/code> parameter can be set for every
import to indicate that this attribute is used to synchronize the start of the simulation. When a simulation contains any interfaces importing attributes
which have &lt;code>syncOnSimulationStart&lt;/code> set, the &lt;code>Simulation::sync&lt;/code> will be called before the first time step. This method will:&lt;/p>
&lt;ul>
&lt;li>write out all attributes configured for export to the environment&lt;/li>
&lt;li>block until all attributes with &lt;code>syncOnSimulationStart&lt;/code> set have been read from the environment at least once&lt;/li>
&lt;li>write out all exported attributes again&lt;/li>
&lt;/ul>
&lt;p>Note that this setting operates independently of the &lt;code>blockOnRead&lt;/code> flag. This means that with both flags set, the simulation will block again after the synchronization at the start of the first time step until another value is received for the attribute in question.&lt;/p>
&lt;h2 id="the-two-tasks">The two tasks&lt;/h2>
&lt;p>Adding an interface adds a &lt;code>PreStep&lt;/code> and a &lt;code>PostStep&lt;/code> task.&lt;/p>
&lt;p>&lt;code>PreStep&lt;/code> is declared to modify every attribute imported from the environment, so the scheduler
places it before any task that depends on those attributes. An imported value is therefore in place
before anything reads it.&lt;/p>
&lt;p>&lt;code>PostStep&lt;/code> is declared to depend on every attribute exported to the environment, so it runs after
anything that might modify them.&lt;/p>
&lt;h2 id="why-poststep-declares-a-modified-attribute">Why PostStep declares a modified attribute&lt;/h2>
&lt;p>&lt;code>PostStep&lt;/code> modifies nothing in the simulation: it only sends values outward. The scheduler prunes
tasks whose outputs nothing needs, so a task that modifies nothing is dropped, and the export would
silently never happen.&lt;/p>
&lt;p>To prevent that, &lt;code>PostStep&lt;/code> is declared to modify &lt;code>Scheduler::external&lt;/code>. That attribute exists to
make a task reachable when its real effect is outside the simulation.&lt;/p>
&lt;div class="alert alert-warning" role="alert">
&lt;h4 class="alert-heading">Watch out: a task that modifies nothing is pruned&lt;/h4>
This is the general rule, not a quirk of interfaces. The scheduler keeps a task only if something
needs what it produces, so any task whose effect leaves the simulation must declare a modified
attribute or it will be dropped without warning. The same mechanism explains why logging an
attribute can change which tasks run; see
&lt;a href="https://sogno.energy/dpsim/docs/developer-guide/attributes-and-scheduling/adding-tasks/">adding tasks to a component&lt;/a>.
&lt;/div>
&lt;h2 id="task-execution-is-not-the-moment-of-transfer">Task execution is not the moment of transfer&lt;/h2>
&lt;p>When these tasks execute is not when the data actually crosses the boundary. The interface spawns a
reader thread and a writer thread and communicates with them over a lock-free queue.&lt;/p>
&lt;p>The consequence is the useful part: a slow import or export does not block the solver. The simulation
hands a value to the queue and continues. That is what makes an interface to a slow or unreliable
far side usable at all, and it is also why a value read this step may have been produced some time
ago.&lt;/p>
&lt;p>Blocking is opt-in through &lt;code>blockOnRead&lt;/code> and &lt;code>syncOnSimulationStart&lt;/code> on the import, described on the
co-simulation page. Those are the only ways the exchange paces the simulation.&lt;/p></description></item><item><title>Docs: State-Space Nodal Across Domains</title><link>https://sogno.energy/dpsim/docs/concepts/ssn-domain-formulation/</link><pubDate>Fri, 31 Jul 2026 00:00:00 +0000</pubDate><guid>https://sogno.energy/dpsim/docs/concepts/ssn-domain-formulation/</guid><description>
&lt;p>A component&amp;rsquo;s state-space model does not change between simulation domains. The same
$\boldsymbol{A}$, $\boldsymbol{B}$, $\boldsymbol{C}$ and $\boldsymbol{D}$ describe the same physics
whichever domain solves them. What changes is the operator that is discretised, and that difference
propagates all the way to whether the resulting nodal stamp is real or complex.&lt;/p>
&lt;p>The construction of the stamp itself is unchanged and is derived under
&lt;a href="https://sogno.energy/dpsim/docs/concepts/models/ssn-components/">state-space nodal components&lt;/a>.&lt;/p>
&lt;h2 id="the-instantaneous-case">The instantaneous case&lt;/h2>
&lt;p>Here the state equation is integrated as written,&lt;/p>
&lt;div class="math">$$\dot{\boldsymbol{x}} = \boldsymbol{A}\boldsymbol{x} + \boldsymbol{B}\boldsymbol{u},$$&lt;/div>&lt;p>the trapezoidal rule gives a real discrete pair, and the equivalent admittance
$\boldsymbol{W} = \boldsymbol{C}\boldsymbol{B}_d + \boldsymbol{D}$ is real.&lt;/p>
&lt;h2 id="the-envelope-case">The envelope case&lt;/h2>
&lt;p>In an envelope domain the state is a complex envelope $\tilde{\boldsymbol{x}}$ carrying an implicit
$e^{j\omega_s t}$. Differentiating that product contributes the carrier term derived under
&lt;a href="https://sogno.energy/dpsim/docs/concepts/dyn-phasors/">dynamic phasors&lt;/a>, so the operator seen by the envelope is&lt;/p>
&lt;div class="math">$$\frac{d}{dt} \; \longrightarrow \; \frac{d}{dt} + j\omega_s ,$$&lt;/div>&lt;p>and the system that must be discretised is not $\boldsymbol{A}$ but
$\boldsymbol{A} - j\omega_s \boldsymbol{I}$.&lt;/p>
&lt;h2 id="the-real-augmented-form">The real-augmented form&lt;/h2>
&lt;p>Rather than integrate a complex system, split the envelope into real and imaginary parts. The
shifted operator becomes a real system of twice the size,&lt;/p>
&lt;div class="math">$$\begin{bmatrix} \dot{\boldsymbol{x}}_{re} \\ \dot{\boldsymbol{x}}_{im} \end{bmatrix}
=
\begin{bmatrix} \boldsymbol{A} &amp; \omega_s \boldsymbol{I} \\
-\omega_s \boldsymbol{I} &amp; \boldsymbol{A} \end{bmatrix}
\begin{bmatrix} \boldsymbol{x}_{re} \\ \boldsymbol{x}_{im} \end{bmatrix}
+
\begin{bmatrix} \boldsymbol{B} &amp; \boldsymbol{0} \\ \boldsymbol{0} &amp; \boldsymbol{B} \end{bmatrix}
\begin{bmatrix} \boldsymbol{u}_{re} \\ \boldsymbol{u}_{im} \end{bmatrix}.$$&lt;/div>&lt;p>The off-diagonal $\pm\omega_s \boldsymbol{I}$ blocks are the carrier rotation, and the block
structure $\begin{bmatrix} P &amp;amp; -Q \ Q &amp;amp; P \end{bmatrix}$ is the real representation of the complex
number $P + jQ$. Discretising this real system with the same trapezoidal rule and recombining the
blocks recovers the complex discrete pair, from which the equivalent admittance and the history term
follow exactly as in the instantaneous case.&lt;/p>
&lt;p>Two things follow. The equivalent admittance is complex in an envelope domain and real in an
instantaneous one, so the same component stamps differently. And setting $\omega_s = 0$ collapses
the augmented system back to the instantaneous one, which is the general statement about the
envelope transform applied here: the instantaneous formulation is the zero-carrier special case, not
a separate method.&lt;/p>
&lt;h2 id="why-this-matters-for-accuracy">Why this matters for accuracy&lt;/h2>
&lt;p>The carrier rotation is handled analytically, inside $\boldsymbol{A}$, rather than numerically by
resolving the oscillation with small steps. For a component whose envelope varies slowly against a
fast carrier, the step size is then set by the envelope&amp;rsquo;s own bandwidth rather than by the carrier
frequency. That is the entire accuracy argument for using an envelope domain here, and it fails for
exactly the reason it succeeds: content outside the retained band around $\omega_s$ has no
representation at all.&lt;/p></description></item><item><title>Docs: Switches</title><link>https://sogno.energy/dpsim/docs/concepts/models/switches/</link><pubDate>Fri, 31 Jul 2026 00:00:00 +0000</pubDate><guid>https://sogno.energy/dpsim/docs/concepts/models/switches/</guid><description>
&lt;p>A switch in a nodal formulation is not an ideal open or short. Both would be singular: an ideal
short shorts two node equations together, and an ideal open leaves a node with no path to ground.
Switches are therefore represented by a finite resistance that takes one of two values.&lt;/p>
&lt;h2 id="the-two-resistance-model">The two-resistance model&lt;/h2>
&lt;p>The switch contributes a single admittance between its two terminals,&lt;/p>
&lt;div class="math">$$G = \begin{cases}
1 / R_{closed} &amp; \text{closed} \\
1 / R_{open} &amp; \text{open}
\end{cases}$$&lt;/div>&lt;p>stamped as a conductance between the two terminal nodes, with the usual reduction when one terminal
is grounded. Typical values are far apart, of the order of milliohms closed and megohms open, so the
switch is a near short or a near open without ever being singular.&lt;/p>
&lt;p>The consequence of this choice is that the ratio $R_{open} / R_{closed}$ lands directly in the
condition number of the system matrix. Making the contrast arbitrarily large to approach an ideal
switch degrades the accuracy of every node voltage in the network, not only those near the switch.
The values are a numerical compromise, not a physical measurement.&lt;/p>
&lt;p>Because the admittance appears in the system matrix rather than in the right hand side, changing
state requires the matrix to be refactorised. This is why a network that switches often costs more
than one that does not, even though the model itself is trivial.&lt;/p>
&lt;h2 id="why-a-step-change-in-resistance-is-a-problem">Why a step change in resistance is a problem&lt;/h2>
&lt;p>Opening a switch that carries inductive current asks the network to interrupt that current within
one time step. The inductor opposes it, and with the trapezoidal companion model the result is a
numerical oscillation across the switch: the current alternates sign at the step frequency and
decays slowly, contaminating the solution for many steps after the event.&lt;/p>
&lt;p>This is a property of the discretisation, not of the physical circuit. The physical arc that would
form across real contacts dissipates that energy; a two-valued resistance has no equivalent
mechanism.&lt;/p>
&lt;h2 id="the-variable-resistance-switch">The variable-resistance switch&lt;/h2>
&lt;p>The variable-resistance switch removes the oscillation by refusing to make the change in a single
step. On opening, the resistance is multiplied by a fixed factor each step,&lt;/p>
&lt;div class="math">$$R[k+1] = \alpha \, R[k], \qquad \alpha > 1,$$&lt;/div>&lt;p>until it reaches the target open value, after which it is held there. The current therefore decays
geometrically over several steps rather than being interrupted at once, which is close to what an
arc does and which the trapezoidal companion model can follow without ringing.&lt;/p>
&lt;p>The growth factor is tied to the step size so that the transition covers a comparable interval of
time rather than a comparable number of steps. Closing is not ramped: the resistance is taken
straight to its closed value, because energising a path through a small resistance does not produce
the same interruption problem.&lt;/p>
&lt;p>The cost is that the system matrix changes on every step of the transition rather than once, so each
of those steps requires a refactorisation. The switch is worth its cost where the interruption is
severe, typically a fault applied at a machine terminal or a transformer winding, and unnecessary
for ordinary load switching.&lt;/p>
&lt;h2 id="series-switching">Series switching&lt;/h2>
&lt;p>Where a switch is combined with the series resistance it energises, the two are represented as one
element rather than as a switch plus a resistor. This keeps the branch to a single admittance and
avoids introducing an internal node that carries no physical meaning and adds an equation to the
system.&lt;/p></description></item><item><title>Docs: Real-Time</title><link>https://sogno.energy/dpsim/docs/user-guide/real-time/</link><pubDate>Thu, 13 Feb 2025 00:00:00 +0000</pubDate><guid>https://sogno.energy/dpsim/docs/user-guide/real-time/</guid><description>
&lt;p>Normally a simulation runs as fast as it can: a one-second study finishes in whatever time the
solver needs. In a real-time simulation, one second of simulated time takes one second of wall clock
time, no faster and no slower.&lt;/p>
&lt;h2 id="when-you-need-it">When you need it&lt;/h2>
&lt;p>Only when something outside the simulation has its own clock. A controller running on real hardware,
another simulator you are coupled to, or a person turning a dial all move at their own pace, and the
simulation has to move with them. If nothing outside is waiting, running in real time only makes the
study slower.&lt;/p>
&lt;p>This is why &lt;a href="https://sogno.energy/dpsim/docs/user-guide/co-simulation/">co-simulation&lt;/a> and real-time usually appear together.&lt;/p>
&lt;h2 id="what-it-changes">What it changes&lt;/h2>
&lt;p>Two things. The simulation waits at the end of each step until the wall clock catches up, so a run
takes as long as the time it simulates. And a step that takes longer than its own duration is an
&lt;strong>overrun&lt;/strong>: the simulation has missed its deadline and can no longer claim to be in step with the
outside world.&lt;/p>
&lt;p>Overruns are the whole difficulty. Everything else about real-time execution is arranging for them
not to happen: keeping the step&amp;rsquo;s work bounded, and keeping the operating system from interrupting
it.&lt;/p>
&lt;div class="alert alert-warning" role="alert">
&lt;h4 class="alert-heading">Watch out: an overrun does not stop the simulation&lt;/h4>
A missed deadline is reported, not fatal. The run continues and its results remain numerically
correct; what is no longer true is that it kept pace with anything external. A run that overran
repeatedly is not a real-time run, however normal its output looks.
&lt;/div>
&lt;h2 id="what-it-takes">What it takes&lt;/h2>
&lt;p>A time step that comfortably exceeds the work done in it, models that do nothing slow inside the
step, and a host that will not interrupt at the wrong moment. Millisecond steps are undemanding;
microsecond steps need a tuned kernel and careful models, and are where most of the effort goes.&lt;/p>
&lt;p>The requirements on the host and on the models are in
&lt;a href="https://sogno.energy/dpsim/docs/developer-guide/writing-a-model/real-time/">real-time execution&lt;/a>. With that
tuning, steps as low as 5 us synchronised to an FPGA through VILLASnode have been achieved.&lt;/p>
&lt;h2 id="running-a-real-time-simulation">Running a Real-Time Simulation&lt;/h2>
&lt;p>Before running a simulation, you can run the following commands as root:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-bash" data-lang="bash">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#204a87">echo&lt;/span> &lt;span style="color:#4e9a06">&amp;#34;evacuating cores&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>tuna isolate -c 9,11,13,15
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#204a87">echo&lt;/span> &lt;span style="color:#4e9a06">&amp;#34;disabling RT throttling&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#204a87">echo&lt;/span> -1 &amp;gt; /proc/sys/kernel/sched_rt_runtime_us
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#204a87">echo&lt;/span> &lt;span style="color:#4e9a06">&amp;#34;stopping systemd services&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>systemctl stop polkit
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>systemctl stop containerd
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>systemctl stop crond
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>systemctl stop chronyd
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>As a reference, real-time simulation examples are provided in the &lt;code>dpsim/examples/cxx&lt;/code> and &lt;code>dpsim-villas/examples/cxx&lt;/code> folder of the DPsim repository.&lt;/p>
&lt;p>To benefit from the &lt;code>PREEMPT_RT&lt;/code> feature and the isolated cores, the simulation has to be started using the &lt;code>chrt&lt;/code> command to set the scheduling policy and priority, and the &lt;code>taskset&lt;/code> command to pin the process to the isolated cores.&lt;/p>
&lt;ul>
&lt;li>&lt;a href="http://man7.org/linux/man-pages/man1/chrt.1.html">chrt man-page&lt;/a>&lt;/li>
&lt;li>&lt;a href="http://man7.org/linux/man-pages/man1/taskset.1.html">taskset man-page&lt;/a>&lt;/li>
&lt;/ul>
&lt;p>In the following example, we set the FIFO scheduling policy with the highest priority (99) and pin the execution of the simulation to CPU cores 9,11,13,15 which have been reserved previously (see above).&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-bash" data-lang="bash">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#8f5902;font-style:italic"># the simple RT_DP_CS_R_1 simulation&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>taskset -c 9,11,13,15 chrt -f &lt;span style="color:#0000cf;font-weight:bold">99&lt;/span> build/dpsim/examples/cxx/RT_DP_CS_R_1
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#8f5902;font-style:italic"># Cosimulation using VILLASnode, FPGA synchronized time step, and exchanging data via Aurora interface.&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#8f5902;font-style:italic"># Here we need sudo, to interact with the FPGA. We disable logging (log=false) and set the time step to 50 us (-t 0.00005).&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>sudo taskset -c 9,11,13,15 chrt -f &lt;span style="color:#0000cf;font-weight:bold">99&lt;/span> build/dpsim-villas/examples/cxx/FpgaCosim3PhInfiniteBus -o &lt;span style="color:#000">log&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">=&lt;/span>&lt;span style="color:#204a87">false&lt;/span> -t 0.00005 -d &lt;span style="color:#0000cf;font-weight:bold">10&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div></description></item><item><title>Docs: Task Scheduling</title><link>https://sogno.energy/dpsim/docs/developer-guide/attributes-and-scheduling/scheduling/</link><pubDate>Fri, 01 May 2020 00:00:00 +0000</pubDate><guid>https://sogno.energy/dpsim/docs/developer-guide/attributes-and-scheduling/scheduling/</guid><description>
&lt;p>Within each simulation timestep, DPsim executes a set of &lt;strong>tasks&lt;/strong>: discrete units of computation contributed by components, the solver, interfaces, and loggers.
Before the first timestep the scheduler collects all tasks, resolves their data dependencies into a directed acyclic graph, and produces an ordered schedule.
That schedule is then replayed on every timestep with no further graph analysis.&lt;/p>
&lt;hr>
&lt;h2 id="tasks">Tasks&lt;/h2>
&lt;h3 id="the-task-base-class">The Task base class&lt;/h3>
&lt;p>Every task is an instance of a class that inherits from &lt;code>CPS::Task&lt;/code>
(&lt;code>dpsim-models/include/dpsim-models/Task.h&lt;/code>).
Each subclass implements one member function:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-cpp" data-lang="cpp">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#204a87;font-weight:bold">virtual&lt;/span> &lt;span style="color:#204a87;font-weight:bold">void&lt;/span> &lt;span style="color:#000">execute&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#000">Real&lt;/span> &lt;span style="color:#000">time&lt;/span>&lt;span style="color:#000;font-weight:bold">,&lt;/span> &lt;span style="color:#000">Int&lt;/span> &lt;span style="color:#000">timeStepCount&lt;/span>&lt;span style="color:#000;font-weight:bold">)&lt;/span> &lt;span style="color:#ce5c00;font-weight:bold">=&lt;/span> &lt;span style="color:#0000cf;font-weight:bold">0&lt;/span>&lt;span style="color:#000;font-weight:bold">;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>To participate in scheduling, a task declares its data dependencies through three attribute lists that are populated in the task&amp;rsquo;s constructor:&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>List&lt;/th>
&lt;th>Meaning&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;code>mAttributeDependencies&lt;/code>&lt;/td>
&lt;td>Attributes this task &lt;em>reads&lt;/em> in &lt;code>execute()&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>mModifiedAttributes&lt;/code>&lt;/td>
&lt;td>Attributes this task &lt;em>writes&lt;/em> in &lt;code>execute()&lt;/code>&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>mPrevStepDependencies&lt;/code>&lt;/td>
&lt;td>Attributes whose value from the &lt;em>previous&lt;/em> timestep this task needs&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>All three lists hold &lt;code>AttributeBase::Ptr&lt;/code> objects, the same pointers used throughout the component model.
See &lt;a href="https://sogno.energy/dpsim/docs/developer-guide/attributes-and-scheduling/attributes/">Attributes&lt;/a> for details on the attribute system.&lt;/p>
&lt;div class="alert alert-warning" role="alert">
&lt;h4 class="alert-heading">Watch out: only attributes can leave a component&lt;/h4>
&lt;p>Only attributes can participate in scheduling. Plain C++ member variables, a &lt;code>Real&lt;/code>, a &lt;code>Matrix&lt;/code> or
an internal state struct, are invisible to the scheduler, so no dependency edge can be formed around
them.&lt;/p>
&lt;p>The same constraint governs recording and exchange: &lt;code>DataLogger&lt;/code> and &lt;code>RealTimeDataLogger&lt;/code> both
implement &lt;code>DataLoggerInterface&lt;/code>, whose &lt;code>logAttribute()&lt;/code> accepts only an &lt;code>AttributeBase::Ptr&lt;/code>, and the
VILLASnode interface works the same way.&lt;/p>
&lt;p>So any value that must cross a task boundary, be written to a result file, or be exchanged with
another tool has to be stored in an &lt;code>Attribute&amp;lt;T&amp;gt;&lt;/code>. Deciding that late means changing the component
rather than the call site.&lt;/p>
&lt;/div>
&lt;p>The component text logger (&lt;code>CPS::Logger&lt;/code>, backed by spdlog) is a separate mechanism used for human-readable debug and diagnostic output.
It is not part of the scheduling system and can print any value regardless of whether it is an attribute.&lt;/p>
&lt;p>For practical rules on when a variable should be an attribute versus a plain member variable, see &lt;a href="https://sogno.energy/dpsim/docs/developer-guide/attributes-and-scheduling/attribute-usage/">Attribute Usage Guidelines&lt;/a>.&lt;/p>
&lt;h3 id="common-component-task-conventions">Common component task conventions&lt;/h3>
&lt;p>The names below are component and solver conventions, not scheduler-level concepts.
The scheduler only sees the attribute dependencies a task declares; it has no notion of a &amp;ldquo;PreStep&amp;rdquo; or &amp;ldquo;PostStep&amp;rdquo; and never orders tasks by these names.&lt;/p>
&lt;p>MNA components typically define two task classes per component:&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Task&lt;/th>
&lt;th>Typical responsibility&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;code>MnaPreStep&lt;/code>&lt;/td>
&lt;td>Component-specific preparation before the matrix solve, often updating internal state and stamping the right-hand-side contribution&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>MnaPostStep&lt;/code>&lt;/td>
&lt;td>Component-specific update after the matrix solve, often reading the solution vector to update interface voltages and currents&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>This is a common pattern rather than a fixed rule; the exact work each task does is component-specific.
Signal-domain components (regulators, governors, control blocks) define their own task list via &lt;code>getTasks()&lt;/code>; many separate previous-step state handling from output updates, for example a &lt;code>PreStep&lt;/code> that copies state from the previous step and a &lt;code>Step&lt;/code> that updates the block outputs.&lt;/p>
&lt;p>The solver itself contributes a task that solves the MNA system; individual components do not depend on it by name, they depend on &lt;code>leftVector&lt;/code> instead (see below).&lt;/p>
&lt;hr>
&lt;h2 id="building-the-schedule">Building the schedule&lt;/h2>
&lt;h3 id="task-collection">Task collection&lt;/h3>
&lt;p>&lt;code>Simulation::prepSchedule()&lt;/code> collects all tasks before the first timestep from three top-level sources:&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Solvers&lt;/strong>: each solver contributes its task list via &lt;code>Solver::getTasks()&lt;/code>. For MNA solvers this list bundles:
&lt;ul>
&lt;li>the matrix-solve task,&lt;/li>
&lt;li>MNA component pre-/post-step tasks from &lt;code>MNASimPowerComp::mnaTasks()&lt;/code> (built during solver initialization via &lt;code>mnaAddPreStepDependencies()&lt;/code> / &lt;code>mnaAddPostStepDependencies()&lt;/code>),&lt;/li>
&lt;li>signal-domain component tasks returned by &lt;code>SimSignalComp::getTasks()&lt;/code>,&lt;/li>
&lt;li>optional solver-side tasks, such as state-space extraction, when enabled.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>&lt;strong>Interfaces&lt;/strong>: each interface contributes its own tasks via &lt;code>Interface::getTasks()&lt;/code>. These typically depend on the attributes exchanged with external systems.&lt;/li>
&lt;li>&lt;strong>Loggers&lt;/strong>: each logger contributes a logging task via &lt;code>Logger::getTask()&lt;/code>, depending on the logged attributes so values are written after the producing tasks have run.&lt;/li>
&lt;/ul>
&lt;p>All tasks are placed in a flat &lt;code>Task::List&lt;/code> and handed to the scheduler.&lt;/p>
&lt;h3 id="dependency-resolution">Dependency resolution&lt;/h3>
&lt;p>&lt;code>Scheduler::resolveDeps()&lt;/code> (&lt;code>dpsim/src/Scheduler.cpp&lt;/code>) translates the attribute-level declarations into directed edges between tasks.
For every attribute in &lt;code>mModifiedAttributes&lt;/code>, it finds all tasks that list that attribute in their &lt;code>mAttributeDependencies&lt;/code> and adds an edge:&lt;/p>
&lt;div class="mermaid">
graph LR
A["Task A&lt;br/>modifies attr_X"] -->|attr_X| B["Task B&lt;br/>depends on attr_X"]
&lt;/div>
&lt;p>Task A modifies &lt;code>attr_X&lt;/code> and task B depends on it, so the edge runs A to B and the scheduler must
place A first.&lt;/p>
&lt;p>A special &lt;code>Root&lt;/code> sentinel task is inserted as a sink for all &lt;code>mPrevStepDependencies&lt;/code> entries.
Its role is explained in the pruning step below.&lt;/p>
&lt;h3 id="topological-sort-and-pruning">Topological sort and pruning&lt;/h3>
&lt;p>&lt;code>Scheduler::topologicalSort()&lt;/code> first runs a backward breadth-first search (BFS) from &lt;code>Root&lt;/code>, marking every task that transitively contributes to a simulation output.
Tasks not reachable in this pass are &lt;strong>dropped&lt;/strong> from the schedule because they produce data no downstream consumer reads in the current timestep.&lt;/p>
&lt;p>Kahn&amp;rsquo;s algorithm then processes the remaining tasks in dependency order and appends them to the schedule.
The result is a flat, ordered list in which every task appears after all of its current-step predecessors.&lt;/p>
&lt;p>The &lt;code>Root&lt;/code> sentinel matters here: it holds a reference to an external attribute updated by an interface or by the solver, so the backward BFS reaches it and keeps every task that writes previous-timestep state, even when that output is only consumed in the next timestep.&lt;/p>
&lt;h3 id="level-scheduling">Level scheduling&lt;/h3>
&lt;p>For parallel execution the ordered list is converted into levels by &lt;code>Scheduler::levelSchedule()&lt;/code>.
Each task is assigned to the level one greater than the highest-level task it depends on:&lt;/p>
&lt;div class="mermaid">
graph TD
subgraph L0["level 0: no dependencies, all start at once"]
T1; T2; T3
end
subgraph L1["level 1: depend only on level 0"]
T4; T5
end
subgraph L2["level 2"]
T6
end
T1 --> T4
T2 --> T4
T3 --> T5
T4 --> T6
T5 --> T6
&lt;/div>
&lt;p>Tasks within the same level have no data dependencies between them and can execute in parallel.
The scheduler guarantees that all tasks in level &lt;em>k&lt;/em> finish before any task in level &lt;em>k+1&lt;/em> starts.&lt;/p>
&lt;p>&lt;img src="task_graph_levels.svg" alt="image">&lt;/p>
&lt;h3 id="scheduler-variants">Scheduler variants&lt;/h3>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Class&lt;/th>
&lt;th>Parallelism strategy&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;code>SequentialScheduler&lt;/code>&lt;/td>
&lt;td>Single-threaded; follows topological order&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>ThreadLevelScheduler&lt;/code>&lt;/td>
&lt;td>Distributes each level across &lt;em>N&lt;/em> worker threads&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>ThreadListScheduler&lt;/code>&lt;/td>
&lt;td>Distributes tasks greedily across &lt;em>N&lt;/em> threads&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>OpenMPLevelScheduler&lt;/code>&lt;/td>
&lt;td>Uses &lt;code>#pragma omp parallel for&lt;/code> per level&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>The scheduler is chosen at &lt;code>Simulation&lt;/code> construction time; &lt;code>SequentialScheduler&lt;/code> is the default.&lt;/p>
&lt;h3 id="per-timestep-execution">Per-timestep execution&lt;/h3>
&lt;p>&lt;code>Scheduler::step(time, timeStepCount)&lt;/code> is called once per timestep.
For the sequential scheduler:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-cpp" data-lang="cpp">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#204a87;font-weight:bold">for&lt;/span> &lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#204a87;font-weight:bold">auto&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">&amp;amp;&lt;/span> &lt;span style="color:#f57900">task&lt;/span> &lt;span style="color:#000;font-weight:bold">:&lt;/span> &lt;span style="color:#000">mSchedule&lt;/span>&lt;span style="color:#000;font-weight:bold">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#000">task&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">-&amp;gt;&lt;/span>&lt;span style="color:#000">execute&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#000">time&lt;/span>&lt;span style="color:#000;font-weight:bold">,&lt;/span> &lt;span style="color:#000">timeStepCount&lt;/span>&lt;span style="color:#000;font-weight:bold">);&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Parallel schedulers distribute tasks across threads within each level and synchronize with a barrier before advancing to the next level.&lt;/p>
&lt;hr>
&lt;p>This page describes how the scheduler works. For how to give a component tasks of its own, see
&lt;a href="https://sogno.energy/dpsim/docs/developer-guide/attributes-and-scheduling/adding-tasks/">adding tasks to a component&lt;/a>.&lt;/p></description></item><item><title>Docs: State-Space Extraction Support</title><link>https://sogno.energy/dpsim/docs/reference/state-space-extraction-support/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://sogno.energy/dpsim/docs/reference/state-space-extraction-support/</guid><description>
&lt;p>For how extraction works and how to enable it, see
&lt;a href="https://sogno.energy/dpsim/docs/developer-guide/solvers/state-space-extraction/">state-space extraction&lt;/a>.&lt;/p>
&lt;p>State-space extraction is available for EMT Ph3 and DP Ph1 simulations
using the direct MNA solver. For models containing switches, the extracted matrix represents the currently
active switch configuration. The matrix is recomputed when the switch status
changes.&lt;/p>
&lt;h2 id="emt-ph3">EMT Ph3&lt;/h2>
&lt;p>Supported components with extraction states are:&lt;/p>
&lt;ul>
&lt;li>&lt;code>EMT::Ph3::Inductor&lt;/code>,&lt;/li>
&lt;li>&lt;code>EMT::Ph3::Capacitor&lt;/code>,&lt;/li>
&lt;li>&lt;code>EMT::Ph3::TwoTerminalVTypeSSNComp&lt;/code>,&lt;/li>
&lt;li>&lt;code>EMT::Ph3::TwoTerminalVTypeVariableSSNComp&lt;/code>.&lt;/li>
&lt;/ul>
&lt;p>Supported algebraic components without extraction states are:&lt;/p>
&lt;ul>
&lt;li>&lt;code>EMT::Ph3::Resistor&lt;/code>,&lt;/li>
&lt;li>&lt;code>EMT::Ph3::Switch&lt;/code>,&lt;/li>
&lt;li>&lt;code>EMT::Ph3::VoltageSource&lt;/code>.&lt;/li>
&lt;/ul>
&lt;p>The following composite components are supported through their immediate
MNA subcomponents:&lt;/p>
&lt;ul>
&lt;li>&lt;code>EMT::Ph3::NetworkInjection&lt;/code>,&lt;/li>
&lt;li>&lt;code>EMT::Ph3::PiLine&lt;/code>,&lt;/li>
&lt;li>&lt;code>EMT::Ph3::RXLoad&lt;/code>,&lt;/li>
&lt;li>&lt;code>EMT::Ph3::RxLine&lt;/code>,&lt;/li>
&lt;li>&lt;code>EMT::Ph3::Shunt&lt;/code>,&lt;/li>
&lt;li>&lt;code>EMT::Ph3::Transformer&lt;/code>.&lt;/li>
&lt;/ul>
&lt;h2 id="dp-ph1">DP Ph1&lt;/h2>
&lt;p>Supported components with extraction states are:&lt;/p>
&lt;ul>
&lt;li>&lt;code>DP::Ph1::Inductor&lt;/code>,&lt;/li>
&lt;li>&lt;code>DP::Ph1::Capacitor&lt;/code>,&lt;/li>
&lt;li>&lt;code>DP::Ph1::TwoTerminalVTypeSSNComp&lt;/code>,&lt;/li>
&lt;li>&lt;code>DP::Ph1::MixedVTypeVariableSSNComp&lt;/code>.&lt;/li>
&lt;/ul>
&lt;p>Supported algebraic components without extraction states are:&lt;/p>
&lt;ul>
&lt;li>&lt;code>DP::Ph1::Resistor&lt;/code>,&lt;/li>
&lt;li>&lt;code>DP::Ph1::Switch&lt;/code>,&lt;/li>
&lt;li>&lt;code>DP::Ph1::VoltageSource&lt;/code>.&lt;/li>
&lt;/ul>
&lt;p>The following composite components are supported through their immediate
MNA subcomponents:&lt;/p>
&lt;ul>
&lt;li>&lt;code>DP::Ph1::NetworkInjection&lt;/code>,&lt;/li>
&lt;li>&lt;code>DP::Ph1::PiLine&lt;/code>,&lt;/li>
&lt;li>&lt;code>DP::Ph1::RXLoad&lt;/code>,&lt;/li>
&lt;li>&lt;code>DP::Ph1::RxLine&lt;/code>,&lt;/li>
&lt;li>&lt;code>DP::Ph1::Shunt&lt;/code>,&lt;/li>
&lt;li>&lt;code>DP::Ph1::Transformer&lt;/code>.&lt;/li>
&lt;/ul>
&lt;p>Supported composite components are expanded by one level during contributor
discovery. Their immediate MNA subcomponents provide the state-space
contributions, while the composite parent remains part of the simulation and
retains its normal MNA stamping. Nested composites are currently unsupported.&lt;/p>
&lt;p>Other component types are rejected explicitly when state-space extraction is
enabled.&lt;/p></description></item><item><title>Docs: Adding a Machine</title><link>https://sogno.energy/dpsim/docs/tutorials/python/a-machine/</link><pubDate>Fri, 31 Jul 2026 00:00:00 +0000</pubDate><guid>https://sogno.energy/dpsim/docs/tutorials/python/a-machine/</guid><description>
&lt;p>Everything so far has been passive. A synchronous machine brings two things that no previous
tutorial needed: it has mechanical state, so it can swing, and it has to be told the operating point
it starts from rather than deducing it.&lt;/p>
&lt;p>The network is a machine feeding a strong grid through a line, with a fault applied at the machine
terminal for 100 ms.&lt;/p>
&lt;h2 id="machine-parameters">Machine parameters&lt;/h2>
&lt;p>A machine is specified by operational parameters rather than winding data:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-python" data-lang="python">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#000">gen&lt;/span> &lt;span style="color:#ce5c00;font-weight:bold">=&lt;/span> &lt;span style="color:#000">dpsimpy&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">dp&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">ph1&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">SynchronGenerator4OrderVBR&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#4e9a06">&amp;#34;gen&amp;#34;&lt;/span>&lt;span style="color:#000;font-weight:bold">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#000">gen&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">set_operational_parameters_per_unit&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#000">nom_power&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">=&lt;/span>&lt;span style="color:#0000cf;font-weight:bold">555e6&lt;/span>&lt;span style="color:#000;font-weight:bold">,&lt;/span> &lt;span style="color:#000">nom_voltage&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">=&lt;/span>&lt;span style="color:#0000cf;font-weight:bold">24e3&lt;/span>&lt;span style="color:#000;font-weight:bold">,&lt;/span> &lt;span style="color:#000">nom_frequency&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">=&lt;/span>&lt;span style="color:#0000cf;font-weight:bold">60.0&lt;/span>&lt;span style="color:#000;font-weight:bold">,&lt;/span> &lt;span style="color:#000">H&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">=&lt;/span>&lt;span style="color:#0000cf;font-weight:bold">3.7&lt;/span>&lt;span style="color:#000;font-weight:bold">,&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#000">Ld&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">=&lt;/span>&lt;span style="color:#0000cf;font-weight:bold">1.81&lt;/span>&lt;span style="color:#000;font-weight:bold">,&lt;/span> &lt;span style="color:#000">Lq&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">=&lt;/span>&lt;span style="color:#0000cf;font-weight:bold">1.76&lt;/span>&lt;span style="color:#000;font-weight:bold">,&lt;/span> &lt;span style="color:#000">L0&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">=&lt;/span>&lt;span style="color:#0000cf;font-weight:bold">0.15&lt;/span>&lt;span style="color:#000;font-weight:bold">,&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#000">Ld_t&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">=&lt;/span>&lt;span style="color:#0000cf;font-weight:bold">0.3&lt;/span>&lt;span style="color:#000;font-weight:bold">,&lt;/span> &lt;span style="color:#000">Lq_t&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">=&lt;/span>&lt;span style="color:#0000cf;font-weight:bold">0.65&lt;/span>&lt;span style="color:#000;font-weight:bold">,&lt;/span> &lt;span style="color:#000">Td0_t&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">=&lt;/span>&lt;span style="color:#0000cf;font-weight:bold">8.0&lt;/span>&lt;span style="color:#000;font-weight:bold">,&lt;/span> &lt;span style="color:#000">Tq0_t&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">=&lt;/span>&lt;span style="color:#0000cf;font-weight:bold">1.0&lt;/span>&lt;span style="color:#000;font-weight:bold">,&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#000;font-weight:bold">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>The inductances are in per unit on the machine&amp;rsquo;s own base and the time constants in seconds. &lt;code>H&lt;/code> is
the inertia constant, and it sets how fast the machine can accelerate: a low &lt;code>H&lt;/code> swings further for
the same disturbance.&lt;/p>
&lt;p>Each model order takes a different parameter set, and the difference is exactly the states it keeps.
The third order model omits &lt;code>Lq_t&lt;/code> and &lt;code>Tq0_t&lt;/code> because it has no q-axis rotor state at all. The
sixth order model adds the subtransient set, &lt;code>Ld_s&lt;/code>, &lt;code>Lq_s&lt;/code>, &lt;code>Td0_s&lt;/code>, &lt;code>Tq0_s&lt;/code> and &lt;code>Taa&lt;/code>. Passing the
wrong set raises a &lt;code>TypeError&lt;/code> listing the accepted signature. The equations are derived under
&lt;a href="https://sogno.energy/dpsim/docs/concepts/models/synchronous-generator/reduced-order/">reduced order machine models&lt;/a>.&lt;/p>
&lt;h2 id="the-machine-base-and-the-network-around-it">The machine base and the network around it&lt;/h2>
&lt;p>The machine parameters are per unit on the machine&amp;rsquo;s own base, while the line is given in ohms, so
the two only make sense together. The base impedance follows from the machine rating,&lt;/p>
&lt;div class="math">$$Z_{base} = \frac{V_{nom}^2}{S_{nom}} = \frac{(24\,\mathrm{kV})^2}{555\,\mathrm{MVA}} = 1.04 \ \Omega .$$&lt;/div>&lt;p>A line reactance of a few tenths of an ohm is therefore a few tenths per unit, which is an ordinary
transmission connection. The same line specified as 20 mH would be 7.5 Ω, above 7 per unit, and no
machine delivers rated power through that.&lt;/p>
&lt;div class="alert alert-warning" role="alert">
&lt;h4 class="alert-heading">Watch out: an impossible operating point looks like instability&lt;/h4>
The consequence is worth knowing because it is not reported as an error. A machine asked to deliver
more power than the network can carry simply accelerates: the rotor speed climbs monotonically and
never returns, which looks like an unstable model rather than an impossible operating point.
&lt;/div>
&lt;h2 id="initializing-the-machine">Initializing the machine&lt;/h2>
&lt;p>The network is initialized from a powerflow exactly as in
&lt;a href="https://sogno.energy/dpsim/docs/tutorials/python/two-bus-network/">the two-bus tutorial&lt;/a>. The machine additionally needs its own
operating point:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-python" data-lang="python">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#000">system_dp&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">init_with_powerflow&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#000">systemPF&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">=&lt;/span>&lt;span style="color:#000">system_pf&lt;/span>&lt;span style="color:#000;font-weight:bold">,&lt;/span> &lt;span style="color:#000">domain&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">=&lt;/span>&lt;span style="color:#000">dpsimpy&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">Domain&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">DP&lt;/span>&lt;span style="color:#000;font-weight:bold">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#000">vterm&lt;/span> &lt;span style="color:#ce5c00;font-weight:bold">=&lt;/span> &lt;span style="color:#000">n1&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">initial_single_voltage&lt;/span>&lt;span style="color:#000;font-weight:bold">()&lt;/span> &lt;span style="color:#8f5902;font-style:italic"># from the powerflow, magnitude and angle&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#000">gen&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">.&lt;/span>&lt;span style="color:#000">set_initial_values&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#000">init_complex_electrical_power&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">=&lt;/span>&lt;span style="color:#204a87">complex&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#0000cf;font-weight:bold">300e6&lt;/span>&lt;span style="color:#000;font-weight:bold">,&lt;/span> &lt;span style="color:#0000cf;font-weight:bold">0&lt;/span>&lt;span style="color:#000;font-weight:bold">),&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#000">init_mechanical_power&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">=&lt;/span>&lt;span style="color:#0000cf;font-weight:bold">300e6&lt;/span>&lt;span style="color:#000;font-weight:bold">,&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#000">init_complex_terminal_voltage&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">=&lt;/span>&lt;span style="color:#000">vterm&lt;/span>&lt;span style="color:#000;font-weight:bold">,&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#000;font-weight:bold">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>
&lt;div class="alert alert-warning" role="alert">
&lt;h4 class="alert-heading">Watch out: take the terminal voltage from the powerflow&lt;/h4>
Take the terminal voltage from the node rather than writing it out. It is tempting to pass
&lt;code>complex(24e3, 0)&lt;/code> since that is the scheduled magnitude, but the generator bus is a PV bus and its
voltage &lt;strong>leads&lt;/strong> the slack: here by 0.158 rad, about 9°. Supplying angle zero gives the machine a
rotor position inconsistent with the network it is connected to, and it starts by swinging into
agreement.
&lt;/div>
&lt;p>The difference is measurable. With the angle assumed zero, the rotor speed oscillates by ±0.33% for
the first half second, and a fault applied at 0.5 s lands on top of a transient that has nothing to
do with it. Taking the voltage from the node, the pre-fault speed is flat to 5 × 10⁻⁶ pu and the
only thing in the result is the fault.&lt;/p>
&lt;p>That the mechanical power equals the scheduled electrical power is the other half of the same
condition: if they disagree, the machine accelerates or decelerates from the first step.&lt;/p>
&lt;h2 id="what-the-model-order-changes">What the model order changes&lt;/h2>
&lt;p>The same fault, applied to the same machine at the same instant, with three model orders:&lt;/p>
&lt;p>&lt;img src="orders.svg" alt="Rotor speed through a fault for third, fourth and sixth order machine models">&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Model order&lt;/th>
&lt;th>Peak speed&lt;/th>
&lt;th>States&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>3rd&lt;/td>
&lt;td>1.00362 pu&lt;/td>
&lt;td>field winding only&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>4th&lt;/td>
&lt;td>1.00308 pu&lt;/td>
&lt;td>field and one q-axis damper&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>6a&lt;/td>
&lt;td>1.00228 pu&lt;/td>
&lt;td>transient and subtransient, both axes&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>All three peak at the clearing instant and all three recover, but the third order model swings
noticeably further. That is not numerical: omitting the q-axis rotor removes damping that is
physically present, so the third order machine is optimistic about how far it swings and
pessimistic about how well it settles.&lt;/p>
&lt;p>The practical reading is that model order is a statement about which phenomena you intend to
capture. For a first-swing stability question the fourth order model is the usual choice. The
subtransient orders matter when the first cycles after the fault are the subject rather than the
envelope of the swing.&lt;/p>
&lt;h2 id="the-script">The script&lt;/h2>
&lt;p>The complete script for this page is &lt;a href="https://github.com/sogno-platform/dpsim/blob/master/examples/Python/Tutorials/06_a_machine.py">&lt;code>06_a_machine.py&lt;/code>&lt;/a> under &lt;code>examples/Python/Tutorials&lt;/code>. The numbers quoted above are the numbers it prints, so if the two ever disagree the page is the one that is wrong.&lt;/p>
&lt;h2 id="next">Next&lt;/h2>
&lt;p>The machine is a source of energy with its own dynamics. Next is a converter, where the dynamics
are in the control rather than in a rotor.&lt;/p></description></item><item><title>Docs: Loads</title><link>https://sogno.energy/dpsim/docs/concepts/models/loads/</link><pubDate>Fri, 31 Jul 2026 00:00:00 +0000</pubDate><guid>https://sogno.energy/dpsim/docs/concepts/models/loads/</guid><description>
&lt;p>A load is specified as an active and a reactive power at a nominal voltage, but a nodal solver needs
either an admittance or a current. The two ways of making that conversion behave differently as the
terminal voltage moves away from nominal, and the difference matters more than the model&amp;rsquo;s
simplicity suggests.&lt;/p>
&lt;h2 id="constant-impedance">Constant impedance&lt;/h2>
&lt;p>The powers are converted once, at the nominal voltage, into a resistance and a reactance,&lt;/p>
&lt;div class="math">$$R = \frac{V_{nom}^2}{P}, \qquad X = \frac{V_{nom}^2}{Q},$$&lt;/div>&lt;p>and the reactance becomes an inductance or a capacitance according to its sign,&lt;/p>
&lt;div class="math">$$L = \frac{X}{\omega} \quad (X > 0), \qquad C = -\frac{1}{\omega X} \quad (X &lt; 0).$$&lt;/div>&lt;p>The load is then an ordinary passive branch to ground, and it is stamped exactly as the elements it
is built from.&lt;/p>
&lt;p>Both conversions divide by a power, so a load with zero active power has no defined resistance and
one with zero reactive power has no defined reactance. Such a branch is simply absent rather than
infinite, which is the correct behaviour but means a load specified with one of the two set to zero
is not the load a reader might expect.&lt;/p>
&lt;p>The assumption is that consumption follows the square of the voltage. At nominal voltage the load
draws exactly $P$ and $Q$; at 0.9 per unit it draws 81 percent of them. For a genuinely impedance
like load this is right, and for anything regulated it understates the demand during a depression.&lt;/p>
&lt;h2 id="constant-current">Constant current&lt;/h2>
&lt;p>The alternative injects a current derived from the specified power,&lt;/p>
&lt;div class="math">$$\underline{I} = \left( \frac{S}{V_{nom}} \right)^{*},$$&lt;/div>&lt;p>held fixed as the terminal voltage varies. Consumption then falls linearly with voltage rather than
quadratically, which is closer to the behaviour of many aggregated loads.&lt;/p>
&lt;p>Note what this is not. Because the current is computed from the nominal voltage and not from the
present terminal voltage, this is a constant current model and not a constant power one. A true
constant power load would require the current to be recomputed from the solved voltage at every
step, making the component nonlinear and the nodal solve iterative. The linear model is used
because it keeps the system matrix constant.&lt;/p>
&lt;h2 id="which-to-use">Which to use&lt;/h2>
&lt;p>The three canonical load characteristics are constant impedance, constant current and constant
power, differing in whether demand follows the square of voltage, the voltage, or neither. Only the
first two are available as linear models. For a voltage excursion of a few percent the choice
changes little; for a deep depression during a fault it changes the answer materially, and the
constant impedance model is the optimistic one because it sheds load exactly when the network is
weakest.&lt;/p>
&lt;h2 id="shunts">Shunts&lt;/h2>
&lt;p>A shunt is specified directly as a conductance and a susceptance rather than as a power, so no
conversion is involved. It is the natural representation for a capacitor bank or a reactor, where
the rating is an admittance and the consumed power is a consequence of the voltage rather than the
specification.&lt;/p></description></item><item><title>Docs: State-Space Extraction</title><link>https://sogno.energy/dpsim/docs/concepts/state-space-extraction-theory/</link><pubDate>Thu, 28 May 2026 00:00:00 +0000</pubDate><guid>https://sogno.energy/dpsim/docs/concepts/state-space-extraction-theory/</guid><description>
&lt;p>A nodal simulation computes a trajectory: given a set of sources and initial conditions it
produces the node voltages step by step. It does not, by itself, say anything about the system&amp;rsquo;s
modes, its damping, or how close it is to instability. Those questions are answered by the
state-space description, which the extraction recovers from the discretised network that the
simulation is already solving. The result is the discrete-time model of exactly what is being
simulated, including the effect of the discretisation itself, rather than of an idealised
continuous system that the simulation approximates.&lt;/p>
&lt;h2 id="where-the-discrete-model-comes-from">Where the Discrete Model Comes From&lt;/h2>
&lt;p>The starting point is the companion form described under
&lt;a href="https://sogno.energy/dpsim/docs/concepts/nodal-analysis/">nodal analysis&lt;/a>. Each dynamic element has already been turned
into a conductance in parallel with a history current source, and its history term is a
difference equation in the element&amp;rsquo;s own quantity: the current for an inductor, the voltage for a
capacitor. Those quantities are the states. The network solve then supplies the terminal voltages
that drive them.&lt;/p>
&lt;p>This gives a system in two parts rather than one. The states advance using their own local
recurrence together with the network solution at the new instant, and the network solution at
that instant is itself driven by the states carried over from the previous one.&lt;/p>
&lt;h2 id="assembled-form">Assembled Form&lt;/h2>
&lt;p>The extracted model is assembled in the form:&lt;/p>
&lt;div class="math">$$\mathbf{x}[k+1]
=
\mathbf{A}_{d,\mathrm{local}} \mathbf{x}[k]
+
\mathbf{B}_{d,\mathrm{MNA}} \mathbf{x}_{\mathrm{MNA}}[k+1]$$&lt;/div>
&lt;div class="math">$$\mathbf{Y} \mathbf{x}_{\mathrm{MNA}}[k+1]
=
\mathbf{C}_{d,\mathrm{MNA}} \mathbf{x}[k]$$&lt;/div>&lt;p>where:&lt;/p>
&lt;ul>
&lt;li>$\mathbf{x}$ is the extraction-state vector,&lt;/li>
&lt;li>$\mathbf{x}_{\mathrm{MNA}}$ is the full MNA unknown vector,&lt;/li>
&lt;li>$\mathbf{Y}$ is the active MNA system matrix,&lt;/li>
&lt;li>$\mathbf{A}_{d,\mathrm{local}}$ contains local component state-transition contributions,&lt;/li>
&lt;li>$\mathbf{B}_{d,\mathrm{MNA}}$ maps MNA unknowns to the state update,&lt;/li>
&lt;li>$\mathbf{C}_{d,\mathrm{MNA}}$ maps extraction states to MNA current injections.&lt;/li>
&lt;/ul>
&lt;p>Eliminating the MNA unknown vector gives the global discrete-time state matrix&lt;/p>
&lt;div class="math">$$\mathbf{A}_{d}
=
\mathbf{A}_{d,\mathrm{local}}
+
\mathbf{B}_{d,\mathrm{MNA}}
\operatorname{solve}
\left(
\mathbf{Y},
\mathbf{C}_{d,\mathrm{MNA}}
\right)$$&lt;/div>&lt;p>The resulting matrix $\mathbf{A}_{d}$ describes the homogeneous discrete-time dynamics of the EMT MNA simulation model at the current operating point and system-matrix configuration.&lt;/p>
&lt;p>The elimination is written as a solve rather than as an inverse for a reason. $\mathbf{Y}$ is
sparse and already factorised for the time stepping, so the term is obtained by one forward and
backward substitution per column of $\mathbf{C}_{d,\mathrm{MNA}}$, that is, one per extracted
state. Forming $\mathbf{Y}^{-1}$ explicitly would discard the sparsity and cost far more than the
extraction itself.&lt;/p>
&lt;h2 id="interpreting-the-result">Interpreting the Result&lt;/h2>
&lt;p>The eigenvalues of $\mathbf{A}_{d}$ are discrete-time modes, so they are read against the unit
circle rather than against the imaginary axis. A mode is stable when $|\lambda_d| &amp;lt; 1$, and the
closer it sits to the unit circle the more lightly damped it is. A corresponding continuous-time
eigenvalue follows from&lt;/p>
&lt;div class="math">$$\lambda_c = \frac{\ln \lambda_d}{\Delta t},$$&lt;/div>&lt;p>whose real part gives the damping and whose imaginary part gives the oscillation frequency, up to
the ambiguity that any frequency above the Nyquist rate of the time step is indistinguishable
from one below it.&lt;/p>
&lt;p>It is worth being clear about what this eigenvalue belongs to. It is a mode of the discretised
system, not of the underlying continuous one. The two differ by the distortion the integration
rule introduces, and for the trapezoidal rule that distortion grows with frequency, so the fastest
modes are the least faithful. Reducing the time step reduces the discrepancy; comparing extraction
results across two time steps is a practical way to see which modes are trustworthy.&lt;/p>
&lt;h2 id="validity">Validity&lt;/h2>
&lt;p>The extracted model is linear and time invariant, and it describes the system only for the
configuration it was taken from. Any event that changes $\mathbf{Y}$, a switch opening or closing
above all, produces a different $\mathbf{A}_{d}$, so a switching study means one extraction per
configuration rather than one for the simulation. The same holds for non-linear elements, whose
contributions are those at the operating point reached when the extraction was performed; moving
the operating point requires extracting again.&lt;/p>
&lt;h2 id="references">References&lt;/h2>
&lt;ul>
&lt;li>J. A. Hollman and J. R. Marti, &lt;em>Step-by-step eigenvalue analysis with EMTP discrete-time solutions&lt;/em>, &lt;em>IEEE Transactions on Power Systems&lt;/em>, 2010. &lt;a href="https://doi.org/10.1109/TPWRS.2009.2039810">https://doi.org/10.1109/TPWRS.2009.2039810&lt;/a>&lt;/li>
&lt;li>Y. Han, H. Sun, B. Huang, S. Qin, M. Mu, and Y. Yu, “Discrete-Time State-Space Construction Method for SSO Analysis of Renewable Power Generation Integrated AC/DC Hybrid System,” &lt;em>IEEE Transactions on Power Systems&lt;/em>, 2022. &lt;a href="https://doi.org/10.1109/TPWRS.2021.3115248">https://doi.org/10.1109/TPWRS.2021.3115248&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>Docs: Co-simulation and Interfaces</title><link>https://sogno.energy/dpsim/docs/user-guide/co-simulation/</link><pubDate>Thu, 13 Feb 2025 00:00:00 +0000</pubDate><guid>https://sogno.energy/dpsim/docs/user-guide/co-simulation/</guid><description>
&lt;p>Interfaces can be used to exchange simulation signals between a DPsim simulation and other soft- or hardware, for example an MQTT-broker or an FPGA.
Simulation signals in the form of &lt;a href="https://sogno.energy/dpsim/docs/developer-guide/attributes-and-scheduling/attributes/">Attributes&lt;/a> can be &lt;strong>imported&lt;/strong> or &lt;strong>exported&lt;/strong> once per simulation time step.
Interfaces are subclasses of &lt;code>Interface&lt;/code> and implement the methods &lt;code>addExport&lt;/code> and &lt;code>addImport&lt;/code>, which add dependencies to the passed attribute that forward the attribute value from or to the interface.
This way, attributes that are imported are read from the interface before they are used in any DPsim component.
Attributes that are exported are written to the interface after they are set by a DPsim component.&lt;/p>
&lt;h2 id="where-the-boundary-is-and-where-to-read-further">Where the boundary is, and where to read further&lt;/h2>
&lt;p>This page documents only DPsim&amp;rsquo;s side of the interface: which attributes are exchanged, when they
are read and written relative to the time step, and how the simulation is synchronized. Everything
on the other side of the JSON configuration belongs to VILLASnode and is documented there rather
than here, so the two should be read together.&lt;/p>
&lt;p>The parts most often needed are these:&lt;/p>
&lt;ul>
&lt;li>&lt;a href="https://villas.fein-aachen.org/doc/node-node-types.html">Node types&lt;/a> is the reference for the
&lt;code>type&lt;/code> key and its per-type options. Which protocols are available, and what each one requires,
is decided here rather than in DPsim.&lt;/li>
&lt;li>&lt;a href="https://villas.fein-aachen.org/doc/node.html">Nodes&lt;/a> covers the surrounding configuration
structure that the node object sits in.&lt;/li>
&lt;li>&lt;a href="https://villas.fein-aachen.org/doc/node-hooks.html">Hooks&lt;/a> describe the processing that can be
applied to samples in transit, such as scaling, limiting or statistics. Anything that can be done
with a hook does not need to be done in the simulation, which is usually the better place for it.&lt;/li>
&lt;/ul>
&lt;div class="alert alert-warning" role="alert">
&lt;h4 class="alert-heading">Watch out: the signal mapping is positional, not by name&lt;/h4>
Two things about the split are worth knowing before configuring anything. The signal ordering in
the VILLASnode configuration must match the order in which attributes are exported and imported,
because the mapping is positional rather than by name; a mismatch produces a running simulation
exchanging the wrong quantities. And the queueless interface additionally reserves the first input
signal for a sequence number, so its signal list is offset by one relative to a queued
configuration carrying otherwise identical data. That is described with the rest of the
configuration under
&lt;a href="https://sogno.energy/dpsim/docs/developer-guide/attributes-and-scheduling/interface-tasks/">interfaces&lt;/a>.
&lt;/div></description></item><item><title>Docs: Adding Tasks to a Component</title><link>https://sogno.energy/dpsim/docs/developer-guide/attributes-and-scheduling/adding-tasks/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://sogno.energy/dpsim/docs/developer-guide/attributes-and-scheduling/adding-tasks/</guid><description>
&lt;p>How to attach tasks to a component. For how the scheduler consumes them, see
&lt;a href="https://sogno.energy/dpsim/docs/developer-guide/attributes-and-scheduling/scheduling/">scheduling&lt;/a>.&lt;/p>
&lt;h2 id="signal-components">Signal components&lt;/h2>
&lt;p>Signal components inherit from &lt;code>SimSignalComp&lt;/code> and return their tasks from &lt;code>getTasks()&lt;/code>.
The usual pattern is to define inner &lt;code>Task&lt;/code> classes whose constructors populate the dependency lists, then instantiate them in &lt;code>getTasks()&lt;/code>:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-cpp" data-lang="cpp">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#204a87;font-weight:bold">class&lt;/span> &lt;span style="color:#000">MyComponent&lt;/span> &lt;span style="color:#ce5c00;font-weight:bold">:&lt;/span> &lt;span style="color:#204a87;font-weight:bold">public&lt;/span> &lt;span style="color:#000">SimSignalComp&lt;/span> &lt;span style="color:#000;font-weight:bold">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#204a87;font-weight:bold">public&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">:&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#204a87;font-weight:bold">const&lt;/span> &lt;span style="color:#000">Attribute&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">&amp;lt;&lt;/span>&lt;span style="color:#000">Real&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">&amp;gt;::&lt;/span>&lt;span style="color:#000">Ptr&lt;/span> &lt;span style="color:#000">mInput&lt;/span>&lt;span style="color:#000;font-weight:bold">;&lt;/span> &lt;span style="color:#8f5902;font-style:italic">// written by upstream component
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#8f5902;font-style:italic">&lt;/span> &lt;span style="color:#204a87;font-weight:bold">const&lt;/span> &lt;span style="color:#000">Attribute&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">&amp;lt;&lt;/span>&lt;span style="color:#000">Real&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">&amp;gt;::&lt;/span>&lt;span style="color:#000">Ptr&lt;/span> &lt;span style="color:#000">mOutput&lt;/span>&lt;span style="color:#000;font-weight:bold">;&lt;/span> &lt;span style="color:#8f5902;font-style:italic">// read by downstream component
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#8f5902;font-style:italic">&lt;/span> &lt;span style="color:#204a87;font-weight:bold">const&lt;/span> &lt;span style="color:#000">Attribute&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">&amp;lt;&lt;/span>&lt;span style="color:#000">Real&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">&amp;gt;::&lt;/span>&lt;span style="color:#000">Ptr&lt;/span> &lt;span style="color:#000">mOutputPrev&lt;/span>&lt;span style="color:#000;font-weight:bold">;&lt;/span> &lt;span style="color:#8f5902;font-style:italic">// state carried across timesteps
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#8f5902;font-style:italic">&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#204a87;font-weight:bold">class&lt;/span> &lt;span style="color:#000">PreStep&lt;/span> &lt;span style="color:#ce5c00;font-weight:bold">:&lt;/span> &lt;span style="color:#204a87;font-weight:bold">public&lt;/span> &lt;span style="color:#000">Task&lt;/span> &lt;span style="color:#000;font-weight:bold">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#204a87;font-weight:bold">public&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">:&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#204a87;font-weight:bold">explicit&lt;/span> &lt;span style="color:#000">PreStep&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#000">MyComponent&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">&amp;amp;&lt;/span> &lt;span style="color:#000">comp&lt;/span>&lt;span style="color:#000;font-weight:bold">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#ce5c00;font-weight:bold">:&lt;/span> &lt;span style="color:#000">Task&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">**&lt;/span>&lt;span style="color:#000">comp&lt;/span>&lt;span style="color:#000;font-weight:bold">.&lt;/span>&lt;span style="color:#000">mName&lt;/span> &lt;span style="color:#ce5c00;font-weight:bold">+&lt;/span> &lt;span style="color:#4e9a06">&amp;#34;.PreStep&amp;#34;&lt;/span>&lt;span style="color:#000;font-weight:bold">),&lt;/span> &lt;span style="color:#000">mComp&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#000">comp&lt;/span>&lt;span style="color:#000;font-weight:bold">)&lt;/span> &lt;span style="color:#000;font-weight:bold">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#000">mPrevStepDependencies&lt;/span>&lt;span style="color:#000;font-weight:bold">.&lt;/span>&lt;span style="color:#000">push_back&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#000">mComp&lt;/span>&lt;span style="color:#000;font-weight:bold">.&lt;/span>&lt;span style="color:#000">mOutput&lt;/span>&lt;span style="color:#000;font-weight:bold">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#000">mModifiedAttributes&lt;/span>&lt;span style="color:#000;font-weight:bold">.&lt;/span>&lt;span style="color:#000">push_back&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#000">mComp&lt;/span>&lt;span style="color:#000;font-weight:bold">.&lt;/span>&lt;span style="color:#000">mOutputPrev&lt;/span>&lt;span style="color:#000;font-weight:bold">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#000;font-weight:bold">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#204a87;font-weight:bold">void&lt;/span> &lt;span style="color:#000">execute&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#000">Real&lt;/span> &lt;span style="color:#000">time&lt;/span>&lt;span style="color:#000;font-weight:bold">,&lt;/span> &lt;span style="color:#000">Int&lt;/span> &lt;span style="color:#000">timeStepCount&lt;/span>&lt;span style="color:#000;font-weight:bold">)&lt;/span> &lt;span style="color:#204a87;font-weight:bold">override&lt;/span> &lt;span style="color:#000;font-weight:bold">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#ce5c00;font-weight:bold">**&lt;/span>&lt;span style="color:#000">mComp&lt;/span>&lt;span style="color:#000;font-weight:bold">.&lt;/span>&lt;span style="color:#000">mOutputPrev&lt;/span> &lt;span style="color:#ce5c00;font-weight:bold">=&lt;/span> &lt;span style="color:#ce5c00;font-weight:bold">**&lt;/span>&lt;span style="color:#000">mComp&lt;/span>&lt;span style="color:#000;font-weight:bold">.&lt;/span>&lt;span style="color:#000">mOutput&lt;/span>&lt;span style="color:#000;font-weight:bold">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#000;font-weight:bold">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#204a87;font-weight:bold">private&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">:&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#000">MyComponent&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">&amp;amp;&lt;/span> &lt;span style="color:#000">mComp&lt;/span>&lt;span style="color:#000;font-weight:bold">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#000;font-weight:bold">};&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#204a87;font-weight:bold">class&lt;/span> &lt;span style="color:#000">Step&lt;/span> &lt;span style="color:#ce5c00;font-weight:bold">:&lt;/span> &lt;span style="color:#204a87;font-weight:bold">public&lt;/span> &lt;span style="color:#000">Task&lt;/span> &lt;span style="color:#000;font-weight:bold">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#204a87;font-weight:bold">public&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">:&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#204a87;font-weight:bold">explicit&lt;/span> &lt;span style="color:#000">Step&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#000">MyComponent&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">&amp;amp;&lt;/span> &lt;span style="color:#000">comp&lt;/span>&lt;span style="color:#000;font-weight:bold">)&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#ce5c00;font-weight:bold">:&lt;/span> &lt;span style="color:#000">Task&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">**&lt;/span>&lt;span style="color:#000">comp&lt;/span>&lt;span style="color:#000;font-weight:bold">.&lt;/span>&lt;span style="color:#000">mName&lt;/span> &lt;span style="color:#ce5c00;font-weight:bold">+&lt;/span> &lt;span style="color:#4e9a06">&amp;#34;.Step&amp;#34;&lt;/span>&lt;span style="color:#000;font-weight:bold">),&lt;/span> &lt;span style="color:#000">mComp&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#000">comp&lt;/span>&lt;span style="color:#000;font-weight:bold">)&lt;/span> &lt;span style="color:#000;font-weight:bold">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#000">mAttributeDependencies&lt;/span>&lt;span style="color:#000;font-weight:bold">.&lt;/span>&lt;span style="color:#000">push_back&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#000">mComp&lt;/span>&lt;span style="color:#000;font-weight:bold">.&lt;/span>&lt;span style="color:#000">mInput&lt;/span>&lt;span style="color:#000;font-weight:bold">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#000">mModifiedAttributes&lt;/span>&lt;span style="color:#000;font-weight:bold">.&lt;/span>&lt;span style="color:#000">push_back&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#000">mComp&lt;/span>&lt;span style="color:#000;font-weight:bold">.&lt;/span>&lt;span style="color:#000">mOutput&lt;/span>&lt;span style="color:#000;font-weight:bold">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#000;font-weight:bold">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#204a87;font-weight:bold">void&lt;/span> &lt;span style="color:#000">execute&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#000">Real&lt;/span> &lt;span style="color:#000">time&lt;/span>&lt;span style="color:#000;font-weight:bold">,&lt;/span> &lt;span style="color:#000">Int&lt;/span> &lt;span style="color:#000">timeStepCount&lt;/span>&lt;span style="color:#000;font-weight:bold">)&lt;/span> &lt;span style="color:#204a87;font-weight:bold">override&lt;/span> &lt;span style="color:#000;font-weight:bold">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#000">mComp&lt;/span>&lt;span style="color:#000;font-weight:bold">.&lt;/span>&lt;span style="color:#000">signalStep&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#000">time&lt;/span>&lt;span style="color:#000;font-weight:bold">,&lt;/span> &lt;span style="color:#000">timeStepCount&lt;/span>&lt;span style="color:#000;font-weight:bold">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#000;font-weight:bold">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#204a87;font-weight:bold">private&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">:&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#000">MyComponent&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">&amp;amp;&lt;/span> &lt;span style="color:#000">mComp&lt;/span>&lt;span style="color:#000;font-weight:bold">;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#000;font-weight:bold">};&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#000">Task&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">::&lt;/span>&lt;span style="color:#000">List&lt;/span> &lt;span style="color:#000">getTasks&lt;/span>&lt;span style="color:#000;font-weight:bold">()&lt;/span> &lt;span style="color:#204a87;font-weight:bold">override&lt;/span> &lt;span style="color:#000;font-weight:bold">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#204a87;font-weight:bold">return&lt;/span> &lt;span style="color:#000;font-weight:bold">{&lt;/span> &lt;span style="color:#000">std&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">::&lt;/span>&lt;span style="color:#000">make_shared&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">&amp;lt;&lt;/span>&lt;span style="color:#000">PreStep&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">&amp;gt;&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">*&lt;/span>&lt;span style="color:#204a87;font-weight:bold">this&lt;/span>&lt;span style="color:#000;font-weight:bold">),&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#000">std&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">::&lt;/span>&lt;span style="color:#000">make_shared&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">&amp;lt;&lt;/span>&lt;span style="color:#000">Step&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">&amp;gt;&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">*&lt;/span>&lt;span style="color:#204a87;font-weight:bold">this&lt;/span>&lt;span style="color:#000;font-weight:bold">)&lt;/span> &lt;span style="color:#000;font-weight:bold">};&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#000;font-weight:bold">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#000;font-weight:bold">};&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;code>PreStep&lt;/code> uses &lt;code>mPrevStepDependencies&lt;/code> for &lt;code>mOutput&lt;/code> because it reads the value produced &lt;em>last&lt;/em> timestep, not the value that &lt;code>Step&lt;/code> will produce &lt;em>this&lt;/em> timestep.
Using &lt;code>mAttributeDependencies&lt;/code> here would create a same-step dependency on &lt;code>Step&lt;/code> and force &lt;code>PreStep&lt;/code> after &lt;code>Step&lt;/code>, which is backwards.&lt;/p>
&lt;h2 id="mna-power-components">MNA power components&lt;/h2>
&lt;p>MNA components inherit from &lt;code>MNASimPowerComp&amp;lt;VarType&amp;gt;&lt;/code>.
Instead of &lt;code>getTasks()&lt;/code>, they implement two hook functions that &lt;code>MNASimPowerComp&lt;/code> calls when it builds the &lt;code>MnaPreStep&lt;/code> and &lt;code>MnaPostStep&lt;/code> tasks during solver initialization.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-cpp" data-lang="cpp">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#204a87;font-weight:bold">void&lt;/span> &lt;span style="color:#000">DP&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">::&lt;/span>&lt;span style="color:#000">Ph1&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">::&lt;/span>&lt;span style="color:#000">MyComponent&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">::&lt;/span>&lt;span style="color:#000">mnaAddPreStepDependencies&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#000">AttributeBase&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">::&lt;/span>&lt;span style="color:#000">List&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">&amp;amp;&lt;/span> &lt;span style="color:#000">prevStepDependencies&lt;/span>&lt;span style="color:#000;font-weight:bold">,&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#000">AttributeBase&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">::&lt;/span>&lt;span style="color:#000">List&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">&amp;amp;&lt;/span> &lt;span style="color:#000">attributeDependencies&lt;/span>&lt;span style="color:#000;font-weight:bold">,&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#000">AttributeBase&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">::&lt;/span>&lt;span style="color:#000">List&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">&amp;amp;&lt;/span> &lt;span style="color:#000">modifiedAttributes&lt;/span>&lt;span style="color:#000;font-weight:bold">)&lt;/span> &lt;span style="color:#000;font-weight:bold">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#000">prevStepDependencies&lt;/span>&lt;span style="color:#000;font-weight:bold">.&lt;/span>&lt;span style="color:#000">push_back&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#000">mIntfCurrent&lt;/span>&lt;span style="color:#000;font-weight:bold">);&lt;/span> &lt;span style="color:#8f5902;font-style:italic">// read from previous step
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#8f5902;font-style:italic">&lt;/span> &lt;span style="color:#000">modifiedAttributes&lt;/span>&lt;span style="color:#000;font-weight:bold">.&lt;/span>&lt;span style="color:#000">push_back&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#000">mRightVector&lt;/span>&lt;span style="color:#000;font-weight:bold">);&lt;/span> &lt;span style="color:#8f5902;font-style:italic">// stamp right-hand side
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#8f5902;font-style:italic">&lt;/span>&lt;span style="color:#000;font-weight:bold">}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#204a87;font-weight:bold">void&lt;/span> &lt;span style="color:#000">DP&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">::&lt;/span>&lt;span style="color:#000">Ph1&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">::&lt;/span>&lt;span style="color:#000">MyComponent&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">::&lt;/span>&lt;span style="color:#000">mnaAddPostStepDependencies&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#000">AttributeBase&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">::&lt;/span>&lt;span style="color:#000">List&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">&amp;amp;&lt;/span> &lt;span style="color:#000">prevStepDependencies&lt;/span>&lt;span style="color:#000;font-weight:bold">,&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#000">AttributeBase&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">::&lt;/span>&lt;span style="color:#000">List&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">&amp;amp;&lt;/span> &lt;span style="color:#000">attributeDependencies&lt;/span>&lt;span style="color:#000;font-weight:bold">,&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#000">AttributeBase&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">::&lt;/span>&lt;span style="color:#000">List&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">&amp;amp;&lt;/span> &lt;span style="color:#000">modifiedAttributes&lt;/span>&lt;span style="color:#000;font-weight:bold">,&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#000">Attribute&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">&amp;lt;&lt;/span>&lt;span style="color:#000">Matrix&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">&amp;gt;::&lt;/span>&lt;span style="color:#000">Ptr&lt;/span>&lt;span style="color:#ce5c00;font-weight:bold">&amp;amp;&lt;/span> &lt;span style="color:#000">leftVector&lt;/span>&lt;span style="color:#000;font-weight:bold">)&lt;/span> &lt;span style="color:#000;font-weight:bold">{&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#000">attributeDependencies&lt;/span>&lt;span style="color:#000;font-weight:bold">.&lt;/span>&lt;span style="color:#000">push_back&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#000">leftVector&lt;/span>&lt;span style="color:#000;font-weight:bold">);&lt;/span> &lt;span style="color:#8f5902;font-style:italic">// wait for matrix solve
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#8f5902;font-style:italic">&lt;/span> &lt;span style="color:#000">modifiedAttributes&lt;/span>&lt;span style="color:#000;font-weight:bold">.&lt;/span>&lt;span style="color:#000">push_back&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#000">mIntfVoltage&lt;/span>&lt;span style="color:#000;font-weight:bold">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#000">modifiedAttributes&lt;/span>&lt;span style="color:#000;font-weight:bold">.&lt;/span>&lt;span style="color:#000">push_back&lt;/span>&lt;span style="color:#000;font-weight:bold">(&lt;/span>&lt;span style="color:#000">mIntfCurrent&lt;/span>&lt;span style="color:#000;font-weight:bold">);&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#000;font-weight:bold">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;code>PostStep&lt;/code> must always list &lt;code>leftVector&lt;/code> in &lt;code>attributeDependencies&lt;/code>.
This creates the edge from the solver&amp;rsquo;s matrix-solve task to every component&amp;rsquo;s &lt;code>PostStep&lt;/code>, ensuring the solution vector is available before voltages and currents are extracted.&lt;/p>
&lt;h2 id="dependency-declaration-checklist">Dependency declaration checklist&lt;/h2>
&lt;ul>
&lt;li>Every attribute &lt;em>read&lt;/em> inside &lt;code>execute()&lt;/code> must appear in &lt;code>mAttributeDependencies&lt;/code> or &lt;code>mPrevStepDependencies&lt;/code>.&lt;/li>
&lt;li>Every attribute &lt;em>written&lt;/em> inside &lt;code>execute()&lt;/code> must appear in &lt;code>mModifiedAttributes&lt;/code>.&lt;/li>
&lt;li>State carried from the previous timestep goes in &lt;code>mPrevStepDependencies&lt;/code>, not &lt;code>mAttributeDependencies&lt;/code>.&lt;/li>
&lt;li>&lt;code>MnaPostStep&lt;/code> must list &lt;code>leftVector&lt;/code> in &lt;code>attributeDependencies&lt;/code>.&lt;/li>
&lt;li>No attribute should appear in both &lt;code>mAttributeDependencies&lt;/code> and &lt;code>mPrevStepDependencies&lt;/code> for the same task.&lt;/li>
&lt;/ul>
&lt;div class="alert alert-warning" role="alert">
&lt;h4 class="alert-heading">Watch out: a missing declaration produces wrong results, not a crash&lt;/h4>
&lt;p>Missing a declaration does not always cause a crash; it silently produces incorrect results or a wrong execution order, which is harder to debug.
Two common failure modes follow from the pruning step:&lt;/p>
&lt;ul>
&lt;li>A &lt;code>PreStep&lt;/code> or &lt;code>PostStep&lt;/code> task is dropped entirely because none of its declared modified attributes is needed by another task, a logger, an interface, or a previous-step dependency. The simulation then runs but its results are always wrong.&lt;/li>
&lt;li>The same task appears to work only when a particular variable is logged or exchanged by an interface, because that logger or interface adds a dependency on the attribute and keeps the producing task reachable. The results then depend on logger or interface configuration even though the physical model did not change.&lt;/li>
&lt;/ul>
&lt;p>Declare dependencies conservatively.&lt;/p>
&lt;/div></description></item><item><title>Docs: Modal Analysis</title><link>https://sogno.energy/dpsim/docs/concepts/modal-analysis/</link><pubDate>Fri, 31 Jul 2026 00:00:00 +0000</pubDate><guid>https://sogno.energy/dpsim/docs/concepts/modal-analysis/</guid><description>
&lt;p>Extracting a state-space model, as described under
&lt;a href="https://sogno.energy/dpsim/docs/concepts/state-space-extraction-theory/">state-space extraction&lt;/a>, produces a discrete state
matrix. Its eigenvalues describe how the system behaves without simulating it: which oscillations
exist, how fast each decays, and which states are involved in each one.&lt;/p>
&lt;h2 id="from-discrete-to-continuous-eigenvalues">From discrete to continuous eigenvalues&lt;/h2>
&lt;p>The extracted model is discrete, so its eigenvalues $z$ live in the complex plane where stability
means $|z| &amp;lt; 1$. That is awkward to read, because the quantities of interest are a frequency in
hertz and a damping ratio, both of which are natural in the continuous plane.&lt;/p>
&lt;p>Because the model was discretised with the trapezoidal rule, the mapping back is its inverse, the
bilinear transform&lt;/p>
&lt;div class="math">$$\lambda = \frac{2}{\Delta t} \, \frac{z - 1}{z + 1}.$$&lt;/div>&lt;p>This maps the interior of the unit disc onto the left half plane exactly, so a mode that is stable
in one description is stable in the other, with no threshold effects at the boundary. From
$\lambda = \sigma + j\omega$ the damped frequency is $\omega / 2\pi$ and the damping ratio is
$-\sigma / |\lambda|$.&lt;/p>
&lt;p>The mapping is exact for the discretisation used, not an approximation of it. What it cannot undo is
the frequency warping the trapezoidal rule introduced in the first place: a continuous mode at a
frequency approaching the Nyquist rate is represented at a shifted frequency in the discrete model,
and mapping back returns the shifted value rather than the original. Modes well below Nyquist are
unaffected; modes near it should not be read literally.&lt;/p>
&lt;h2 id="what-the-eigenvectors-say">What the eigenvectors say&lt;/h2>
&lt;p>The eigenvalues say which modes exist but not which parts of the system take part in them. The right
eigenvectors describe how each mode appears in the states, the left eigenvectors describe how
strongly each state excites each mode, and the product of the two, element by element,&lt;/p>
&lt;div class="math">$$p_{ki} = \phi_{ki} \, \psi_{ik},$$&lt;/div>&lt;p>is the participation factor of state $k$ in mode $i$.&lt;/p>
&lt;p>Participation factors are the useful output. A poorly damped oscillation is a number; knowing that
two particular machine rotor states dominate it is actionable. They are also dimensionless and
normalised in a way that makes them comparable across states with different units, which raw
eigenvector entries are not.&lt;/p>
&lt;p>The computation requires the eigenvector matrix to be invertible. It is not, when the state matrix
is defective, meaning it has a repeated eigenvalue without a full set of independent eigenvectors.
This is not a numerical failure but a property of the system, and it is the one case where
participation factors are not defined at all.&lt;/p>
&lt;h2 id="choice-of-frame">Choice of frame&lt;/h2>
&lt;p>The states of the extracted model are in whatever frame each component works in, which for a network
containing machines means several rotating frames turning at different speeds plus the network&amp;rsquo;s own.
Eigenvalues of that system are still correct, but the modes mix frames, and a mode&amp;rsquo;s frequency is
then relative to whichever frame its dominant states live in.&lt;/p>
&lt;p>Transforming everything into one common frame before the analysis removes that ambiguity, at the
price of choosing the frame and its initial angle. The two choices are therefore: analyse in the
native frames and read each mode relative to its own states, or transform to a single frame and read
every frequency against the same reference. The second is what makes modes from different machines
directly comparable.&lt;/p>
&lt;h2 id="limits">Limits&lt;/h2>
&lt;p>The analysis is linear and local. It describes the system as it is at the operating point where the
model was extracted, and says nothing about behaviour after a large disturbance moves it elsewhere.
A system can be comfortably damped at its nominal point and not at another, so a single modal
analysis is evidence about one condition rather than about the system.&lt;/p></description></item><item><title>Docs: Sources</title><link>https://sogno.energy/dpsim/docs/concepts/models/sources/</link><pubDate>Fri, 31 Jul 2026 00:00:00 +0000</pubDate><guid>https://sogno.energy/dpsim/docs/concepts/models/sources/</guid><description>
&lt;p>A source imposes a quantity on the network. Which quantity it imposes, and whether it does so
exactly, determines how it enters the system of equations and what it costs.&lt;/p>
&lt;h2 id="current-sources-are-free-voltage-sources-are-not">Current sources are free, voltage sources are not&lt;/h2>
&lt;p>A current source imposes a known current into a node. Its contribution is entirely on the right hand
side of the nodal equations, and the system matrix does not know it exists.&lt;/p>
&lt;p>A voltage source imposes a relation between two node voltages, which is not a nodal equation at all.
Nodal analysis has one equation per node expressing current balance, and there is no current
variable for an ideal voltage source to appear in. The system is extended with the source current as
an unknown and with the constraint that fixes the voltage difference, as described under
&lt;a href="https://sogno.energy/dpsim/docs/concepts/nodal-analysis/">nodal analysis&lt;/a>. The matrix grows by one row and
column per source, and the added diagonal entry is zero, so the extended matrix is no longer
positive definite and cannot be factorised by methods that assume it is.&lt;/p>
&lt;p>This asymmetry is the reason so many models are formulated as current injections even when what they
physically represent is a voltage behind an impedance.&lt;/p>
&lt;h2 id="the-norton-equivalent">The Norton equivalent&lt;/h2>
&lt;p>A voltage source with a series resistance can avoid the extension entirely. Source transformation
replaces a voltage $V$ behind a resistance $R$ with a current $V/R$ in parallel with the same
resistance,&lt;/p>
&lt;div class="math">$$I_{eq} = \frac{V}{R}, \qquad G = \frac{1}{R},$$&lt;/div>&lt;p>which contributes a conductance to the matrix and a current to the right hand side. No extra
unknown, no zero on the diagonal, and the matrix stays the shape it would have had without the
source.&lt;/p>
&lt;p>The two representations are equivalent at the terminals, exactly, for any $R$ that is not zero. The
choice is therefore numerical rather than physical, and the cost is that the source is no longer
ideal: its terminal voltage falls with the current drawn. Where a genuinely stiff source is wanted,
$R$ has to be made small, and a small $R$ means a large conductance, which is the same conditioning
trade-off that appears in &lt;a href="https://sogno.energy/dpsim/docs/concepts/models/switches/">switches&lt;/a>.&lt;/p>
&lt;h2 id="sources-that-change-over-time">Sources that change over time&lt;/h2>
&lt;p>The simplest time-varying source takes its value from a generator, as described under
&lt;a href="https://sogno.energy/dpsim/docs/concepts/models/signal-processing/">signal processing blocks&lt;/a>.&lt;/p>
&lt;p>A ramp source is more specific: it holds one value, then moves to a second over a defined interval,
and holds that. The subtlety is what happens when the ramp changes not only the magnitude and phase
but also the frequency. Interpolating a frequency linearly and applying it as if it had always been
in force produces a phase discontinuity at both ends of the ramp, because phase is the integral of
frequency and not its product with time. Blending the frequency contribution in and out smoothly
over the ramp interval avoids that, at the price that the frequency during the transition is not the
linear interpolation it appears to be.&lt;/p>
&lt;p>A profile source takes its value from a recorded sequence instead of from a formula, stepping
through samples as the simulation advances. It is the right choice when the excitation comes from a
measurement, and it carries the obvious constraint that the sample rate and the simulation step must
be reconciled: a profile is silent about what happens between its samples, and the simulation will
ask.&lt;/p>
&lt;h2 id="controlled-sources">Controlled sources&lt;/h2>
&lt;p>A controlled source takes its reference from another quantity in the simulation rather than from a
parameter or a clock. This is what allows a component to be built out of sources: a converter
imposes a voltage its control law computed, and an interface between two solvers imposes a value the
other side produced.&lt;/p>
&lt;p>The distinction from a time-varying source is that the reference is not known in advance. Since the
reference is read as an input rather than solved simultaneously, it is the value from the previous
step, which introduces a delay of one step into whatever loop the source closes. For a control loop
that is usually acceptable and always worth knowing about; for a coupling between two solvers it is
the central property of the method, and it is the subject of
&lt;a href="https://sogno.energy/dpsim/docs/concepts/models/branches/">branches&lt;/a> where the same delay is used deliberately.&lt;/p></description></item><item><title>Docs: Component and Solver Initialization</title><link>https://sogno.energy/dpsim/docs/developer-guide/writing-a-model/initialization/</link><pubDate>Thu, 18 Jun 2026 00:00:00 +0000</pubDate><guid>https://sogno.energy/dpsim/docs/developer-guide/writing-a-model/initialization/</guid><description>
&lt;p>Initialization is the phase between constructing the system topology and running the first timestep.
Its job is to size the system matrices, derive initial state from power-flow results, register MNA tasks, and stamp static conductances.
Two constraints drive its structure:&lt;/p>
&lt;ul>
&lt;li>The system matrix size depends on the total number of simulation nodes, including &lt;strong>virtual nodes&lt;/strong> declared by composite components and their sub-components. All virtual nodes must therefore be known &lt;em>before&lt;/em> the matrices are allocated.&lt;/li>
&lt;li>Component parameter values (impedances, initial phasors) depend on terminal voltages and powers, which are only available &lt;em>after&lt;/em> a power-flow solve.&lt;/li>
&lt;/ul>
&lt;p>These two constraints impose an ordering that is captured in the solver&amp;rsquo;s initialization sequence.&lt;/p>
&lt;hr>
&lt;h2 id="mna-solver-initialization-sequence">MNA Solver Initialization Sequence&lt;/h2>
&lt;p>&lt;code>MnaSolver::initialize()&lt;/code> executes the following steps in order.&lt;/p>
&lt;div class="mermaid">
flowchart TD
start([Simulation::run]) --> init[MnaSolver::initialize]
init --> s1["S1: identifyTopologyObjects()\nSort into mMNAComponents,\nmSimSignalComps, ..."]
s1 --> s2["S2: createSubComponents() pre-pass\nRecursively instantiate sub-components\nso all virtual nodes exist"]
s2 --> s3["S3: collectVirtualNodes()\nassignMatrixNodeIndices()\nMatrix size is now fixed"]
s3 --> s4["S4: createEmptyVectors()\ncreateEmptySystemMatrix()"]
s4 --> s5a["S5a: initializeFromNodesAndTerminals(freq)\nfor each SimPowerComp"]
s5a --> s5b["S5b: initialize(omega, dt)\nfor each SimSignalComp"]
s5b --> s5c["S5c: mnaInitialize(omega, dt, v)\nfor each MNAInterface component"]
s5c --> cond{mSteadyStateInit?}
cond -- yes --> s6["S6: steadyStateInitialization()\nIterate MNA until phasors converge"]
s6 --> s7
cond -- no --> s7["S7: setBehaviour(MNASimulation)\non all components"]
s7 --> s8["S8: initializeSystem()\nStamp static elements,\ncompute LU factorizations"]
s8 --> done([Ready for timesteps])
&lt;/div>
&lt;h3 id="step-1--identify-topology-objects">Step 1 — Identify topology objects&lt;/h3>
&lt;p>&lt;code>identifyTopologyObjects()&lt;/code> iterates over &lt;code>SystemTopology::mComponents&lt;/code> and sorts each component into one of four lists:&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>List&lt;/th>
&lt;th>Contents&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;code>mMNAComponents&lt;/code>&lt;/td>
&lt;td>Static MNA power components&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>mMNAIntfVariableComps&lt;/code>&lt;/td>
&lt;td>Variable-stamp MNA components (e.g. under &lt;code>MNAVariableCompInterface&lt;/code>)&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>mMNAIntfSwitches&lt;/code>&lt;/td>
&lt;td>Components with a switch interface&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>mSimSignalComps&lt;/code>&lt;/td>
&lt;td>Signal components (&lt;code>SimSignalComp&lt;/code>)&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>Ground nodes are excluded here.&lt;/p>
&lt;h3 id="step-2--create-sub-components-pre-pass">Step 2 — Create sub-components (pre-pass)&lt;/h3>
&lt;p>Before the matrix can be sized, every composite component&amp;rsquo;s sub-component tree must be fully instantiated so that all virtual nodes are visible.
The solver calls &lt;code>createSubComponents()&lt;/code> recursively on every MNA component:&lt;/p>
&lt;ul>
&lt;li>Only sub-components &lt;em>newly registered&lt;/em> by this call are recursed into, because eagerly-constructed sub-components (created in the constructor before &lt;code>connect()&lt;/code> has run) are not yet safe to recurse into.&lt;/li>
&lt;li>This step is a pre-pass only — it must not set parameter values derived from terminal data or frequency.&lt;/li>
&lt;/ul>
&lt;p>For details on the three-stage composite lifecycle (&lt;code>createSubComponents&lt;/code>, &lt;code>initializeParentFromNodesAndTerminals&lt;/code>, &lt;code>mnaCompInitialize&lt;/code>), see &lt;a href="https://sogno.energy/dpsim/docs/developer-guide/writing-a-model/subcomponents/">Subcomponent Handling&lt;/a>.&lt;/p>
&lt;h3 id="step-3--collect-virtual-nodes-and-assign-indices">Step 3 — Collect virtual nodes and assign indices&lt;/h3>
&lt;p>&lt;code>collectVirtualNodes()&lt;/code> visits every component and calls &lt;code>virtualNodes()&lt;/code> to collect all virtual &lt;code>SimNode&lt;/code> objects, then appends them to the solver&amp;rsquo;s node list.
&lt;code>assignMatrixNodeIndices()&lt;/code> then assigns a contiguous integer index to every simulation node (real and virtual), which determines the row/column layout of the system matrices.&lt;/p>
&lt;p>After this step the matrix size is fixed.&lt;/p>
&lt;h3 id="step-4--allocate-empty-matrices">Step 4 — Allocate empty matrices&lt;/h3>
&lt;p>&lt;code>createEmptyVectors()&lt;/code> and &lt;code>createEmptySystemMatrix()&lt;/code> allocate the left-side vector, right-side vector, system matrix (dense or sparse depending on the solver variant), and switch-variant copies.
For sparse solvers, &lt;code>mBaseSystemMatrix&lt;/code> and &lt;code>mLuFactorizations&lt;/code> are also allocated here, with one variant per switch combination.&lt;/p>
&lt;h3 id="step-5--initialize-components-initializecomponents">Step 5 — Initialize components (&lt;code>initializeComponents&lt;/code>)&lt;/h3>
&lt;p>This step has three sub-passes over the component lists.&lt;/p>
&lt;h4 id="5a--power-components-initializefromnodesandterminals">5a — Power components: &lt;code>initializeFromNodesAndTerminals&lt;/code>&lt;/h4>
&lt;p>For every &lt;code>SimPowerComp&amp;lt;VarType&amp;gt;&lt;/code> in &lt;code>mMNAComponents&lt;/code> and &lt;code>mMNAIntfVariableComps&lt;/code>:&lt;/p>
&lt;ol>
&lt;li>&lt;code>checkForUnconnectedTerminals()&lt;/code> validates connectivity.&lt;/li>
&lt;li>If &lt;code>mInitFromNodesAndTerminals&lt;/code> is set (the default), &lt;code>initializeFromNodesAndTerminals(mSystem.mSystemFrequency)&lt;/code> is called.&lt;/li>
&lt;/ol>
&lt;p>This is where components read their terminal voltages and powers and derive physical parameters (impedances, initial phasor values, per-unit quantities).
For composite components &lt;code>initializeFromNodesAndTerminals()&lt;/code> is &lt;code>final&lt;/code> in &lt;code>CompositePowerComp&lt;/code> and sequences the three lifecycle stages automatically; non-composite power components override it directly.&lt;/p>
&lt;h4 id="5b--signal-components-initializeomega-timestep">5b — Signal components: &lt;code>initialize(omega, timeStep)&lt;/code>&lt;/h4>
&lt;p>Each &lt;code>SimSignalComp&lt;/code> in &lt;code>mSimSignalComps&lt;/code> receives &lt;code>initialize(mSystem.mSystemOmega, mTimeStep)&lt;/code>.
This is the hook for signal-domain components (regulators, governors, PSS blocks) to allocate their state buffers, set initial values, and wire up attribute connections.&lt;/p>
&lt;div class="alert alert-warning" role="alert">
&lt;h4 class="alert-heading">Watch out: do not name a hook initialize(Real)&lt;/h4>
Do not use &lt;code>initialize(Real)&lt;/code> or &lt;code>initialize(Real, Real)&lt;/code> as a user-facing initialization hook
for power components. Those signatures match the solver&amp;rsquo;s signal-component hook, so the solver
calls them rather than the component author&amp;rsquo;s intent. Use &lt;code>initializeFromNodesAndTerminals()&lt;/code>
or a named method such as &lt;code>initializeStates()&lt;/code> instead.
&lt;/div>
&lt;h4 id="5c--mna-components-mnainitialize">5c — MNA components: &lt;code>mnaInitialize&lt;/code>&lt;/h4>
&lt;p>Each MNA component (including switches) receives &lt;code>mnaInitialize(omega, timeStep, leftVector)&lt;/code>.
In &lt;code>MNASimPowerComp&lt;/code> this method:&lt;/p>
&lt;ol>
&lt;li>Clears and re-registers &lt;code>MNAPreStep&lt;/code> / &lt;code>MNAPostStep&lt;/code> tasks according to the &lt;code>hasPreStep&lt;/code> / &lt;code>hasPostStep&lt;/code> flags.&lt;/li>
&lt;li>Initializes &lt;code>mRightVector&lt;/code> to zero with the correct size.&lt;/li>
&lt;li>Calls &lt;code>mnaCompInitialize(omega, timeStep, leftVector)&lt;/code> on the component.&lt;/li>
&lt;/ol>
&lt;p>In &lt;code>mnaCompInitialize&lt;/code>, component classes call &lt;code>updateMatrixNodeIndices()&lt;/code> and perform any one-time MNA setup that requires the final node layout (e.g. allocating per-component history vectors sized to the system).&lt;/p>
&lt;p>Nodes are initialized last via &lt;code>SimNode::initialize()&lt;/code>, which zeros the node voltage.&lt;/p>
&lt;h3 id="step-6--optional-steady-state-initialization">Step 6 — Optional steady-state initialization&lt;/h3>
&lt;p>If &lt;code>mSteadyStateInit&lt;/code> is set, &lt;code>steadyStateInitialization()&lt;/code> iterates the MNA solve until the phasor solution converges.
The flag &lt;code>mIsInInitialization&lt;/code> is set to &lt;code>true&lt;/code> for this sub-phase so that components can distinguish initialization solves from simulation solves via &lt;code>mBehaviour&lt;/code> (see below).&lt;/p>
&lt;h3 id="step-7--set-simulation-behaviour">Step 7 — Set simulation behaviour&lt;/h3>
&lt;p>After initialization solves are complete, the solver calls &lt;code>setBehaviour(TopologicalPowerComp::Behaviour::MNASimulation)&lt;/code> on every &lt;code>TopologicalPowerComp&lt;/code> and &lt;code>setBehaviour(SimSignalComp::Behaviour::Simulation)&lt;/code> on every &lt;code>SimSignalComp&lt;/code>.&lt;/p>
&lt;p>The &lt;code>Behaviour&lt;/code> enum (defined in &lt;code>TopologicalPowerComp&lt;/code>) has three values:&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Value&lt;/th>
&lt;th>When active&lt;/th>
&lt;th>Typical use&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>&lt;code>Behaviour::Initialization&lt;/code>&lt;/td>
&lt;td>During PF steady-state init pass&lt;/td>
&lt;td>Components may disable transient update equations&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>Behaviour::PFSimulation&lt;/code>&lt;/td>
&lt;td>During PFSolver run&lt;/td>
&lt;td>Activates power-flow-specific stamping&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>Behaviour::MNASimulation&lt;/code>&lt;/td>
&lt;td>After &lt;code>initialize()&lt;/code> completes&lt;/td>
&lt;td>Normal simulation; components should be in their run-time mode&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>Components that need different behaviour between initialization and simulation check &lt;code>mBehaviour&lt;/code> in their pre/post-step methods or in &lt;code>mnaCompPreStep&lt;/code>.&lt;/p>
&lt;h3 id="step-8--initialize-system-matrices-initializesystem">Step 8 — Initialize system matrices (&lt;code>initializeSystem&lt;/code>)&lt;/h3>
&lt;p>&lt;code>initializeSystem()&lt;/code> selects one of three paths:&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Parallel frequencies&lt;/strong> (&lt;code>initializeSystemWithParallelFrequencies&lt;/code>): stamps each frequency into a separate thread.&lt;/li>
&lt;li>&lt;strong>Variable matrix&lt;/strong> (&lt;code>initializeSystemWithVariableMatrix&lt;/code>): used by &lt;code>MnaSolverSysRecomp&lt;/code>; saves static switch matrices as base matrices and adds variable elements on top.&lt;/li>
&lt;li>&lt;strong>Precomputed matrices&lt;/strong> (&lt;code>initializeSystemWithPrecomputedMatrices&lt;/code>): the common path. Calls &lt;code>switchedMatrixStamp()&lt;/code> for each switch combination, which iterates over all static MNA components and calls &lt;code>mnaApplySystemMatrixStamp()&lt;/code> and &lt;code>mnaApplyRightSideVectorStamp()&lt;/code>. LU factorizations are computed for each variant.&lt;/li>
&lt;/ul>
&lt;p>After this step the solver is ready to execute timesteps.&lt;/p>
&lt;hr>
&lt;h2 id="component-class-hierarchy-and-init-hooks">Component Class Hierarchy and Init Hooks&lt;/h2>
&lt;p>The following diagram shows which initialization methods live in which class, and the override points for component authors.&lt;/p>
&lt;div class="mermaid">
classDiagram
class TopologicalPowerComp {
+Behaviour mBehaviour
+setBehaviour(b)
}
class SimPowerComp~T~ {
+initialize(Matrix frequencies)
+initializeFromNodesAndTerminals(Real freq)
+virtualNodes()
}
class MNASimPowerComp~T~ {
+mnaInitialize(omega, dt, v) final
+mnaCompInitialize(omega, dt, v)*
+mnaCompApplySystemMatrixStamp()*
+mnaCompPreStep()*
+mnaCompPostStep()*
}
class CompositePowerComp~T~ {
+createSubComponents()*
+initializeFromNodesAndTerminals(freq) final
+initializeParentFromNodesAndTerminals(freq)*
+mnaParentInitialize(omega, dt, v)*
+mnaParentPreStep()*
+mnaParentPostStep()*
}
class SimSignalComp {
+initialize(Real omega, Real dt)*
}
TopologicalPowerComp &lt;|-- SimPowerComp
SimPowerComp &lt;|-- MNASimPowerComp
MNASimPowerComp &lt;|-- CompositePowerComp
&lt;/div>
&lt;p>Methods marked &lt;code>*&lt;/code> are the virtual override points for component authors.
Methods marked &lt;code>final&lt;/code> must not be overridden; the base class sequences them correctly.&lt;/p>
&lt;hr>
&lt;h2 id="component-method-contracts">Component Method Contracts&lt;/h2>
&lt;p>The table below summarizes which initialization method has which responsibilities. A tick means the operation &lt;em>belongs&lt;/em> in that method; a cross means it must not appear there.&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Responsibility&lt;/th>
&lt;th style="text-align:center">Constructor / &lt;code>setParameters&lt;/code>&lt;/th>
&lt;th style="text-align:center">&lt;code>createSubComponents&lt;/code>&lt;/th>
&lt;th style="text-align:center">&lt;code>initializeFromNodesAndTerminals&lt;/code>&lt;/th>
&lt;th style="text-align:center">&lt;code>mnaCompInitialize&lt;/code>&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>Declare virtual node count&lt;/td>
&lt;td style="text-align:center">✓&lt;/td>
&lt;td style="text-align:center">—&lt;/td>
&lt;td style="text-align:center">—&lt;/td>
&lt;td style="text-align:center">—&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Allocate sub-component objects&lt;/td>
&lt;td style="text-align:center">—&lt;/td>
&lt;td style="text-align:center">✓&lt;/td>
&lt;td style="text-align:center">—&lt;/td>
&lt;td style="text-align:center">—&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>connect()&lt;/code> sub-components to virtual nodes&lt;/td>
&lt;td style="text-align:center">—&lt;/td>
&lt;td style="text-align:center">✓&lt;/td>
&lt;td style="text-align:center">—&lt;/td>
&lt;td style="text-align:center">—&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;code>addMNASubComponent()&lt;/code> registration&lt;/td>
&lt;td style="text-align:center">—&lt;/td>
&lt;td style="text-align:center">✓&lt;/td>
&lt;td style="text-align:center">—&lt;/td>
&lt;td style="text-align:center">—&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Read terminal voltage / power&lt;/td>
&lt;td style="text-align:center">✗&lt;/td>
&lt;td style="text-align:center">✗&lt;/td>
&lt;td style="text-align:center">✓&lt;/td>
&lt;td style="text-align:center">—&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Read system frequency&lt;/td>
&lt;td style="text-align:center">✗&lt;/td>
&lt;td style="text-align:center">✗&lt;/td>
&lt;td style="text-align:center">✓ (via argument)&lt;/td>
&lt;td style="text-align:center">✓ (via &lt;code>omega&lt;/code>)&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Compute impedance / admittance&lt;/td>
&lt;td style="text-align:center">—&lt;/td>
&lt;td style="text-align:center">✗&lt;/td>
&lt;td style="text-align:center">✓&lt;/td>
&lt;td style="text-align:center">—&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Call &lt;code>setParameters()&lt;/code> on sub-components&lt;/td>
&lt;td style="text-align:center">—&lt;/td>
&lt;td style="text-align:center">—&lt;/td>
&lt;td style="text-align:center">✓&lt;/td>
&lt;td style="text-align:center">—&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Call &lt;code>updateMatrixNodeIndices()&lt;/code>&lt;/td>
&lt;td style="text-align:center">—&lt;/td>
&lt;td style="text-align:center">—&lt;/td>
&lt;td style="text-align:center">—&lt;/td>
&lt;td style="text-align:center">✓&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Allocate per-step vectors (history, right vector)&lt;/td>
&lt;td style="text-align:center">—&lt;/td>
&lt;td style="text-align:center">—&lt;/td>
&lt;td style="text-align:center">—&lt;/td>
&lt;td style="text-align:center">✓&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Register MNA tasks (handled by base class)&lt;/td>
&lt;td style="text-align:center">—&lt;/td>
&lt;td style="text-align:center">—&lt;/td>
&lt;td style="text-align:center">—&lt;/td>
&lt;td style="text-align:center">✓ (via &lt;code>mnaCompInitialize&lt;/code>)&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;h3 id="common-pitfalls">Common pitfalls&lt;/h3>
&lt;ul>
&lt;li>&lt;strong>Accessing terminals in the constructor or &lt;code>createSubComponents&lt;/code>&lt;/strong>: terminal data (initial voltage, connected power) is not yet populated. The topology is set up but power-flow has not run.&lt;/li>
&lt;li>&lt;strong>Accessing &lt;code>mFrequencies(0,0)&lt;/code> in &lt;code>createSubComponents&lt;/code>&lt;/strong>: the system frequency matrix is set on &lt;code>SimPowerComp&lt;/code> via &lt;code>initialize(Matrix)&lt;/code> which only runs later. Use the &lt;code>frequency&lt;/code> argument passed to &lt;code>initializeParentFromNodesAndTerminals&lt;/code> or the &lt;code>omega&lt;/code> argument in &lt;code>mnaCompInitialize&lt;/code>.&lt;/li>
&lt;li>&lt;strong>Zero-valued shunt branches&lt;/strong>: a capacitor or reactor with zero admittance injects a zero row/column into the system matrix, which makes the LU factorization singular. Guard with a strict &lt;code>&amp;gt; 0&lt;/code> check and omit the branch rather than inserting a zero stamp.&lt;/li>
&lt;/ul>
&lt;div class="alert alert-warning" role="alert">
&lt;h4 class="alert-heading">Watch out: virtual nodes must exist before the solver collects them&lt;/h4>
A virtual node created for the first time &lt;em>after&lt;/em> &lt;code>collectVirtualNodes()&lt;/code> (step 3) never gets a
matrix index, and the solver then crashes or silently produces wrong results. Declare every virtual
node in the constructor or in &lt;code>setParameters()&lt;/code>.
&lt;/div>
&lt;hr>
&lt;h2 id="composite-component-initialization-sequence">Composite Component Initialization Sequence&lt;/h2>
&lt;p>The following diagram shows how the solver and a composite component interact during initialization. For further details see &lt;a href="https://sogno.energy/dpsim/docs/developer-guide/writing-a-model/subcomponents/">Subcomponent Handling&lt;/a>.&lt;/p>
&lt;div class="mermaid">
sequenceDiagram
participant MNA as MnaSolver
participant CC as CompositePowerComp
participant SC as SubComponent
Note over MNA,SC: Step 2 - pre-pass (topology only)
MNA->>CC: createSubComponents()
CC->>SC: make_shared + connect() + addMNASubComponent()
Note over MNA,SC: Step 3 - matrix sizing
MNA->>CC: collectVirtualNodes()
MNA->>MNA: assignMatrixNodeIndices()
Note over MNA,SC: Step 5a - parameterization
MNA->>CC: initializeFromNodesAndTerminals(freq)
CC->>CC: createSubComponents() idempotent guard
CC->>CC: initializeParentFromNodesAndTerminals(freq)
CC->>SC: initialize(frequencies)
CC->>SC: initializeFromNodesAndTerminals(freq)
Note over MNA,SC: Step 5c - MNA setup
MNA->>CC: mnaInitialize(omega, dt, v)
CC->>SC: mnaInitialize(omega, dt, v)
CC->>CC: mnaParentInitialize(omega, dt, v)
&lt;/div>
&lt;hr>
&lt;h2 id="pfsolver-initialization">PFSolver Initialization&lt;/h2>
&lt;p>&lt;code>PFSolver::initialize()&lt;/code> follows a simpler sequence because it operates only on single-phase SP components with no sub-component tree and does not need a &lt;code>createSubComponents&lt;/code> pre-pass.&lt;/p>
&lt;div class="mermaid">
flowchart TD
pf[PFSolver::initialize] --> p1[Classify components\ninto generator/load/line/... lists]
p1 --> p2[setBaseApparentPower\nCompute per-unit base]
p2 --> p3[assignMatrixNodeIndices]
p3 --> p4[initializeComponents\ninitializeFromNodesAndTerminals\ncalculatePerUnitParameters]
p4 --> p5[determinePFBusType\nPQ / PV / VD]
p5 --> p6[determineNodeBaseVoltages]
p6 --> p7[composeAdmittanceMatrix\nBuild Y-bus]
p7 --> done([Ready to solve power flow])
&lt;/div>
&lt;p>&lt;code>PFSolver::setSolverAndComponentBehaviour()&lt;/code> is the equivalent of Step 7 for the MNA solver: it calls &lt;code>setBehaviour(Behaviour::PFSimulation)&lt;/code> or &lt;code>setBehaviour(Behaviour::Initialization)&lt;/code> on all components to allow them to switch stamping modes.&lt;/p>
&lt;hr>
&lt;h2 id="known-design-issues-issue-59">Known Design Issues (issue #59)&lt;/h2>
&lt;p>The following areas were identified in &lt;a href="https://github.com/sogno-platform/dpsim/issues/59">GitHub issue #59&lt;/a> as needing improvement.&lt;/p>
&lt;h3 id="simpowercompinitializematrix-frequencies-naming-clash">&lt;code>SimPowerComp::initialize(Matrix frequencies)&lt;/code> naming clash&lt;/h3>
&lt;p>&lt;code>SimPowerComp&amp;lt;T&amp;gt;::initialize(Matrix frequencies)&lt;/code> is called by the solver to propagate frequency information down the component tree.
&lt;div class="alert alert-warning" role="alert">
&lt;h4 class="alert-heading">Watch out: overriding this hook makes you responsible for the base call&lt;/h4>
It is &lt;em>not&lt;/em> a hook for component authors — a component that overrides it takes over responsibility for calling the base class version, which is easy to forget.
&lt;/div>
The recommended path is:&lt;/p>
&lt;ul>
&lt;li>For power components, use &lt;code>initializeFromNodesAndTerminals()&lt;/code> or &lt;code>initializeParentFromNodesAndTerminals()&lt;/code>.&lt;/li>
&lt;li>For signal components, use the &lt;code>initialize(Real omega, Real timeStep)&lt;/code> hook provided by &lt;code>SimSignalComp&lt;/code>.&lt;/li>
&lt;li>For anything else (e.g. setting up state-space matrices), add a named helper called from one of the above.&lt;/li>
&lt;/ul>
&lt;p>The base implementation of &lt;code>SimPowerComp::initialize(Matrix)&lt;/code> should be renamed to something that cannot be accidentally overridden (e.g. &lt;code>propagateFrequencies()&lt;/code>), and an &lt;code>override&lt;/code> guard should be added to catch accidental overrides.&lt;/p>
&lt;h3 id="sub-component-construction-in-constructors">Sub-component construction in constructors&lt;/h3>
&lt;p>Some components create and register sub-components eagerly in their constructor before &lt;code>connect()&lt;/code> has been called on those sub-components.
This works today because the solver&amp;rsquo;s &lt;code>createSubComponents&lt;/code> pre-pass skips already-registered sub-components, but it couples topology creation to object construction and makes components harder to reason about.
The long-term goal is to migrate all sub-component construction to &lt;code>createSubComponents()&lt;/code>, giving a clear rule: the constructor only allocates and the topology stage wires.&lt;/p>
&lt;h3 id="signal-component-initialize-not-sequenced-with-power-flow">Signal component &lt;code>initialize&lt;/code> not sequenced with power flow&lt;/h3>
&lt;p>Signal components receive &lt;code>initialize(omega, timeStep)&lt;/code> &lt;em>after&lt;/em> &lt;code>initializeFromNodesAndTerminals&lt;/code> on power components but &lt;em>before&lt;/em> the MNA tasks are registered.
If a signal component&amp;rsquo;s initial state depends on the power-flow solution (e.g. an exciter initializing to match the generator terminal voltage), it must read the relevant attribute values directly — there is no formal mechanism today to express this dependency in the initialization sequence.
A future improvement would be to give signal components access to the settled power-flow solution before their &lt;code>initialize&lt;/code> is called.&lt;/p></description></item><item><title>Docs: How to Cite</title><link>https://sogno.energy/dpsim/docs/citation/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://sogno.energy/dpsim/docs/citation/</guid><description>
&lt;p>If you use DPsim in your research, please cite the software paper below. If your work depends on
a specific capability, cite the corresponding paper from
&lt;a href="#further-publications">further publications&lt;/a> as well.&lt;/p>
&lt;h2 id="software-paper">Software paper&lt;/h2>
&lt;p>M. Mirz, S. Vogel, G. Reinke and A. Monti, &amp;ldquo;DPsim: A dynamic phasor real-time simulator for
power systems&amp;rdquo;, &lt;em>SoftwareX&lt;/em>, vol. 10, 100253, 2019.
&lt;a href="https://doi.org/10.1016/j.softx.2019.100253">https://doi.org/10.1016/j.softx.2019.100253&lt;/a>&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-bibtex" data-lang="bibtex">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#000">@article&lt;/span>&lt;span style="color:#000;font-weight:bold">{&lt;/span>&lt;span style="color:#f57900">mirz2019dpsim&lt;/span>&lt;span style="color:#000;font-weight:bold">,&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#c4a000">title&lt;/span> &lt;span style="color:#000;font-weight:bold">=&lt;/span> &lt;span style="color:#4e9a06">{DPsim: A dynamic phasor real-time simulator for power systems}&lt;/span>&lt;span style="color:#000;font-weight:bold">,&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#c4a000">author&lt;/span> &lt;span style="color:#000;font-weight:bold">=&lt;/span> &lt;span style="color:#4e9a06">{Mirz, Markus and Vogel, Steffen and Reinke, Georg and Monti, Antonello}&lt;/span>&lt;span style="color:#000;font-weight:bold">,&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#c4a000">journal&lt;/span> &lt;span style="color:#000;font-weight:bold">=&lt;/span> &lt;span style="color:#4e9a06">{SoftwareX}&lt;/span>&lt;span style="color:#000;font-weight:bold">,&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#c4a000">volume&lt;/span> &lt;span style="color:#000;font-weight:bold">=&lt;/span> &lt;span style="color:#4e9a06">{10}&lt;/span>&lt;span style="color:#000;font-weight:bold">,&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#c4a000">pages&lt;/span> &lt;span style="color:#000;font-weight:bold">=&lt;/span> &lt;span style="color:#4e9a06">{100253}&lt;/span>&lt;span style="color:#000;font-weight:bold">,&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#c4a000">year&lt;/span> &lt;span style="color:#000;font-weight:bold">=&lt;/span> &lt;span style="color:#4e9a06">{2019}&lt;/span>&lt;span style="color:#000;font-weight:bold">,&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#c4a000">issn&lt;/span> &lt;span style="color:#000;font-weight:bold">=&lt;/span> &lt;span style="color:#4e9a06">{2352-7110}&lt;/span>&lt;span style="color:#000;font-weight:bold">,&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#c4a000">doi&lt;/span> &lt;span style="color:#000;font-weight:bold">=&lt;/span> &lt;span style="color:#4e9a06">{10.1016/j.softx.2019.100253}&lt;/span>&lt;span style="color:#000;font-weight:bold">,&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#c4a000">url&lt;/span> &lt;span style="color:#000;font-weight:bold">=&lt;/span> &lt;span style="color:#4e9a06">{https://www.sciencedirect.com/science/article/pii/S2352711018302760}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#000;font-weight:bold">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="citing-a-specific-version">Citing a specific version&lt;/h2>
&lt;p>To make a result reproducible, cite the version you ran alongside the paper:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="background-color:#f8f8f8;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-bibtex" data-lang="bibtex">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#000">@software&lt;/span>&lt;span style="color:#000;font-weight:bold">{&lt;/span>&lt;span style="color:#f57900">dpsim&lt;/span>&lt;span style="color:#000;font-weight:bold">,&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#c4a000">title&lt;/span> &lt;span style="color:#000;font-weight:bold">=&lt;/span> &lt;span style="color:#4e9a06">{DPsim}&lt;/span>&lt;span style="color:#000;font-weight:bold">,&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#c4a000">author&lt;/span> &lt;span style="color:#000;font-weight:bold">=&lt;/span> &lt;span style="color:#4e9a06">{{The DPsim Authors}}&lt;/span>&lt;span style="color:#000;font-weight:bold">,&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#c4a000">url&lt;/span> &lt;span style="color:#000;font-weight:bold">=&lt;/span> &lt;span style="color:#4e9a06">{https://github.com/sogno-platform/dpsim}&lt;/span>&lt;span style="color:#000;font-weight:bold">,&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#c4a000">note&lt;/span> &lt;span style="color:#000;font-weight:bold">=&lt;/span> &lt;span style="color:#4e9a06">{Version 1.2.1}&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#000;font-weight:bold">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>The repository also carries a &lt;code>CITATION.cff&lt;/code> file, so GitHub offers a ready-made citation
through the &amp;ldquo;Cite this repository&amp;rdquo; link on the project page.&lt;/p>
&lt;h2 id="further-publications">Further publications&lt;/h2>
&lt;p>Cite these when your work builds on the specific method they describe.&lt;/p>
&lt;p>Shifted frequency analysis and reduced-order machine models:&lt;/p>
&lt;ul>
&lt;li>J. Dinkelbach, M. Moraga and A. Monti, &amp;ldquo;Reduced-Order Synchronous Generator Modelling for
Real-Time Simulation using Shifted Frequency Analysis&amp;rdquo;, &lt;em>OSMSES&lt;/em>, 2023.
&lt;a href="https://ieeexplore.ieee.org/document/10089718">https://ieeexplore.ieee.org/document/10089718&lt;/a>&lt;/li>
&lt;li>G. Nakti, J. Dinkelbach, M. Mirz and A. Monti, &amp;ldquo;Comparative Assessment of Shifted Frequency
Modeling in Transient Stability Analysis using the Open Source Simulator DPsim&amp;rdquo;, &lt;em>OSMSES&lt;/em>, 2022.
&lt;a href="https://ieeexplore.ieee.org/document/9769135">https://ieeexplore.ieee.org/document/9769135&lt;/a>&lt;/li>
&lt;li>J. Dinkelbach, G. Nakti, M. Mirz and A. Monti, &amp;ldquo;Simulation of Low Inertia Power Systems Based
on Shifted Frequency Analysis&amp;rdquo;, &lt;em>Energies&lt;/em>, vol. 14, no. 7, 1860, 2021.
&lt;a href="https://www.mdpi.com/1996-1073/14/7/1860">https://www.mdpi.com/1996-1073/14/7/1860&lt;/a>&lt;/li>
&lt;/ul>
&lt;p>Power electronics modelling and parallelisation:&lt;/p>
&lt;ul>
&lt;li>M. Mirz, J. Dinkelbach and A. Monti, &amp;ldquo;DPsim: Advancements in Power Electronics Modelling Using
Shifted Frequency Analysis and in Real-Time Simulation Capability by Parallelization&amp;rdquo;,
&lt;em>Energies&lt;/em>, vol. 13, no. 15, 3879, 2020. &lt;a href="https://www.mdpi.com/1996-1073/13/15/3879">https://www.mdpi.com/1996-1073/13/15/3879&lt;/a>&lt;/li>
&lt;/ul>
&lt;p>Solver performance:&lt;/p>
&lt;ul>
&lt;li>J. Dinkelbach, L. Schumacher, L. Razik, A. Benigni and A. Monti, &amp;ldquo;Factorisation Path Based
Refactorisation for High-Performance LU Decomposition in Real-Time Power System Simulation&amp;rdquo;,
&lt;em>Energies&lt;/em>, vol. 14, no. 23, 7989, 2021. &lt;a href="https://www.mdpi.com/1996-1073/14/23/7989">https://www.mdpi.com/1996-1073/14/23/7989&lt;/a>&lt;/li>
&lt;/ul>
&lt;p>Grid data and CIM:&lt;/p>
&lt;ul>
&lt;li>J. Dinkelbach, L. Razik, M. Mirz, A. Benigni and A. Monti, &amp;ldquo;Template-based generation of
programming language specific code for smart grid modelling compliant with CIM and CGMES&amp;rdquo;,
&lt;em>The Journal of Engineering&lt;/em>, 2022.
&lt;a href="https://onlinelibrary.wiley.com/doi/abs/10.1049/tje2.12208">https://onlinelibrary.wiley.com/doi/abs/10.1049/tje2.12208&lt;/a>&lt;/li>
&lt;/ul>
&lt;p>Real-time and co-simulation:&lt;/p>
&lt;ul>
&lt;li>S. Vogel, M. Mirz, L. Razik and A. Monti, &amp;ldquo;An Open Solution for Next-generation Real-time Power
System Simulation&amp;rdquo;, &lt;em>IEEE EI2&lt;/em>, 2017. &lt;a href="https://ieeexplore.ieee.org/document/8245739">https://ieeexplore.ieee.org/document/8245739&lt;/a>&lt;/li>
&lt;li>M. Mirz, A. Estebsari, F. Arrigo, E. Bompard and A. Monti, &amp;ldquo;Dynamic phasors to enable
distributed real-time simulation&amp;rdquo;, &lt;em>ICCEP&lt;/em>, 2017.
&lt;a href="https://ieeexplore.ieee.org/document/8004805">https://ieeexplore.ieee.org/document/8004805&lt;/a>&lt;/li>
&lt;/ul></description></item></channel></rss>