Skip to content

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:

dyad
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:

ConnectorDomainpotentialflow
PinElectricalv::Voltagei::Current
NodeThermalT::TemperatureQ::HeatFlowRate
FlangeTranslationals::Positionf::Force
SplineRotationalphi::Angletau::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:

v=iR

In Dyad, we can define this as follows:

dyad
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:

dyad
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:

dyad
# 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:

dyad
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:

dyad
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.

dyad
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.

julia
using CairoMakie # for plotting
res = SimRLC() # Perform our analysis
f, a, p = lines(res)
axislegend(a)
f