Getting Started with Dyad Studio
In this section, we will discuss the basics for using Dyad for modeling and simulation. This includes:
How to use the Dyad VS Code extension
How to create a Dyad model
How to compile a model for simulation
How to run basic simulations
How to bring in standard libraries and compose models from components
Before you start this tutorial, make sure you have installed Dyad Studio and VSCode!
First Step: Creating a Component Library
In Dyad, all new components must be created in a component library. Thus our first step will be to create a new component library which we can use as a playground.
In Dyad Studio, you can create a new library by opening the Command Palette in VS Code (⇧ ⌘ P
on MacOS, Ctrl-Shift-P
on Windows). From the Command Palette, type Dyad
and you should see the following options:
Select Dyad: Create Component Library
from the list. You will then be prompted for the name of the library with a dialog like this one (although the appearance of the VS Code UI elements can differ from operating system to operating system).
It is suggested that the library name end in Components
as a simple way of indicating that the package is a Dyad component library. But it isn't strictly necessary that it end with Components
. But it does need to be both a valid Julia identifier and a valid directory name. As such, the names are currently limited to include only letters to ensure both constraints are satisified.
Once you enter a name, you'll be prompted for a directory to store the new package in. The Create Component Library
command will create a new directory inside the directory you select. Furthermore, the new directory will have the same name as the package. Said another way, whatever directory you select will get another directory created inside of it.
When the library creation process is done, a new Visual Studio Code window will open in the newly created directory.
NOTE
If you want to create a new Dyad component library with the Dyad CLI, you can simply run:
$ dyad create <LibraryName>
...from any directory and this command will create a new directory called with the library name you provided.
Setting Up and Testing Your Environment
The generated library includes a sample component
and analysis
. Let's use those to validate our development configuration. We must first "instantiate" our Julia environment. That means we need to resolve what versions of all the Julia packages we depend on and then download them. If your current directory is the directory that contains your component library's Project.toml
file, you can instantiate the package by starting a Julia REPL. Look for Julia: Start REPL
in the Command Palette (reminder: ⇧ ⌘ P
on MacOS, Ctrl-Shift-P
on Windows).
Once you get the julia>
prompt in VS Code, type the ]
character. This takes you to the package manager prompt. You should now see something that looks like this:
(<LibraryName>) pkg>
Here you should type instantiate
. If that works, at the same pkg>
prompt type test
. It may take some time to recompile some of the dependencies for testing, but when it's done, you should see something like this:
Test Summary: | Pass Total Time
Running test case1 for Hello | 2 2 20.6s
Testing FooBar tests passed
NOTE
These steps often take a long time (on the order of minutes) to complete. This is because all ~500 individual packages in the Dyad ecosystem are being compiled for your machine the first time you load them. This is a one-time cost, and subsequent loads will be much faster.
If you see this, everything is working!
What exactly is it testing? We'll come back to that in a second. Let's look at the model code now.
Running Our First Simulation: Lumped Thermal Model
Before getting to anything fancy, let's see how to use Dyad to define a lumped thermal model and simulate to see the solution. We use Newton's Law of cooling/heating which can be represented by the following simple linear ODE:
In the rest of this section, we will:
Look at a component representing this lumped thermal system.
Discuss an analysis for simulating the thermal response of the system.
Explore the analysis results via plots and other solution artifacts.
Newton's Law of Cooling/Heating
TIP
By whatever means you created the library, for the rest of this section we will assume you have opened the Dyad library in Visual Studio Code.
As mentioned previously, the result of all of this will be a new Julia package pre-configured for use as a Dyad library. Included will be a Project.toml
file that identifies the directory as a Julia package and lists its (initial) dependencies. You will also find a dyad
directory that includes a file called hello.dyad
. In there you will find two things. First, you'll find a Dyad component definition that should look something like this:
# A simple lumped thermal model
component Hello
# Ambient temperature
parameter T_inf::Temperature = 300
# Initial temperature
parameter T0::Temperature = 320
# Convective heat transfer coefficient
parameter h::CoefficientOfHeatTransfer = 0.7
# Surface area
parameter A::Area = 1.0
# Mass of thermal capacitance
parameter m::Mass = 0.1
# Specific Heat
parameter c_p::SpecificHeatCapacity = 1.2
variable T::Temperature
relations
# Specify initial conditions
initial T = T0
# Newton's law of cooling/heating
m*c_p*der(T) = h*A*(T_inf-T)
metadata {"Dyad": {"tests": {"case1": {"stop": 10, "expect": {"initial": {"T": 320}}}}}}
end
In addition you should also find an analysis
that looks something like this:
analysis World
extends TransientAnalysis(stop=10)
model = Hello(T_inf=T_inf, h=h)
parameter T_inf::Temperature = 300
parameter h::CoefficientOfHeatTransfer = 0.7
end
The Hello
definition describes a first order system. The temperature response of the system is contained in the variable T
. The various physical properties of the material are represented by the mass (m
), the surface area (A
), and the specific heat capacity (c_p
). The environmental conditions are represented by the ambient temperature (T_inf
) and the convective heat transfer coefficient (h
). Finally, the initial temperature for T
at the start of the simulation is prescribed using the parameter T0
(and the initial x = x0
relation found later in the model).
Simulating the Thermal Model
To run our simulation, we simply need to call the analysis as a Julia function of the same name in the REPL. If you are still at the pkg>
, press Backspace
or Delete
to return to the julia>
prompt (or select Julia: Start REPL
again from the Command Palette).
At the julia>
prompt, type the following lines of Julia code:
using <LibraryName>
result = World()
(You will need to substitute the name you gave your Dyad library wherever you see <LibraryName>
)
This will run the World
analysis, i.e. a TransientAnalysis
which simulates the Hello
model with T_inf=300
and h=0.7
. The result object displays some information about the result, but usually the most interesting information is a plot. In order to create this plot, we can use the Julia Plots.jl library. This is done like:
using Plots
plot(result)
The first time you run `plot(result)` you may see a significant delay before the plot window comes up. Don't panic. That delay is **not** the time required to simulate a simple first order model. This is Julia performing what we call "pre-compilation". See the [FAQ](/manual/faq#faq) for more details.
Note that T
does, in fact, start at
Interactively Modifying Analyses
You might be wondering, what is this World()
function and where did it come from. Recall our original Dyad code includes the following analysis
definition:
analysis World
extends TransientAnalysis(stop=10, abstol=1m, reltol=1m)
model = Hello(T_inf=T_inf, h=h)
parameter T_inf::Temperature = 300
parameter h::CoefficientOfHeatTransfer = 0.7
end
When Dyad generates Julia code, it maps each analysis
definition to a Julia function. In this case, the analysis is performing a TransientAnalysis
(there are other types of analyses...and you can even create your own...but this is beyond the scope of this introduction).
So the World()
function is running a transient simulation of our Hello
component (this is because the model
in our TransientAnalysis
is set to Hello(T_inf=T_inf, h=h)
). Furthermore, this World
analysis is parameterized. That means we can change the values of T_inf
and h
used in the analysis, e.g.,
plot(World(T_inf=400, h=0.2))
Running the command above should demonstrate heating instead of cooling since the ambient temperature is now higher than the initial temperature of
Obtaining Artifacts from an Analysis
While the plot of a transient simulation is the most basic diagnostic of the simulation solution, there are many other "views" of the a model to explore. All of the provided elements an analysis in Dyad are termed artifacts. To see what artifacts are available for the TransientAnalysis result, we can query it via:
using DyadInterface
artifacts(result)
5-element Vector{Symbol}:
:SimulationSolutionPlot
:SimulationSolutionTable
:RawSolution
:SimplifiedSystem
:InitialSystem
Notice that it says it has the basic plot, which we have already explored, and a solution table. Let's use the artifacts
function to grab that solution table:
table = artifacts(result, :SimulationSolutionTable)
Row | timestamp | T(t) |
---|---|---|
Float64 | Float64 | |
1 | 0.0 | 320.0 |
2 | 0.0859919 | 312.111 |
3 | 0.183252 | 306.867 |
4 | 0.312675 | 303.228 |
5 | 0.465576 | 301.323 |
6 | 0.65667 | 300.435 |
7 | 0.888094 | 300.113 |
8 | 1.17836 | 300.022 |
9 | 1.54544 | 300.003 |
10 | 2.0287 | 300.001 |
11 | 2.67484 | 300.002 |
12 | 3.46661 | 300.009 |
13 | 4.26205 | 300.056 |
14 | 4.97066 | 300.162 |
15 | 5.59423 | 300.204 |
16 | 6.16965 | 300.155 |
17 | 6.73219 | 300.103 |
18 | 7.30327 | 300.075 |
19 | 7.89056 | 300.064 |
20 | 8.49208 | 300.065 |
21 | 9.71004 | 299.987 |
22 | 10.0 | 299.998 |
As described in the TransientAnalysis
documentation, this returns a data table of the solution values that can be used to serialize the information.
Every analysis has their own artifacts. A controls analysis will have special plots such as Bode plots of the system, while a surrogate analysis will have a trained neural network. See the documentation on the different analyses in order to learn more about what can be generated from a model.
Writing Unit Tests
Unit tests and test-driven development are essential to all proper software development. Because of this, Dyad includes syntax within the model definitions that make it easy to do test-driven development. Recall our Hello
model looks like this:
# A simple lumped thermal model
component Hello
# Ambient temperature
parameter T_inf::Temperature = 300
# Initial temperature
parameter T0::Temperature = 320
# Convective heat transfer coefficient
parameter h::CoefficientOfHeatTransfer = 0.7
# Surface area
parameter A::Area = 1.0
# Mass of thermal capacitance
parameter m::Mass = 0.1
# Specific Heat
parameter c_p::SpecificHeatCapacity = 1.2
variable T::Temperature
relations
# Specify initial conditions
initial T = T0
# Newton's law of cooling/heating
m*c_p*der(T) = h*A*(T_inf-T)
metadata {"Dyad": {"tests": {"case1": {"stop": 10, "expect": {"initial": {"T": 320}}}}}}
end
Note specifically the metadata
:
{
"Dyad": {"tests": {"case1": {"stop": 10, "expect": {"initial": {"x": 10}}}}}
}
This metadata instructs the Dyad compiler to create tests cases for the Hello
components. Specifically, this creates a single test case, case1
, that should run for 10 seconds (stop
). Furthermore, it expects that the value of T
at the start of the simulation should be pkg> test
, it simulated the Hello
model and ensured that the results match what was expected.
The Dyad compiler is capable of automatically creating reference trajectories for regression testing of simulated results. But that functionality is outside the scope of this introduction.
Using Model Libraries and Composing Components: Generating an RLC Circuit
Dyad supports acausal modeling which means it has the ability to take pre-built model components and stich them together to quickly build complex models. Let's show this off by building a simple RLC circuit. The components for an RLC circuit are defined in the ElectricalComponents standard library, so let's first pull that library in:
using Pkg
Pkg.add("ElectricalComponents")
This is just one of many standard libraries included in Dyad, peruse the documentation pages on the component libraries to see all of the prebuilt models for fluids, hydraulics, mechanical systems, and much more!
Now let's define the RLC model. What we will do is create a resistor, an inductor, and a capacitor, and connect them together. To give it power, we will connect it to a voltage source. All together, this looks like:
component StepVoltage
extends ElectricalComponents.TwoPin
parameter Vf::Voltage = 32
parameter stepTime::Time = 5
relations
v = if time < stepTime then 0 else Vf
end
component RLC
resistor = ElectricalComponents.Resistor(R=100)
capacitor = ElectricalComponents.Capacitor(C=1m)
inductor = ElectricalComponents.Inductor(L=1)
source = StepVoltage()
ground = ElectricalComponents.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
Notice this is the same syntax as how we defined the Hello
component, but now instead of defining all of the relational equations from scratch, we are pulling in these equations from pre-defined components and using the connect
relation to compose them together. And just like before, we can use a TransientAnalysis
to simulate this model. Let's build a simulation of this component and example its results:
analysis SimRLC
extends TransientAnalysis(stop=10, abstol=1m, reltol=1m)
model = RLC()
end
Now let's run and plot our simulation:
using DyadInterface, Plots
result = SimRLC()
plot(result)
By default it only plots what was solved, but the RLC has many more aspects to its model. We can use the idxs
command in the plot to show other solved values:
sys = artifacts(result, :SimplifiedSystem)
plot(result; idxs = [sys.capacitor.v, sys.resistor.i])
Next Steps and Where to Learn More
Congrats! You now know how to build some basic components in Dyad, run simulations, and stitch together pre-built components for more complex models. But there are lots of other features to explore and much more to learn. Some recommended next steps are:
See how to construct more complex models using the standard libraries. This is recommended if you want to get up and running with complex simulations quickly.
Learn more about how acausal modeling works by building the RLC circuit from scratch. This is for if you care about the details: how was everything defined? How does it know equations to simulate? Etc.
Want more details on the Dyad language? Check out the Dyad language syntax definition
Learn how to do more with your models by seeing analysis-specific tutorials, such as learning how to perform a design optmization or build an inversion-based control
Explore other standard libraries, like the HydraulicComponents and ThermalComponents
If you have questions or just want to chat with Dyad devs, feel free to reach out on the #dyad Slack channel on the JuliaLang Slack. (If you're not already on JuliaLang Slack, this page can help you join.) If you find bugs or want to suggest features, you can also create issues on the DyadIssues Github repo.