Last week, JuliaHub released Dyad Studio, an extension for Visual Studio Code. This is the first tool we are releasing for the Dyad platform. Future tools will include Dyad Builder and our Dyad compiler CLI.
At the core of all of these tools is the Dyad language. The Dyad language is heavily inspired by both the Modelica language and the Julia language. So a reasonable question to ask is...why another language? Why not use Modelica or Julia (or Python or MATLAB). With this blog post, I'll explain why we chose to go this route and why we think it is so exciting.
Modelica is a language that was ahead of its time. Released in 1997, Modelica charted a bold course by committing to a declarative language for modeling of physical systems. It pioneered the use of symbolic manipulation to improve simulation speed. It incorporated software engineering principles to allow modeling activities to scale. It is a treasure trove of amazing ideas that have withstood the test of time thanks to contributors like Hilding Elmqvist, Martin Otter, François Cellier and countless others.
The Dyad language is clearly and unapologetically inspired by the Modelica language. But the more time passes, the more the technological landscape changes and with that change come opportunities to capitalize on those changes. When Modelica was created, the world was a very different place. Furthermore, we have a greater understanding of the requirements and use cases for such a modeling language now.
At JuliaHub, our goal is to drive innovation in the area of technical computing. We want to bridge the gap between hardware engineering and software development. We have a vision, which we'll be communicating over the coming months, for doing this. But one part of that vision was a language for describing and analyzing the combination of hardware and software. We could have used Modelica for this, but we didn't. And this article is going to describe why.
There are a bunch of relatively simple limitations in Modelica. Any one of them would not be justification by itself to create a new language. But even these small things add up.
The first thing to recognize about Modelica was that it has its own system for doing scalable vector graphics. The SVG specification wasn't even published until 2001 and it took many years for it to be widely adopted. So it wasn't a viable option for Modelica to rely on SVG. But now, it is ubiquitous, well-supported and powerful. So the Dyad language delegates all representations of structured graphics to SVG and in doing so it can support a wide range of drawing primitives, filters and effects.
Similarly, the first edition of ECMA-404, the Ecmascript standard for Javascript Object Notation (JSON), wasn't adopted until 2013. As with SVG, it is now ubiquitous and supported by every major language. But JSON didn't exist when Modelica was developed so Modelica developed its own special syntax for representing structured data (called annotations, in Modelica). The Dyad language just uses JSON to represent metadata associated with Dyad entities. But with respect to JSON, we go one step further. The Dyad toolchain fully supports import and export of Dyad's abstract syntax trees serialized to JSON. This means that you can write tools that read, transform and write Dyad code programmatically in virtually every modern programming language. No need for special parsers or tooling. Annotations are an underutilized feature in Modelica largely because it was difficult for users to actually read and write that data. With Dyad you can not only access the metadata easily, you can access the entire AST.
Modelica is "mostly" a declarative language. Declarative languages are very nice for describing the structure of problems in a way that can be soundly reasoned about and transformed. But declarative languages aren't good for representing computations. As such, Modelica adopted a limited set of imperative semantics and a foreign function interface to languages like C and Fortran for more complex cases. With Dyad, we have only declarative functionality in the language. For imperative calculations, we integrate directly with Julia. The Julia language is powerful enough for us to not only perform most technical computing calculations, but because of its multiple dispatch functionality it also allows us to write a single function that operates both on numerical values and symbols. For modern modeling and simulation approaches that heavily leverage symbolic manipulation, this is an amazingly powerful feature that comes at no cost to the user. Julia also seamlessly integrates with C and Fortran (among others), allowing users to directly call C functions in libraries. Finally, the whole ecosystem of Julia code is at your fingertips in Dyad.
In Modelica, documentation was written in HTML and wrapped by an annotation. As a result, in Modelica code documentation looks like this:
annotation (
Documentation(info="<html>
<p>
This component models a <strong>one-way clutch</strong>, i.e., a component with
two flanges where friction is present between the two flanges
and these flanges are pressed together via a normal force. These
flanges may be sliding with respect to each other.
</p>
...
<blockquote><pre>
frictional_torque = <strong>cgeo</strong> * <strong>mu</strong>(w_rel) * <strong>fn</strong>
</pre></blockquote>
<dl>
<dt>Otter M., Elmqvist H., and Mattsson S.E. (1999):</dt>
<dd><strong>Hybrid Modeling in Modelica based on the Synchronous
Data Flow Principle</strong>. CACSD'99, Aug. 22.-26, Hawaii.</dd>
</dl></html>"))
Dyad went a different direction in two regards. First, we fully embrace Markdown for documentation. While HTML is the cornerstone of the web, it isn't the most human readable form. Furthermore, in technical computing mathematics are important and using Markdown allows embedding LaTeX expressions and equations. Second, we don't bury the documentation in an annotation. In fact, we got rid of Modelica's "descriptive strings" altogether and replaced them with Markdown doc strings. So the above documentation looks like this in Dyad:
# Parallel connection of freewheel and clutch
#
# This component models a *one-way clutch*, _i.e._, a component with
# two flanges where friction is present between the two flanges
# and these flanges are pressed together via a normal force. These
# flanges may be sliding with respect to each other.
#
# $$ \tau_f = c_{geo} \mu \omega_{rel} f_n $$
#
# - Otter M., Elmqvist H., and Mattsson S.E. (1999):
# - *Hybrid Modeling in Modelica based on the Synchronous Data Flow Principle*. CACSD'99, Aug. 22.-26, Hawaii.
This approach isn't just for the models. In Dyad every parameter, variable, sub-component and equation can feature this same rich approach to documentation.
Modelica handles modeling of complex fluid systems quite well with features like stream variables on connectors. Dyad also incorporates stream variables. But it adds path variables which complements the modeling of complex systems (in particular fluid systems). A path variable represents information that is shared among all connectors in a "circuit". This is the ideal way to propagate media information. Instead of having to cascade replaceable packages tediously through hierarchies of components, we can infer the choice of medium models via connections and circuit continuity constraints. In this way, the modeler simply needs to define what the medium of a circuit is in one place and the system can infer it everywhere else largely via existing connections.
It is still early in the development of Dyad, but we feel that this path concept can help unify modeling of multibody systems (including cut joint analysis), propagation of medium models and awkward scoping constructs like inner and outer. We haven't "proven" this to ourselves yet, but we are confident that this approach could help simplify and unify several concepts that are distinct in Modelica.
Speaking of media, we leverage Julia's multiple dispatch system to handle evaluation of medium properties. This same approach was used in Modia as well. The net result, for modelers, is that they don't need to create media libraries based on a fixed set of fluid properties that might be needed but can instead create medium models that implement exactly the properties that their system requires without sacrificing flexibility and reusability.
Modelica version 3.3 (May, 2012) incorporated synchronous language elements. These language elements made it possible to more rigorously define the behavior of discrete systems and, in particular, state machines. But state machines were missing from the language for 15 years. And while version 3.3 introduced very clear and powerful semantics around state machines, the "developer experience" around using them suffered because they had to conform to the existing semantics of the language. These issues are hinted at by comments like the following in the Modelica Specification:
When a state class uses an outer output declaration, the equations have access to the corresponding variable declared inner. Special rules are then needed to maintain the single assignment rule since multiple definitions of such outer variables in different mutually exclusive states needs to be merged.
In Dyad, we hope to go even further than Modelica with respect to first class support for state machines and their role in code generation for embedded systems.
In Modelica (as in many languages), an enum is simply a named subset of integer values. This is, of course, quite useful for type checking but not quite as expressive as enumerated types (or sum types) in other languages. In Modelica one often finds that a model has a parameter that is an enumeration type and then a series of "conditional" parameters or subcomponents whose existence is controlled by the enumeration. For example, in Modelica an enumeration type could be defined as:
type Dynamics = enumeration(
DynamicFreeInitial,
FixedInitial,
SteadyStateInitial,
SteadyState)
"Enumeration to define definition of balance equations";
...and then used as follows...
parameter Types.Dynamics massDynamics "Formulation of mass balance";
parameter Medium.AbsolutePressure p_start if massDynamics==Dynamics.FixedInitial
...
equation
if massDynamics == Dynamics.SteadyState then
0 = mb_flow;
else
der(m) = mb_flow;
end if;
initial equation
if massDynamics == Dynamics.FixedInitial then
medium.p = p_start;
elseif massDynamics == Dynamics.SteadyStateInitial then
der(medium.p) = 0;
end if;
In Dyad, we do things slightly differently:
# Enumeration to define definition of balance equations
enum Dynamics =
| DynamicFreeInitial,
| FixedInitial(p_start::AbsolutePressure),
| SteadyStateInitial,
| SteadyState
...and then used as follows...
# Formulation of mass balance
parameter massDynamics::Types.Dynamics
...
relations
switch massDynamics
case SteadyState
mb_flow = 0
default
der(m) = mb_flow
end
switch massDynamics
case FixedInitial
initial medium.p = massDynamics.p_start;
case SteadyStateInitial
initial der(medium.p) = 0;
end
The key point here is that the additional information required for each contingency is contained in the enum which avoids the clutter of conditional parameters and components that may or may not be used.
Modelica doesn't have a package manager. In 1997, that was quite reasonable. But today, every major language has a package manager. One consequence of Modelica's lack of a package manager is that the standard library is quite monolithic. As a practical matter, since there was no package manager the best way for a library to gain widespread adoption was by being included in the Modelica standard library and that is precisely what happened.
For Dyad, we take a very different approach. First, we already have a package manager. This is because we leverage the existing Julia package manager, Pkg. So out of the gate we have a first class package manager which is already serving (and scaling with) the growing Julia ecosystem and it will do the same for Dyad. This is because every Dyad library is a Julia package.
In addition, we've organized our "standard" set of libraries a bit differently. We have a "base" library that is always loaded with every project but it is quite tiny. It includes only the physical types associated with the SI standard and connectors for all the basic engineering domains. Everything else is optional, but you can easily add any Julia packages as dependencies and Pkg will take care of finding the correct version and downloading and installing the package for you automatically. Furthermore, Pkg sets up a separate environment for each of your projects which makes it easy to use different versions of packages across different projects.
I'm often asked the question, "why not just take all these ideas to the Modelica Association and use them to improve Modelica itself?". Of course, over the years I have done just that many times. But the process of adding new functionality to Modelica is fairly time and resource intensive. First you must make a proposal but for that proposal to really have a chance it is necessary to make a test implementation of the feature. Then the detailed semantics of the proposal must be formulated for the specification and voted on. Finally, the various tool vendors must implement the functionality.
The choice to have a common specification was a strategic and well motivated one. It is not unlike how HTML, CSS, SVG and Javascript came to be part of all the major browsers. Having a specification and holding vendors accountable for it has its advantages. But it is interesting to note that most major browser vendors actually gave up on having their own implementations of these standards. The cost of implementation was quite high and so now the major browsers are mostly built on WebKit itself or a fork of it, Blink. Similarly, they largely rely on v8 as their Javascript engine for the same reason. It is also worth noting that developing an IDE for a new language, like Dyad, from scratch would also be unusual when it is possible to achieve the same functionality via a simple extension to VS Code. As such, creating a new browser from scratch would be an enormous undertaking. The lesson here is that coding to a specification, even for large and well resourced companies, is quite an effort. For a niche community like the modeling community it is even more of a burden.
So we've opted instead to take the route of having a single implementation, for better or worse. This allows us the flexibility to make changes where we want and to finish those implementations quickly. Where possible, we leverage adjacent standards like SVG, JSON, Markdown, etc. to avoid having to reinvent anything ourselves. We feel this makes our operation quite streamlined and efficient.
Of course, another thing that Modelica (and FMI) pioneered was being exceptionally open. We plan to publish most of the Dyad toolchain under a "Source available" license. Furthermore, many of the fundamental technologies that Dyad is built on, like Julia and ModelingToolkit, are available under open source licenses. We also plan to make our Dyad "standard libraries" available under an open source license.
But our openness goes beyond that. The Dyad tools don't just translate Dyad code into fast simulation code. We emit representations that allow you to interact directly with the symbolic forms of the problems. So you can actually visualize and even transform those systems directly using Julia. What's more, the various analyses that we support like transient simulation, steady state analysis, linearization, sensitivity analysis, optimization and so on are all implemented via our "Analysis API" which allows users not only to create their own models, but to create their own types of custom analyses with access to the symbolic representations and language features like automatic differentiation. In fact, we have a significant number of analysis types in development around AI and machine learning related to optimization, reduced order models and digital twin synthesis built on this foundation. And these analyses are first class citizens in Dyad, meaning you can describe not only your models and their behaviors in Dyad, you can describe the analyses you wish to perform as well (akin to the experiment annotation in Modelica but on steroids and extensible).
There are a number of high-level languages, like Python and MATLAB, that are well liked because they are well suited to prototyping of ideas. Julia is among those languages. But for models and control strategies to be transformed into embedded systems, it is often necessary to rewrite those prototypes into performance oriented languages, like C and Rust, that generate code optimized for a particular platform. Coincidentally, Julia is also among those languages.
For this reason, we feel that Julia is an ideal language to bridge this gap by allowing the same code that is written to prototype an idea to evolve into code that is suitable for embedded systems without the need to spend time and resources on translation from one language to the next.
With all of these differences to Modelica, you might think we are rethinking everything. But that isn't the case. The fact that Modelica uses text as the single source of truth for everything about a model, including the graphical representation, is a great strength and we've followed suit. Furthermore, we are convinced that symbolic manipulation is the key to optimal simulation performance so we use many of the same methods, but (discussed earlier) implemented in a way so that they are user accessible. Many of the ideas in Modelica around fluid modeling, software engineering, static analysis and multi-domain modeling have stood the test of time. As the old saying goes, "if it ain't broke, don't fix it".
In conclusion, Dyad is building on the legacy of Modelica but adapting to modern engineering workflows and technologies. We are building a new system for modeling, simulation, analysis and code generation on top of Julia. In doing so, we are leveraging the wide range of numerical solvers found in DifferentialEquations.jl, the symbolic math capabilities of Symbolics.jl, the symbolic manipulation of ModelingToolkit.jl and a whole host of other Scientific Machine Learning (SciML) libraries.
In 2001, I wrote the first book on Modelica because I believed in the vision that Modelica had for the future of modeling and simulation...and I still do. Back then, I would talk with people in the modeling community about this great new technology and how people should adopt it. What I heard was often that companies were too deeply invested in other technologies and that it would be very difficult for people to adopt Modelica.
Today, when I go around sharing our vision for the Dyad platform, I hear the same thing. Except the incumbent technology that people are so deeply invested in is very often Modelica. The point here is that although it may seem like change is impossible, it is not only possible but sometimes surprisingly rapid. Furthermore, the so-called "change cost" with technologies is rapidly being eroded away by LLM fueled translations. While it may have taken a generation for Modelica to become widely adopted, I suspect Dyad adoption could be much faster. We are already working on LLM based translation tools. As a result, tools that rely on vendor lock-in find themselves in an increasingly tenuous situation.
All of this is good news for practitioners who demand better modeling tools and the developers who want to build them (and I place myself firmly in both camps).
For more resources:
Dyad: A New Language to Make Hardware Engineering as Fast as Software
Download and Install Dyad Studio.