Skip to content

Commit 64a2721

Browse files
committed
docs: add Markdown version
1 parent 0d41984 commit 64a2721

File tree

1 file changed

+383
-0
lines changed

1 file changed

+383
-0
lines changed

cheatsheet.md

Lines changed: 383 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,383 @@
1+
# JSML Cheatsheet
2+
3+
## Defining Types
4+
5+
Generally speaking, you'll want to define types for physical quantities. This
6+
will include the units associated with that physical type along with things like
7+
limits. Here are a few basic types for electrical systems (although the goal
8+
will be to eventually create a comprehensive library of SI types).
9+
10+
```
11+
type Voltage = Real(units="V")
12+
type Current = Real(units="A")
13+
type Resistance = Real(units="Ω", min=0)
14+
type Capacitance = Real(units="F", min=0)
15+
type Inductance = Real(units="H", min=0)
16+
type VoltageRate = Real(units="V/s")
17+
```
18+
19+
There is no MTK equivalent of `type`, but we'll see the effect of these types in
20+
the next section.
21+
22+
## Creating a Connector
23+
24+
In JSML, a connector is defined with matching pairs of `potential` and `flow`
25+
variables and then any number of `stream` and `singleton` fields. The latter
26+
two are not discussed here, but a basic electrical pin connector could be
27+
defined as:
28+
29+
```
30+
31+
connector Pin
32+
potential v::Voltage
33+
flow i::Current
34+
end
35+
36+
```
37+
38+
...and the generated MTK code would be:
39+
40+
```julia
41+
@connector Pin begin
42+
v(t), [unit = u"V"]
43+
i(t), [unit = u"A", connect = Flow]
44+
end
45+
export Pin
46+
Pin
47+
```
48+
49+
Note how the variables have units? Those units are based on the `type` used in
50+
the JSML source.
51+
52+
## Creating a Model
53+
54+
In JSML, creating an acausal component model looks like this:
55+
56+
```
57+
component Resistor
58+
parameter R::Resistance
59+
p = Pin()
60+
n = Pin()
61+
variable v::Voltage
62+
variable i::Current
63+
relations
64+
v = p.v - n.v
65+
i = p.i
66+
p.i + n.i = 0
67+
v = i * R
68+
end
69+
```
70+
71+
The generated Julia code will then look like this:
72+
73+
```julia
74+
@component function Resistor(; name, R=nothing)
75+
systems = @named begin
76+
p = Pin()
77+
n = Pin()
78+
end
79+
vars = @variables begin
80+
v(t), [unit = u"V", guess = 0]
81+
i(t), [unit = u"A", guess = 0]
82+
end
83+
params = @parameters begin
84+
(R = R), [unit = u"Ω"]
85+
end
86+
eqs = [
87+
v ~ p.v - n.v
88+
i ~ p.i
89+
p.i + n.i ~ 0
90+
v ~ i * R
91+
]
92+
return ODESystem(eqs, t, vars, params; systems, name)
93+
end
94+
export Resistor
95+
```
96+
97+
JSML **does not have comments**. But it does have descriptive strings that
98+
are part of the JSML grammar and, therefore, bind the descriptions to
99+
specific entities. So, for example, we could add these descriptive strings to
100+
our JSML model:
101+
102+
```
103+
# A basic linear [resistor](https://en.wikipedia.org/wiki/Resistor)
104+
component Resistor
105+
# The resistance of the resistor
106+
parameter R::Resistance
107+
p = Pin()
108+
n = Pin()
109+
variable v::Voltage
110+
variable i::Current
111+
relations
112+
v = p.v - n.v
113+
i = p.i
114+
p.i + n.i = 0
115+
# Ohm's law
116+
v = i * R
117+
end
118+
119+
```
120+
121+
...and we'd get the following updated Julia code:
122+
123+
```julia
124+
"""
125+
A basic linear [resistor](https://en.wikipedia.org/wiki/Resistor)
126+
"""
127+
@component function Resistor(; name, R=nothing)
128+
systems = @named begin
129+
p = Pin()
130+
n = Pin()
131+
end
132+
vars = @variables begin
133+
v(t), [unit = u"V", guess = 0]
134+
i(t), [unit = u"A", guess = 0]
135+
end
136+
params = @parameters begin
137+
(R = R), [description = "The resistance of the resistor", unit = u"Ω"]
138+
end
139+
eqs = [
140+
v ~ p.v - n.v
141+
i ~ p.i
142+
p.i + n.i ~ 0
143+
# Ohm's Law
144+
v ~ i * R
145+
]
146+
return ODESystem(eqs, t, vars, params; systems, name)
147+
end
148+
export Resistor
149+
```
150+
151+
## System Models
152+
153+
A system level model in JSML would look something like:
154+
155+
```
156+
component RLCModel
157+
resistor = Resistor(R=100)
158+
capacitor = Capacitor(C=1m)
159+
inductor = Inductor(L=1)
160+
source::TwoPin = ConstantVoltage(V=30)
161+
ground = Ground()
162+
relations
163+
initial inductor.i = 0
164+
connect(source.p, inductor.n)
165+
connect(inductor.p, resistor.p, capacitor.p)
166+
connect(resistor.n, ground.g, capacitor.n, source.n)
167+
end
168+
169+
```
170+
171+
## Graphics
172+
173+
In order to represent the layout the components and connections in a system
174+
level model, we add metadata to the model. So our previous `RLCModel` with
175+
graphical layout information would look like this:
176+
177+
```
178+
component RLCModel
179+
resistor = Resistor(R=100) [
180+
{ "JuliaSim": { "placement": { "icon": { "x1": 700, "y1": 400, "x2": 900, "y2": 600, "rot": 90 } } } }
181+
]
182+
capacitor = Capacitor(C=1m) [
183+
{ "JuliaSim": { "placement": { "icon": { "x1": 400, "y1": 400, "x2": 600, "y2": 600, "rot": 90 } } } }
184+
]
185+
inductor = Inductor(L=1) [
186+
{ "JuliaSim": { "placement": { "icon": { "x1": 200, "y1": 100, "x2": 400, "y2": 300, "rot": 180 } } } }
187+
]
188+
source::TwoPin = ConstantVoltage(V=30) [
189+
{ "JuliaSim": { "placement": { "icon": { "x1": 0, "y1": 400, "x2": 200, "y2": 600, "rot": 90 } } } }
190+
]
191+
ground = Ground() [
192+
{ "JuliaSim": { "placement": { "icon": { "x1": 400, "y1": 900, "x2": 600, "y2": 1100 } } } }
193+
]
194+
relations
195+
initial inductor.i = 0
196+
connect(source.p, inductor.n) [{
197+
"JuliaSim": {
198+
"route": [[{"x": 100, "y":200}]]
199+
}
200+
}]
201+
connect(inductor.p, resistor.p, capacitor.p) [{
202+
"JuliaSim": {
203+
"route": [
204+
[{"x": 500, "y": 200}, {"x": 800, "y":200}],
205+
[{"x": 800, "y": 200}, {"x": 500, "y": 200}]
206+
]
207+
}
208+
}]
209+
connect(resistor.n, ground.g, capacitor.n, source.n) [{
210+
"JuliaSim": {
211+
"route": [
212+
[{"x": 800, "y": 800}, {"x": 500, "y": 800}],
213+
[{"x": 500, "y": 800}],
214+
[{"x": 500, "y": 800}, {"x": 100, "y": 800}]
215+
]
216+
}
217+
}]
218+
end
219+
```
220+
221+
...will have the same generated Julia code. But it is also possible to generate
222+
an SVG of the system:
223+
224+
![RLC Model Diagram](./rlc.svg)
225+
226+
## Test Cases
227+
228+
A component definition can also include experiments and test cases. For
229+
example, the same `RLCModel` could be augmented with metadata as follows:
230+
231+
```
232+
component RLCModel
233+
resistor = Resistor(R=100)
234+
capacitor = Capacitor(C=1m)
235+
inductor = Inductor(L=1)
236+
source::TwoPin = ConstantVoltage(V=30)
237+
ground = Ground()
238+
relations
239+
initial inductor.i = 0
240+
connect(source.p, inductor.n)
241+
connect(inductor.p, resistor.p, capacitor.p)
242+
connect(resistor.n, ground.g, capacitor.n, source.n)
243+
metadata {
244+
"JuliaSim": {
245+
"experiments": {
246+
"simple": { "start": 0, "stop": 10.0, "initial": { "capacitor.v": 10, "inductor.i": 0 } }
247+
},
248+
"tests": {
249+
"case1": {
250+
"stop": 10,
251+
"initial": { "capacitor.v": 10, "inductor.i": 0 },
252+
"expect": {
253+
"initial": {
254+
"t": 0,
255+
"capacitor.v": 10.0
256+
},
257+
"final": {
258+
"t": 10.0
259+
}
260+
}
261+
}
262+
}
263+
}
264+
}
265+
end
266+
```
267+
268+
...and the generated Julia code would be augmented with additional functions,
269+
`@test`s and `@testset`s as a result:
270+
271+
```julia
272+
"""Run model RLCModel from 0 to 10"""
273+
function simple()
274+
@mtkbuild model = RLCModel()
275+
u0 = [model.capacitor.v => 10, model.inductor.i => 0]
276+
prob = ODEProblem(model, u0, (0, 10))
277+
sol = solve(prob)
278+
end
279+
export simple
280+
281+
@test try
282+
simple()
283+
true
284+
catch
285+
false
286+
end
287+
288+
@testset "Running test case1 for RLCModel" begin
289+
@mtkbuild model = RLCModel()
290+
u0 = [model.capacitor.v => 10, model.inductor.i => 0]
291+
prob = ODEProblem(model, u0, (0, 10))
292+
sol = solve(prob)
293+
@test sol.t[1] 0
294+
@test sol[model.capacitor.v][1] 10
295+
@test sol.t[end] 10
296+
end
297+
```
298+
299+
## Expressions
300+
301+
```
302+
expression
303+
| simple_expression
304+
| ternary_expression
305+
306+
simple_expression
307+
| logical_expressions (':' logical_expression (`:` logical_expression)?)?
308+
309+
ternary_expression
310+
| 'if' expression 'then' expression ('elseif' expressions 'then' expression)* 'else' expression
311+
312+
logical_expression
313+
| logical_term ('or' logical_term)*
314+
315+
logical_term
316+
| logical_factor ('and' logical_factor)*
317+
318+
logical_factor
319+
| (`not`)? relation_expr
320+
321+
relation_expr
322+
| arithmetic_expression (rel_op arithmetic_expression)?
323+
324+
rel_op
325+
| '<='
326+
| '<'
327+
| '>='
328+
| '>'
329+
| '=='
330+
| '<>'
331+
332+
arithmetic_expression
333+
| (`+' | '-') term (add_op term)*
334+
| term (add_op term)*
335+
336+
add_op
337+
| '+'
338+
| '-'
339+
| '.+'
340+
| '.-'
341+
342+
term
343+
| factor (mul_op factor)*
344+
345+
mul_op
346+
| '%'
347+
| '*'
348+
| '/'
349+
| '.%'
350+
| '.*'
351+
| './'
352+
353+
factor
354+
| primary (('^' | '.^') primary)*
355+
356+
primary
357+
| UNSIGNED_NUMBER
358+
| STRING_LITERAL
359+
| BOOLEAN_LITERAL
360+
| component_reference ('(' argument_list ')')?
361+
| parenthetical_expression
362+
| array_expression
363+
364+
component_reference
365+
| deref ('.' deref)+
366+
367+
deref
368+
| IDENTIFIER ('[' expression (',' expression)+ ']')
369+
370+
argument_list
371+
| (expression | keyword_pair) (',' (expression | keyword_pair))+
372+
373+
keyword_pair
374+
| IDENTIFIER '=' expression
375+
376+
parenthetical_expression
377+
| `(` expression `)`
378+
379+
array_expression
380+
| '[' ']'
381+
| '[' expression (',' expression)* ']'
382+
```
383+

0 commit comments

Comments
 (0)