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
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. 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)
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. Open a Julia REPL and perform the following commands.
using <LibraryName>
using Plots
result = SimRLC()
plot(result)