Skip to content

Commit 887ad82

Browse files
authored
Merge pull request #1410 from SciML/myb/release
v8 Release
2 parents 12b2efe + 6defe6b commit 887ad82

File tree

5 files changed

+58
-102
lines changed

5 files changed

+58
-102
lines changed

NEWS.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
# ModelingToolkit v8 Release Notes
2+
3+
4+
### Upgrade guide
5+
6+
- `connect` should not be overloaded by users anymore. `[connect = Flow]`
7+
informs ModelingToolkit that particular variable in a connector ought to sum
8+
to zero, and by default, variables are equal in a connection. Please check out
9+
[acausal components tutorial](https://mtk.sciml.ai/dev/tutorials/acausal_components/)
10+
for examples.

docs/src/basics/Composition.md

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -167,13 +167,17 @@ before numerically solving. The `structural_simplify` function removes
167167
these trivial equality relationships and trivial singularity equations,
168168
i.e. equations which result in `0~0` expressions, in over-specified systems.
169169

170-
## Inheritance and Combine (TODO)
170+
## Inheritance and Combine
171171

172-
Model inheritance can be done in two ways: explicitly or implicitly.
173-
The explicit way is to shadow variables with equality expressions.
174-
For example, let's assume we have three separate systems which we
175-
want to compose to a single one. This is how one could explicitly
176-
forward all states and parameters to the higher level system:
172+
Model inheritance can be done in two ways: implicitly or explicitly. First, one
173+
can use the `extend` function to extend a base model with another set of
174+
equations, states, and parameters. An example can be found in the
175+
[acausal components tutorial](@ref acausal).
176+
177+
The explicit way is to shadow variables with equality expressions. For example,
178+
let's assume we have three separate systems which we want to compose to a single
179+
one. This is how one could explicitly forward all states and parameters to the
180+
higher level system:
177181

178182
```julia
179183
using ModelingToolkit, OrdinaryDiffEq, Plots
@@ -340,4 +344,4 @@ tv = sort([LinRange(0, 10, 200); sol.t])
340344
plot(sol(tv)[y], sol(tv)[x], line_z=tv)
341345
vline!([-1.5, 1.5], l=(:black, 5), primary=false)
342346
hline!([0], l=(:black, 5), primary=false)
343-
```
347+
```

docs/src/tutorials/acausal_components.md

Lines changed: 11 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -16,22 +16,10 @@ using ModelingToolkit, Plots, DifferentialEquations
1616

1717
@variables t
1818
@connector function Pin(;name)
19-
sts = @variables v(t)=1.0 i(t)=1.0
19+
sts = @variables v(t)=1.0 i(t)=1.0 [connect = Flow]
2020
ODESystem(Equation[], t, sts, []; name=name)
2121
end
2222

23-
function ModelingToolkit.connect(::Type{Pin}, ps...)
24-
eqs = [
25-
0 ~ sum(p->p.i, ps) # KCL
26-
]
27-
# KVL
28-
for i in 1:length(ps)-1
29-
push!(eqs, ps[i].v ~ ps[i+1].v)
30-
end
31-
32-
return eqs
33-
end
34-
3523
function Ground(;name)
3624
@named g = Pin()
3725
eqs = [g.v ~ 0]
@@ -92,7 +80,8 @@ V = 1.0
9280
rc_eqs = [
9381
connect(source.p, resistor.p)
9482
connect(resistor.n, capacitor.p)
95-
connect(capacitor.n, source.n, ground.g)
83+
connect(capacitor.n, source.n)
84+
connect(capacitor.n, ground.g)
9685
]
9786

9887
@named _rc_model = ODESystem(rc_eqs, t)
@@ -117,11 +106,15 @@ For each of our components we use a Julia function which emits an `ODESystem`.
117106
At the top we start with defining the fundamental qualities of an electrical
118107
circuit component. At every input and output pin a circuit component has
119108
two values: the current at the pin and the voltage. Thus we define the `Pin`
120-
component (connector) to simply be the values there:
109+
component (connector) to simply be the values there. Whenever two `Pin`s in a
110+
circuit are connected together, the system satisfies [Kirchoff's laws](https: //en.wikipedia.org/wiki/Kirchhoff%27s_circuit_laws),
111+
i.e. that currents sum to zero and voltages across the pins are equal.
112+
`[connect = Flow]` informs MTK that currents ought to sum to zero, and by
113+
default, variables are equal in a connection.
121114

122115
```julia
123116
@connector function Pin(;name)
124-
sts = @variables v(t)=1.0 i(t)=1.0
117+
sts = @variables v(t)=1.0 i(t)=1.0 [connect = Flow]
125118
ODESystem(Equation[], t, sts, []; name=name)
126119
end
127120
```
@@ -253,26 +246,6 @@ V = 1.0
253246
@named ground = Ground()
254247
```
255248

256-
Next we have to define how we connect the circuit. Whenever two `Pin`s in a
257-
circuit are connected together, the system satisfies
258-
[Kirchoff's laws](https://en.wikipedia.org/wiki/Kirchhoff%27s_circuit_laws),
259-
i.e. that currents sum to zero and voltages across the pins are equal. Thus
260-
we will build a helper function `connect_pins` which implements these rules:
261-
262-
```julia
263-
function ModelingToolkit.connect(::Type{Pin}, ps...)
264-
eqs = [
265-
0 ~ sum(p->p.i, ps) # KCL
266-
]
267-
# KVL
268-
for i in 1:length(ps)-1
269-
push!(eqs, ps[i].v ~ ps[i+1].v)
270-
end
271-
272-
return eqs
273-
end
274-
```
275-
276249
Finally we will connect the pieces of our circuit together. Let's connect the
277250
positive pin of the resistor to the source, the negative pin of the resistor
278251
to the capacitor, and the negative pin of the capacitor to a junction between
@@ -282,7 +255,8 @@ the source and the ground. This would mean our connection equations are:
282255
rc_eqs = [
283256
connect(source.p, resistor.p)
284257
connect(resistor.n, capacitor.p)
285-
connect(capacitor.n, source.n, ground.g)
258+
connect(capacitor.n, source.n)
259+
connect(capacitor.n, ground.g)
286260
]
287261
```
288262

docs/src/tutorials/spring_mass.md

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,15 @@ In this tutorial we will build a simple component-based model of a spring-mass s
66

77
```julia
88
using ModelingToolkit, Plots, DifferentialEquations, LinearAlgebra
9+
using Symbolics: scalarize
910

1011
@variables t
1112
D = Differential(t)
1213

1314
function Mass(; name, m = 1.0, xy = [0., 0.], u = [0., 0.])
1415
ps = @parameters m=m
1516
sts = @variables pos[1:2](t)=xy v[1:2](t)=u
16-
eqs = collect(D.(pos) .~ v)
17+
eqs = scalarize(D.(pos) .~ v)
1718
ODESystem(eqs, t, [pos..., v...], ps; name)
1819
end
1920

@@ -25,12 +26,12 @@ end
2526

2627
function connect_spring(spring, a, b)
2728
[
28-
spring.x ~ norm(collect(a .- b))
29-
collect(spring.dir .~ collect(a .- b))
29+
spring.x ~ norm(scalarize(a .- b))
30+
scalarize(spring.dir .~ scalarize(a .- b))
3031
]
3132
end
3233

33-
spring_force(spring) = -spring.k .* collect(spring.dir) .* (spring.x - spring.l) ./ spring.x
34+
spring_force(spring) = -spring.k .* scalarize(spring.dir) .* (spring.x - spring.l) ./ spring.x
3435

3536
m = 1.0
3637
xy = [1., -1.]
@@ -43,7 +44,7 @@ g = [0., -9.81]
4344

4445
eqs = [
4546
connect_spring(spring, mass.pos, center)
46-
collect(D.(mass.v) .~ spring_force(spring) / mass.m .+ g)
47+
scalarize(D.(mass.v) .~ spring_force(spring) / mass.m .+ g)
4748
]
4849

4950
@named _model = ODESystem(eqs, t)
@@ -65,7 +66,7 @@ For each component we use a Julia function that returns an `ODESystem`. At the t
6566
function Mass(; name, m = 1.0, xy = [0., 0.], u = [0., 0.])
6667
ps = @parameters m=m
6768
sts = @variables pos[1:2](t)=xy v[1:2](t)=u
68-
eqs = collect(D.(pos) .~ v)
69+
eqs = scalarize(D.(pos) .~ v)
6970
ODESystem(eqs, t, [pos..., v...], ps; name)
7071
end
7172
```
@@ -97,16 +98,16 @@ We now define functions that help construct the equations for a mass-spring syst
9798
```julia
9899
function connect_spring(spring, a, b)
99100
[
100-
spring.x ~ norm(collect(a .- b))
101-
collect(spring.dir .~ collect(a .- b))
101+
spring.x ~ norm(scalarize(a .- b))
102+
scalarize(spring.dir .~ scalarize(a .- b))
102103
]
103104
end
104105
```
105106

106107
Lastly, we define the `spring_force` function that takes a `spring` and returns the force exerted by this spring.
107108

108109
```julia
109-
spring_force(spring) = -spring.k .* collect(spring.dir) .* (spring.x - spring.l) ./ spring.x
110+
spring_force(spring) = -spring.k .* scalarize(spring.dir) .* (spring.x - spring.l) ./ spring.x
110111
```
111112

112113
To create our system, we will first create the components: a mass and a spring. This is done as follows:
@@ -127,7 +128,7 @@ We can now create the equations describing this system, by connecting `spring` t
127128
```julia
128129
eqs = [
129130
connect_spring(spring, mass.pos, center)
130-
collect(D.(mass.v) .~ spring_force(spring) / mass.m .+ g)
131+
scalarize(D.(mass.v) .~ spring_force(spring) / mass.m .+ g)
131132
]
132133
```
133134

@@ -230,4 +231,4 @@ We can also plot the path of the mass:
230231
plot(sol, vars = (mass.pos[1], mass.pos[2]))
231232
```
232233

233-
![plotpos](https://user-images.githubusercontent.com/23384717/130322197-cff35eb7-0739-471d-a3d9-af83d87f1cc7.png)
234+
![plotpos](https://user-images.githubusercontent.com/23384717/130322197-cff35eb7-0739-471d-a3d9-af83d87f1cc7.png)

docs/src/tutorials/tearing_parallelism.md

Lines changed: 14 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -12,34 +12,11 @@ electrical circuits:
1212
```julia
1313
using ModelingToolkit, OrdinaryDiffEq
1414

15-
function connect_pin(ps...)
16-
eqs = [
17-
0 ~ sum(p->p.i, ps) # KCL
18-
]
19-
# KVL
20-
for i in 1:length(ps)-1
21-
push!(eqs, ps[i].v ~ ps[i+1].v)
22-
end
23-
24-
return eqs
25-
end
26-
27-
function connect_heat(ps...)
28-
eqs = [
29-
0 ~ sum(p->p.Q_flow, ps)
30-
]
31-
for i in 1:length(ps)-1
32-
push!(eqs, ps[i].T ~ ps[i+1].T)
33-
end
34-
35-
return eqs
36-
end
37-
3815
# Basic electric components
3916
@variables t
4017
const D = Differential(t)
41-
function Pin(;name)
42-
@variables v(t)=1.0 i(t)=1.0
18+
@connector function Pin(;name)
19+
@variables v(t)=1.0 i(t)=1.0 [connect = Flow]
4320
ODESystem(Equation[], t, [v, i], [], name=name)
4421
end
4522

@@ -61,8 +38,8 @@ function ConstantVoltage(;name, V = 1.0)
6138
compose(ODESystem(eqs, t, [], [V], name=name), p, n)
6239
end
6340

64-
function HeatPort(;name)
65-
@variables T(t)=293.15 Q_flow(t)=0.0
41+
@connector function HeatPort(;name)
42+
@variables T(t)=293.15 Q_flow(t)=0.0 [connect = Flow]
6643
ODESystem(Equation[], t, [T, Q_flow], [], name=name)
6744
end
6845

@@ -120,10 +97,10 @@ function parallel_rc_model(i; name, source, ground, R, C)
12097
heat_capacitor = HeatCapacitor(name=Symbol(:heat_capacitor, i))
12198

12299
rc_eqs = [
123-
connect_pin(source.p, resistor.p)
124-
connect_pin(resistor.n, capacitor.p)
125-
connect_pin(capacitor.n, source.n, ground.g)
126-
connect_heat(resistor.h, heat_capacitor.h)
100+
connect(source.p, resistor.p)
101+
connect(resistor.n, capacitor.p)
102+
connect(capacitor.n, source.n, ground.g)
103+
connect(resistor.h, heat_capacitor.h)
127104
]
128105

129106
compose(ODESystem(rc_eqs, t, name=Symbol(name, i)),
@@ -139,8 +116,8 @@ we can connect a bunch of RC components as follows:
139116

140117
```julia
141118
V = 2.0
142-
source = ConstantVoltage(name=:source, V=V)
143-
ground = Ground(name=:ground)
119+
@named source = ConstantVoltage(V=V)
120+
@named ground = Ground()
144121
N = 50
145122
Rs = 10 .^range(0, stop=-4, length=N)
146123
Cs = 10 .^range(-3, stop=0, length=N)
@@ -170,19 +147,9 @@ Yes, that's a good question! Let's investigate a little bit more what had happen
170147
If you look at the system we defined:
171148

172149
```julia
173-
equations(big_rc)
174-
175-
1051-element Vector{Equation}:
176-
Differential(t)(E(t)) ~ rc10₊resistor10₊h₊Q_flow(t) + rc11₊resistor11₊h₊Q_flow(t) + rc12₊resistor12₊h₊Q_flow(t) + rc13₊resistor13₊h₊Q_flow(t) + rc14₊resistor14₊h₊Q_flow(t) + rc15₊resistor15₊h₊Q_flow(t) + rc16₊resistor16₊h₊Q_flow(t) + rc17₊resistor17₊h₊Q_flow(t) + rc18₊resistor18₊h₊Q_flow(t) + rc19₊resistor19₊h₊Q_flow(t) + rc1₊resistor1₊h₊Q_flow(t) + rc20₊resistor20₊h₊Q_flow(t) + rc21₊resistor21₊h₊Q_flow(t) + rc22₊resistor22₊h₊Q_flow(t) + rc23₊resistor23₊h₊Q_flow(t) + rc24₊resistor24₊h₊Q_flow(t) + rc25₊resistor25₊h₊Q_flow(t) + rc26₊resistor26₊h₊Q_flow(t) + rc27₊resistor27₊h₊Q_flow(t) + rc28₊resistor28₊h₊Q_flow(t) + rc29₊resistor29₊h₊Q_flow(t) + rc2₊resistor2₊h₊Q_flow(t) + rc30₊resistor30₊h₊Q_flow(t) + rc31₊resistor31₊h₊Q_flow(t) + rc32₊resistor32₊h₊Q_flow(t) + rc33₊resistor33₊h₊Q_flow(t) + rc34₊resistor34₊h₊Q_flow(t) + rc35₊resistor35₊h₊Q_flow(t) + rc36₊resistor36₊h₊Q_flow(t) + rc37₊resistor37₊h₊Q_flow(t) + rc38₊resistor38₊h₊Q_flow(t) + rc39₊resistor39₊h₊Q_flow(t) + rc3₊resistor3₊h₊Q_flow(t) + rc40₊resistor40₊h₊Q_flow(t) + rc41₊resistor41₊h₊Q_flow(t) + rc42₊resistor42₊h₊Q_flow(t) + rc43₊resistor43₊h₊Q_flow(t) + rc44₊resistor44₊h₊Q_flow(t) + rc45₊resistor45₊h₊Q_flow(t) + rc46₊resistor46₊h₊Q_flow(t) + rc47₊resistor47₊h₊Q_flow(t) + rc48₊resistor48₊h₊Q_flow(t) + rc49₊resistor49₊h₊Q_flow(t) + rc4₊resistor4₊h₊Q_flow(t) + rc50₊resistor50₊h₊Q_flow(t) + rc5₊resistor5₊h₊Q_flow(t) + rc6₊resistor6₊h₊Q_flow(t) + rc7₊resistor7₊h₊Q_flow(t) + rc8₊resistor8₊h₊Q_flow(t) + rc9₊resistor9₊h₊Q_flow(t)
177-
0 ~ rc1₊resistor1₊p₊i(t) + rc1₊source₊p₊i(t)
178-
rc1₊source₊p₊v(t) ~ rc1₊resistor1₊p₊v(t)
179-
0 ~ rc1₊capacitor1₊p₊i(t) + rc1₊resistor1₊n₊i(t)
180-
rc1₊resistor1₊n₊v(t) ~ rc1₊capacitor1₊p₊v(t)
181-
182-
rc50₊source₊V ~ rc50₊source₊p₊v(t) - rc50₊source₊n₊v(t)
183-
0 ~ rc50₊source₊n₊i(t) + rc50₊source₊p₊i(t)
184-
rc50₊ground₊g₊v(t) ~ 0
185-
Differential(t)(rc50₊heat_capacitor50₊h₊T(t)) ~ rc50₊heat_capacitor50₊h₊Q_flow(t)*(rc50₊heat_capacitor50₊V^-1)*(rc50₊heat_capacitor50₊cp^-1)*(rc50₊heat_capacitor50₊rho^-1)
150+
length(equations(big_rc))
151+
152+
801
186153
```
187154

188155
You see it started as a massive 1051 set of equations. However, after eliminating
@@ -211,7 +178,7 @@ investigate what this means:
211178

212179
```julia
213180
using ModelingToolkit.BipartiteGraphs
214-
big_rc = initialize_system_structure(big_rc)
181+
big_rc = initialize_system_structure(expand_connections(big_rc))
215182
inc_org = BipartiteGraphs.incidence_matrix(structure(big_rc).graph)
216183
blt_org = StructuralTransformations.sorted_incidence_matrix(big_rc, only_algeqs=true, only_algvars=true)
217184
blt_reduced = StructuralTransformations.sorted_incidence_matrix(sys, only_algeqs=true, only_algvars=true)

0 commit comments

Comments
 (0)