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.
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.
In Dyad, the connector for an electrical system (think of this as a wire or a pin) would be defined as follows:
connector Pin
potential v::Voltage
flow i::Current
end
In 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 component
s 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
end
Similarly, 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
end
But 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
end
Then, 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 = if time < stepTime then 0 else 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
end
RLC Model
With all these components in place, we can build an RLC circuit model as follows:
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)
end
Analysis
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()
end
Simulating
Now that we have defined our analysis, we can run it and visualize the results.
Dyad is a declarative modeling language meant for stating the mathematical problem we are interested in. But it is not a general purpose (imperative) programming language. If we need to describe a step by step description of what calculations we want done, we do that in Julia.
If you have written all this Dyad code to a tutorial library, you can now open the Julia REPL, activate the environment of your tutorial library, and load it by using TutorialLibrary
. If you aren't sure how to create a new Dyad library, instantiate your environment or use the REPL, then you should consider going through this tutorial first and then returning here.
using CairoMakie # for plotting
res = SimRLC() # Perform our analysis
f, a, p = lines(res)
axislegend(a)
f