Electrical Components

The previous section discussed how to create component models in the heat transfer domain. Now let’s turn our attention to how to construct some basic electrical components and then use them to simulate the kinds of systems we saw in our previous electrical example.

In this section we will implement the basic electrical component models twice. The first time through, we will implement each component without any regard to the others. But the second time through, we’ll see how we can use the inheritance mechanism in Modelica to make our lives a little easier.

But in both cases, we’ll use the same connector definitions. In our discussion of Simple Domains, we saw how to construct an electrical connector. As with the previous section on heat transfer, the examples in this section will rely on the connector definitions from the Modelica Standard Library. Those connector definitions look like this:

connector PositivePin "Positive pin of an electric component"
  Modelica.SIunits.Voltage v "Potential at the pin";
  flow Modelica.SIunits.Current i "Current flowing into the pin";
end PositivePin;

connector NegativePin "Negative pin of an electric component"
  Modelica.SIunits.Voltage v "Potential at the pin";
  flow Modelica.SIunits.Current i "Current flowing into the pin";
end NegativePin;

Basic Component Models

Given these connector definitions, it is relatively straightforward to construct a resistor model. The goal of a resistor model is to encapsulate the relationship between the voltage across the resistor and the current through the resistor using Ohm’s law. The following model represents how one might expect such a resistor model to look:

within ModelicaByExample.Components.Electrical.VerboseApproach;
model Resistor "A resistor model"
  parameter Modelica.SIunits.Resistance R;
  Modelica.Electrical.Analog.Interfaces.PositivePin p
    annotation (Placement(transformation(extent={{-110,-10},{-90,10}})));
  Modelica.Electrical.Analog.Interfaces.NegativePin n
    annotation (Placement(transformation(extent={{90,-10},{110,10}})));
protected
  Modelica.SIunits.Voltage v = p.v-n.v;
equation
  p.i + n.i = 0 "Conservation of charge";
  v = p.i*R "Ohm's law";
end Resistor;

In the same way, we might create inductor and capacitor models as follows:

within ModelicaByExample.Components.Electrical.VerboseApproach;
model Inductor "An inductor model"
  parameter Modelica.SIunits.Inductance L;
  Modelica.Electrical.Analog.Interfaces.PositivePin p
    annotation (Placement(transformation(extent={{-110,-10},{-90,10}})));
  Modelica.Electrical.Analog.Interfaces.NegativePin n
    annotation (Placement(transformation(extent={{90,-10},{110,10}})));
protected
  Modelica.SIunits.Voltage v = p.v-n.v;
equation
  p.i + n.i = 0 "Conservation of charge";
  L*der(p.i) = p.v;
end Inductor;
within ModelicaByExample.Components.Electrical.VerboseApproach;
model Capacitor "A capacitor model"
  parameter Modelica.SIunits.Capacitance C;
  Modelica.Electrical.Analog.Interfaces.PositivePin p
    annotation (Placement(transformation(extent={{-110,-10},{-90,10}})));
  Modelica.Electrical.Analog.Interfaces.NegativePin n
    annotation (Placement(transformation(extent={{90,-10},{110,10}})));
protected
  Modelica.SIunits.Voltage v = p.v-n.v;
equation
  p.i + n.i = 0 "Conservation of charge";
  C*der(v) = p.i;
end Capacitor;

The important thing to notice about these models is the amount of common code shared between them. In software development, this kind of redundancy is frowned upon. In fact, a common software maxim is “Redundancy is the root of all evil”. The reason this redundancy is a problem is partly because you are doing the same work multiple times, but also because this code needs to be maintained as well. When you repeat code and then find a mistake in that code, you have to fix it everywhere.

The DRY Principle

This issue of redundancy is an important one. So let’s revisit building models of resistors, inductors and capacitors with the goal of reducing the amount of repeated code. In software, there is something called the DRY principle where DRY stands for “Don’t Repeat Yourself”. So our next step is to make our resistor, capacitor and inductor models DRY.

The key to eliminating redundant code is to identify all the common code between these models and create a partial model that we can inherit from. We highlighted the common lines previously. Now we can capture them in their own model as follows:

within ModelicaByExample.Components.Electrical.DryApproach;
partial model TwoPin "Common elements of two pin electrical components"
  Modelica.Electrical.Analog.Interfaces.PositivePin p
    annotation (Placement(transformation(extent={{-110,-10},{-90,10}})));
  Modelica.Electrical.Analog.Interfaces.NegativePin n
    annotation (Placement(transformation(extent={{90,-10},{110,10}})));
protected
  Modelica.SIunits.Voltage v = p.v-n.v;
  Modelica.SIunits.Current i = p.i;
equation
  p.i + n.i = 0 "Conservation of charge";
end TwoPin;

In summary, we’ve extracted the declarations for p, n and v from the previous models into this model. We’ve also included a variable, i, to represent the current flowing from pin p to pin n. Finally, the conservation of charge equation is also included.

Creating such a model then allows us to create a much more succinct resistor model as follows:

within ModelicaByExample.Components.Electrical.DryApproach;
model Resistor "A DRY resistor model"
  parameter Modelica.SIunits.Resistance R;
  extends TwoPin;
equation
  v = i*R "Ohm's law";
end Resistor;

There are several things to notice about this Resistor model. The first is how much shorter it is. This is because we inherit the electrical pins, the conservation of charge equation and the variables v and i from the TwoPin model. Another thing to notice is that, by leverage the definitions of v and i, Ohm’s law looks just like it would if you saw it in a text book.

We can give the same treatment to our inductor and capacitor models:

within ModelicaByExample.Components.Electrical.DryApproach;
model Capacitor "A DRY capacitor model"
  parameter Modelica.SIunits.Capacitance C;
  extends TwoPin;
equation
  C*der(v) = i;
end Capacitor;
within ModelicaByExample.Components.Electrical.DryApproach;
model Inductor "A DRY inductor model"
  parameter Modelica.SIunits.Inductance L;
  extends TwoPin;
equation
  L*der(i) = v;
end Inductor;

Again, we see that the models are much more succinct. Ultimately, factoring out common code in this way means that the component models are easier to write and easier to maintain.

Circuit Model

So far, we’ve only built component models. In order to create a circuit model we first need to define a few more component models. Specifically, we need to create a step voltage source model:

within ModelicaByExample.Components.Electrical.DryApproach;
model StepVoltage "A DRY step voltage source"
  parameter Modelica.SIunits.Voltage V0;
  parameter Modelica.SIunits.Voltage Vf;
  parameter Modelica.SIunits.Time stepTime;
  extends TwoPin;
          color={0,0,255},

Note how the StepVoltage model also leverages the TwoPin model. We will also need a ground model which we model as follows:

within ModelicaByExample.Components.Electrical.DryApproach;
model Ground "Electrical ground"
  Modelica.Electrical.Analog.Interfaces.PositivePin ground "Ground pin"
    annotation (Placement(transformation(extent={{-10,70},{10,90}})));
equation
  ground.v = 0;
end Ground;

The Ground model has only one pin so it cannot inherit from TwoPin. Recall how we described the AmbientCondition model from our discussion on Heat Transfer Components an infinite reservoir. The Ground model serves a very similar purpose. No matter how much current flows in to or out of an electrical ground, the voltage remains zero.

Having defined all these components, we can now create a circuit model as follows:

within ModelicaByExample.Components.Electrical.Examples;
model SwitchedRLC "Recreation of the switched RLC circuit"
  DryApproach.StepVoltage Vs(V0=0, Vf=24, stepTime=0.5)
    annotation (Placement(transformation(
        extent={{-10,-10},{10,10}},
        rotation=270,
        origin={-40,0})));
  DryApproach.Inductor inductor(L=1)
    annotation (Placement(transformation(extent={{-10,10},{10,30}})));
  DryApproach.Capacitor capacitor(C=1e-3) annotation (Placement(transformation(
        extent={{-10,-10},{10,10}},
        rotation=90,
        origin={30,0})));
  DryApproach.Resistor resistor(R=100) annotation (Placement(transformation(
        extent={{-10,-10},{10,10}},
        rotation=90,
        origin={60,2})));
  DryApproach.Ground ground
    annotation (Placement(transformation(extent={{-10,-38},{10,-18}})));
equation
  connect(inductor.n, resistor.n) annotation (Line(
      points={{10,20},{60,20},{60,12}},
      color={0,0,255},
      smooth=Smooth.None));
  connect(capacitor.n, inductor.n) annotation (Line(
      points={{30,10},{30,20},{10,20}},
      color={0,0,255},
      smooth=Smooth.None));
  connect(inductor.p, Vs.p) annotation (Line(
      points={{-10,20},{-40,20},{-40,10}},
      color={0,0,255},
      smooth=Smooth.None));
  connect(capacitor.p, ground.ground) annotation (Line(
      points={{30,-10},{30,-20},{0,-20}},
      color={0,0,255},
      smooth=Smooth.None));
  connect(resistor.p, ground.ground) annotation (Line(
      points={{60,-8},{60,-20},{0,-20}},
      color={0,0,255},
      smooth=Smooth.None));
  connect(Vs.n, ground.ground) annotation (Line(
      points={{-40,-10},{-40,-20},{0,-20}},
      color={0,0,255},
      smooth=Smooth.None));
end SwitchedRLC;

The schematic diagram for this model is rendered as:

Modeling the step response of an RLC circuit