Skip to content

Commit 35117a9

Browse files
authored
Improve utils for v1 (#6)
* add new has_parameter * allow get var to be used with symbols as well * complete in the tests * add changelog * clarify src for new derived * allow has directly to models * add all_equations * ensure tracked variables are of type Num * we don't need ordinary diff eq in docs * better blocks * don't export core api
1 parent d625fff commit 35117a9

File tree

8 files changed

+77
-38
lines changed

8 files changed

+77
-38
lines changed

CHANGELOG.md

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
# Changelog
2+
3+
ProcessBasedModelling.jl follows semver 2.0.
4+
Changelog is kept with respect to v1 release.

docs/Project.toml

-1
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,3 @@ DocumenterCitations = "daee34ce-89f3-4625-b898-19384cb65244"
55
DocumenterTools = "35a29f4d-8980-5a13-9543-d66fff28ecb8"
66
DynamicalSystems = "61744808-ddfa-5f27-97ff-6e42cc95d634"
77
ModelingToolkit = "961ee093-0014-501f-94e3-6117800e7a78"
8-
OrdinaryDiffEq = "1dea7af3-3e70-54e6-95c3-0bf5283fa5ed"

docs/src/index.md

+11-6
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,6 @@ symbolically using ModelingToolkit.jl (**MTK**). We define
3333

3434
```@example MAIN
3535
using ModelingToolkit
36-
using OrdinaryDiffEq: Tsit5
3736
3837
@variables t # independent variable _without_ units
3938
@variables z(t) = 0.0
@@ -78,9 +77,10 @@ The error message is unhelpful as all variables are reported as "potentially mis
7877
At least on the basis of our scientific reasoning however, both ``x, z`` have an equation.
7978
It is ``y`` that ``x`` introduced that does not have an equation.
8079
Moreover, in our experience these error messages become increasingly less useful when a model has many equations and/or variables, as many variables get cited as "missing" from the variable map even when only one should be.
80+
This makes it difficult to quickly find out where the "mistake" happened in the equations.
8181

82-
**PBM** resolves these problems and always gives accurate error messages when it comes to
83-
the construction of the system of equations.
82+
**PBM** resolves these problems and always gives accurate error messages when
83+
it comes to the construction of the system of equations.
8484
This is because on top of the variable map that MTK constructs automatically, **PBM** requires the user to implicitly provide a map of variables to processes that govern said variables. **PBM** creates the map automatically, the only thing the user has to do is to define the equations in terms of what [`processes_to_mtkmodel`](@ref) wants (which are either [`Process`](@ref)es or `Equation`s as above).
8585

8686
Here is what the user defines to make the same system of equations via **PBM**:
@@ -199,12 +199,17 @@ ProcessBasedModelling.NoTimeDerivative
199199
ProcessBasedModelling.lhs
200200
```
201201

202-
## Utility functions
202+
## Automatic named parameters
203203

204204
```@docs
205-
default_value
206-
has_variable
207205
new_derived_named_parameter
208206
@convert_to_parameters
209207
LiteralParameter
210208
```
209+
210+
## Utility functions
211+
212+
```@docs
213+
default_value
214+
has_symbolic_var
215+
```

src/ProcessBasedModelling.jl

+3-2
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,9 @@ export t
2525
export Process, ParameterProcess, TimeDerivative, ExpRelaxation, AdditionProcess
2626
export processes_to_mtkmodel
2727
export new_derived_named_parameter
28-
export has_variable, default_value
28+
export has_symbolic_var, default_value
2929
export @convert_to_parameters, LiteralParameter
30-
export lhs_variable, rhs, lhs
30+
# export lhs_variable, rhs, lhs # I am not sure whether these should be exported.
31+
export all_equations
3132

3233
end

src/make.jl

+2-2
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,8 @@ function processes_to_mtkmodel(_processes, _default = [];
3333
processes = expand_multi_processes(_processes)
3434
default = default_dict(_default)
3535
# Setup: obtain lhs-variables so we can track new variables that are not
36-
# in this vector
37-
lhs_vars = map(lhs_variable, processes)
36+
# in this vector. The vector has to be of type `Num`
37+
lhs_vars = Num[lhs_variable(p) for p in processes]
3838
ensure_unique_vars(lhs_vars)
3939
# First pass: generate equations from given processes
4040
# and collect variables without equations

src/processes_basic.jl

+1-1
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ struct ParameterProcess <: Process
1717
variable
1818
value
1919
function ParameterProcess(variable, value)
20-
var = new_derived_named_parameter(variable, value, "0")
20+
var = new_derived_named_parameter(variable, value, "0"; prefix = false)
2121
return new(variable, var)
2222
end
2323
end

src/utils.jl

+42-13
Original file line numberDiff line numberDiff line change
@@ -14,22 +14,48 @@ _literalvalue(x) = x
1414
_literalvalue(p::LiteralParameter) = p.p
1515

1616
"""
17-
has_variable(eq, var)
17+
has_symbolic_var(eqs, var)
1818
19-
Return `true` if variable `var` exists in the equation(s) `eq`, `false` otherwise.
20-
Function works irrespectively if `var` is an `@variable` or `@parameter`.
19+
Return `true` if symbolic variable `var` exists in the equation(s) `eq`, `false` otherwise.
20+
This works for either `@parameters` or `@variables`.
21+
If `var` is a `Symbol` isntead of a `Num`, all variables are converted to their names
22+
and equality is checked on the basis of the name only.
23+
24+
has_symbolic_var(model, var)
25+
26+
When given a MTK model (such as `ODESystem`) search in _all_ the equations of the system,
27+
including observed variables.
2128
"""
22-
function has_variable(eq::Equation, var)
29+
function has_symbolic_var(eq::Equation, var)
2330
vars = get_variables(eq)
31+
return _has_thing(var, vars)
32+
end
33+
has_symbolic_var(eqs::Vector{Equation}, var) = any(eq -> has_symbolic_var(eq, var), eqs)
34+
has_symbolic_var(mtk, var) = has_symbolic_var(all_equations(mtk), var)
35+
36+
function _has_thing(var::Num, vars)
2437
return any(isequal(var), vars)
2538
end
26-
has_variable(eqs, var) = any(eq -> has_variable(eq, var), eqs)
39+
function _has_thing(var::Symbol, vars)
40+
vars = ModelingToolkit.getname.(vars)
41+
var = ModelingToolkit.getname(var)
42+
return any(isequal(var), vars)
43+
end
44+
45+
"""
46+
all_equations(model)
47+
48+
Equivalent with `vcat(equations(model), observed(model))`.
49+
"""
50+
all_equations(model) = vcat(equations(model), observed(model))
2751

2852
"""
2953
default_value(x)
3054
3155
Return the default value of a symbolic variable `x` or `nothing`
3256
if it doesn't have any. Return `x` if `x` is not a symbolic variable.
57+
The difference with `ModelingToolkit.getdefault` is that this function will
58+
not error on the absence of a default value.
3359
"""
3460
default_value(x) = x
3561
default_value(x::Num) = default_value(x.val)
@@ -64,11 +90,14 @@ end
6490
6591
If `value isa Num` return `value`.
6692
If `value isa LiteralParameter`, replace it with its literal value.
67-
Otherwise, create a new MTK `@parameter`
68-
whose name is created from `variable` (which could also be just a `Symbol`) by adding the `extra` string.
69-
If the keyword `prefix == false` the `extra` is added at the end after a `_`. Otherwise
70-
it is added at the start, then a `_` and then the variable name.
71-
The keyword `connector = "_"` is what connects the `extra` with the name.
93+
Otherwise, create a new MTK `@parameter` whose name is created from `variable`
94+
(which could also be just a `Symbol`) by adding the `extra` string.
95+
96+
**Keywords**:
97+
98+
- `prefix = true`: whether the `extra` is added at the start or the end, connecting
99+
with the with the `connector`.
100+
- `connector = "_"`: what to use to connect `extra` with the name.
72101
73102
74103
For example,
@@ -83,10 +112,10 @@ new_derived_named_parameter(v, value::Num, extra::String; kw...) = value
83112
new_derived_named_parameter(v, value::LiteralParameter, extra::String; kw...) = value.p
84113
function new_derived_named_parameter(v, value::Real, extra; connector = "_", prefix = true)
85114
n = string(ModelingToolkit.getname(v))
86-
newstring = if !(prefix)
87-
n*connector*extra
88-
else
115+
newstring = if prefix
89116
extra*connector*n
117+
else
118+
n*connector*extra
90119
end
91120
new_derived_named_parameter(newstring, value)
92121
end

test/runtests.jl

+14-13
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ using OrdinaryDiffEq
5555

5656
sys = structural_simplify(sys)
5757
@test length(unknowns(sys)) == 1
58-
@test has_variable(equations(sys), T)
58+
@test has_symbolic_var(equations(sys), T)
5959

6060
u0s = [[300.0], [100.0]]
6161
ufs = []
@@ -81,7 +81,7 @@ using OrdinaryDiffEq
8181

8282
sys = structural_simplify(sys)
8383
@test length(unknowns(sys)) == 1
84-
@test has_variable(equations(sys), T)
84+
@test has_symbolic_var(equations(sys), T)
8585

8686
end
8787

@@ -160,12 +160,9 @@ end
160160
@test p == 0.5
161161
end
162162

163-
164-
@testset "new named var"
165-
166163
end
167164

168-
@testset "default processes" begin
165+
@testset "default processes, has_thing" begin
169166
@variables x(t) = 0.5
170167
@variables y(t) = 0.5
171168
@variables z(t) = 0.5
@@ -174,15 +171,19 @@ end
174171
processes = [
175172
TimeDerivative(x, x^2, 1.2),
176173
ParameterProcess(y),
177-
ExpRelaxation(z, x^2),
174+
ExpRelaxation(z, x^2, 0.5),
178175
AdditionProcess(ParameterProcess(w), x^2),
179176
AdditionProcess(TimeDerivative(q, x^2, 1.2), ExpRelaxation(q, x^2))
180177
]
181178
mtk = processes_to_mtkmodel(processes)
182-
eqs = equations(mtk)
183-
@test has_variable(eqs, x)
184-
@test has_variable(eqs, y)
185-
@test has_variable(eqs, z)
186-
@test has_variable(eqs, w)
187-
@test has_variable(eqs, q)
179+
mtk = structural_simplify(mtk)
180+
eqs = all_equations(mtk)
181+
@test has_symbolic_var(eqs, x)
182+
@test has_symbolic_var(eqs, y)
183+
@test has_symbolic_var(eqs, :z)
184+
@test has_symbolic_var(eqs, mtk.w)
185+
@test has_symbolic_var(eqs, q)
186+
@test has_symbolic_var(mtk, q)
187+
@test has_symbolic_var(eqs, mtk.τ_z)
188+
@test has_symbolic_var(eqs, :w_0)
188189
end

0 commit comments

Comments
 (0)