Acausal Modeling and Creating Components
In this tutorial, we will build a simple RLC circuit from scratch (no external libraries like ElectricalComponents), and solve it using Dyad.
To get started, create a new component library, as indicated on the Getting started page.
Connector
First, let's start with the connector. Modeling physical systems always starts by defining connectors. This is because connectors are used to represent the information exchanged by components that interact with each other.
We will define the connector for an electrical system (think of this as a wire or a pin). In the dyad folder, create a new file named rlc_parts.dyad and include the code below.
connector Pin
potential v::Voltage
flow i::Current
endIn Dyad, a typical connector is defined by matching pairs of potential and flow variables.
However, you don't need to create such a definition. This is because Dyad already has connectors for the primary engineering domains built in (including this electrical connector). Because these connectors are "built in", you don't need to worry about loading a large standard library just to have access to these definitions or units. Furthermore, you don't need to load in all the components associated with a given domain just so you have access to the connectors.
The `flow` variable is generally the time derivative of a conserved quantity.
Current (time derivative of charge)
Torque (time derivative of angular momentum)
Force (time derivative of linear momentum)
Mass flow rate (time derivative of stored mass)
The sign convention used in Dyad is that flow is always positive into a component. Without a symmetric convention like this, it would matter which way you oriented a resistor or capacitor (which do not have an inherent orientation). For components where orientation matters, the icon will indicate how the orientation aligns with behavior.
If a component doesn't store the conserved quantity, the sum of all flow variables across all connectors associated with that quantity should be zero.
The potential variables are the quantities that drive this flow, e.g., differences in pressure, temperature, position, voltage, etc.
The built in connectors in Dyad are shown in the table below along with their choices for potential and flow variables:
| Connector | Domain | potential | flow |
|---|---|---|---|
Pin | Electrical | v::Voltage | i::Current |
Node | Thermal | T::Temperature | Q::HeatFlowRate |
Flange | Translational | s::Position | f::Force |
Spline | Rotational | phi::Angle | tau::Torque |
The physical types used in this connector, Voltage and Current (in fact, all ISO standard units) are also built in to Dyad, as are all the ISO standard units.
Components
Now, let's define some components to model with. Components are the the building blocks of Dyad, and are defined by parameters, variables, and relations.
A resistor is simply a component with two pins that obeys Ohm's law:
In Dyad, we can define this as follows:
component Resistor
p = Pin()
n = Pin()
variable v::Voltage
variable i::Current
# Resistance of this resistor
parameter R::Resistance
relations
v = p.v - n.v
i = p.i
p.i + n.i = 0
# Ohm's Law
v = i * R
endSimilarly, we could define a capacitor:
component Capacitor
p = Pin()
n = Pin()
variable v::Voltage
variable i::Current
# Capacitance of this capacitor
parameter C::Capacitance
relations
v = p.v - n.v
i = p.i
p.i + n.i = 0
C * der(v) = i
endBut let's avoid repeating ourselves - you can notice the structural similarity between the resistor and capacitor.
Partial components
In Dyad, we can define partial components. A partial component is a component that is not complete, but can be used to build a complete component.
To avoid repeating quite a bit of code, we could define OnePort as follows:
# A `OnePort` component is one that has two pins, `p` and `n`, and which
# has a single current flow path such that all the current that flows in
# from one side of that path flows out the other. We can also define the
# voltage drop, `v`, across this path.
partial component OnePort
p = Pin()
n = Pin()
variable v::Voltage
variable i::Current
relations
v = p.v - n.v
i = p.i
p.i + n.i = 0
endThen, the definitions of all our simple electrical components become much simpler:
component Resistor
extends OnePort
parameter R::Resistance
relations
v = i * R
end
component StepVoltage
extends OnePort
parameter Vf::Voltage = 32
parameter stepTime::Time = 5
relations
v = ifelse((time < stepTime), 0, Vf)
end
component Capacitor
extends OnePort
parameter C::Capacitance
relations
C*der(v) = i
end
component Inductor
extends OnePort
parameter L::Inductance
relations
L*der(i) = v
end
component Ground
g = Pin()
relations
g.v = 0
endRLC Model
With all these components in place, we can build an RLC circuit model. In the dyad folder, create a new file named rlc.dyad and include the code below.
component RLC
resistor = Resistor(R=100)
capacitor = Capacitor(C=1m)
inductor = Inductor(L=1)
source = StepVoltage()
ground = Ground()
relations
initial inductor.i = 0
initial capacitor.v = 10
connect(source.p, inductor.n)
connect(inductor.p, resistor.p, capacitor.p)
connect(resistor.n, ground.g, capacitor.n, source.n)
endAnalysis
Now that we have defined our component, we need a way to analyze it.
This is where the Dyad concept of an analysis comes in. Analyses are quite powerful. They not only provide out of the box workflows for doing things like performing a transient analysis or linearizing the equations of a system, they are extensible, i.e., users can create their own analyses. These new analyses (just like the ones built in ones) are written in Julia and have access to the fully symbolic problem statement.
For now, let's investigate the simplest case, a transient analysis. This is a simulation of the component over a given time span.
analysis SimRLC
extends TransientAnalysis(stop=10, abstol=1m, reltol=1m)
model = RLC()
endSimulating
Now that we have defined our analysis, we can run it and visualize the results. Open a Julia REPL and perform the following commands.
using <LibraryName>using Plots
result = SimRLC()
plot(result)