Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
80 changes: 80 additions & 0 deletions src/systems/abstractsystem.jl
Original file line number Diff line number Diff line change
Expand Up @@ -3285,6 +3285,86 @@ function dump_unknowns(sys::AbstractSystem)
end
end

"""
$(TYPEDSIGNATURES)

Return the variable in `sys` referred to by its string representation `str`.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

add it to the docs

Roughly supports the following CFG:

```
varname = "D(" varname ")" | arrvar | maybe_dummy_var
arrvar = maybe_dummy_var "[idxs...]"
idxs = int | int "," idxs
maybe_dummy_var = namespacedvar | namespacedvar "(t)" |
namespacedvar "(t)" "ˍ" ts | namespacedvar "ˍ" ts |
namespacedvar "ˍ" ts "(t)"
ts = "t" | "t" ts
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nameof(independent_variable(sys))?

namespacedvar = ident "₊" namespacedvar | ident "." namespacedvar | ident
```
"""
function parse_variable(sys::AbstractSystem, str::AbstractString)
# I'd write a regex to validate `str`, but https://xkcd.com/1171/
str = strip(str)
derivative_level = 0
while startswith(str, "D(") && endswith(str, ")")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We may need to validate that D is also the differential?

Copy link
Member Author

@AayushSabharwal AayushSabharwal Nov 20, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

D never shows up in expressions, it always prints as Differential(t)(x(t)). In the interest of concision, I assumed that D refers to the derivative with respect to the independent variable. I could also add a case that handles Differential(t)(x(t)) and validates that the independent variable matches?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay

derivative_level += 1
str = _string_view_inner(str, 2, 1)
end

arr_idxs = nothing
if endswith(str, ']')
open_idx = only(findfirst('[', str))
idxs_range = nextind(str, open_idx):prevind(str, lastindex(str))
idxs_str = view(str, idxs_range)
str = view(str, firstindex(str):prevind(str, open_idx))
arr_idxs = map(Base.Fix1(parse, Int), eachsplit(idxs_str, ","))
end

if endswith(str, "(t)")
str = _string_view_inner(str, 0, 3)
end

dummyderivative_level = 0
if (dd_idx = findfirst('ˍ', str)) !== nothing
t_idx = nextind(str, dd_idx)
while checkbounds(Bool, str, t_idx)
if str[t_idx] != 't'
throw(ArgumentError("Dummy derivative must be 'ˍ' followed by one or more 't'."))
end
dummyderivative_level += 1
t_idx = nextind(str, t_idx)
end
str = view(str, firstindex(str):prevind(str, dd_idx))
end

if endswith(str, "(t)")
str = _string_view_inner(str, 0, 3)
end

cur = sys
for ident in eachsplit(str, ('.', NAMESPACE_SEPARATOR))
ident = Symbol(ident)
hasproperty(cur, ident) ||
throw(ArgumentError("System $(nameof(cur)) does not have a subsystem/variable named $(ident)"))
cur = getproperty(cur, ident)
end

if arr_idxs !== nothing
cur = cur[arr_idxs...]
end

for i in 1:(derivative_level + dummyderivative_level)
cur = Differential(get_iv(sys))(cur)
end

return cur
end

function _string_view_inner(str, startoffset, endoffset)
view(str,
nextind(str, firstindex(str), startoffset):prevind(str, lastindex(str), endoffset))
end

### Functions for accessing algebraic/differential equations in systems ###

"""
Expand Down
99 changes: 98 additions & 1 deletion test/variable_utils.jl
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using ModelingToolkit, Test
using ModelingToolkit: value, vars
using ModelingToolkit: value, vars, parse_variable
using SymbolicUtils: <ₑ

@parameters α β δ
expr = (((1 / β - 1) + δ) / α)^(1 / (α - 1))
ref = sort([β, δ, α], lt = <ₑ)
Expand Down Expand Up @@ -41,3 +42,99 @@ ts = collect_ivs([eq])
res = vars(fn([x, y], z))
@test length(res) == 3
end

@testset "parse_variable" begin
@mtkmodel Lorenz begin
@variables begin
x(t)
y(t)
z(t)
end
@parameters begin
σ
ρ
β
end
@equations begin
D(D(x)) ~ σ * (y - x)
D(y) ~ x * (ρ - z) - y
D(z) ~ x * y - β * z
end
end
@mtkmodel ArrSys begin
@variables begin
x(t)[1:2]
end
@parameters begin
p[1:2, 1:2]
end
@equations begin
D(D(x)) ~ p * x
end
end
@mtkmodel Outer begin
@components begin
😄 = Lorenz()
arr = ArrSys()
end
end

@mtkbuild sys = Outer()
for (str, var) in [
# unicode system, scalar variable
("😄.x", sys.😄.x),
("😄.x(t)", sys.😄.x),
("😄₊x", sys.😄.x),
("😄₊x(t)", sys.😄.x),
# derivative
("D(😄.x)", D(sys.😄.x)),
("D(😄.x(t))", D(sys.😄.x)),
("D(😄₊x)", D(sys.😄.x)),
("D(😄₊x(t))", D(sys.😄.x)),
# other derivative
("😄.xˍt", D(sys.😄.x)),
("😄.x(t)ˍt", D(sys.😄.x)),
("😄₊xˍt", D(sys.😄.x)),
("😄₊x(t)ˍt", D(sys.😄.x)),
# scalar parameter
("😄.σ", sys.😄.σ),
("😄₊σ", sys.😄.σ),
# array variable
("arr.x", sys.arr.x),
("arr₊x", sys.arr.x),
("arr.x(t)", sys.arr.x),
("arr₊x(t)", sys.arr.x),
# getindex
("arr.x[1]", sys.arr.x[1]),
("arr₊x[1]", sys.arr.x[1]),
("arr.x(t)[1]", sys.arr.x[1]),
("arr₊x(t)[1]", sys.arr.x[1]),
# derivative
("D(arr.x(t))", D(sys.arr.x)),
("D(arr₊x(t))", D(sys.arr.x)),
("D(arr.x[1])", D(sys.arr.x[1])),
("D(arr₊x[1])", D(sys.arr.x[1])),
("D(arr.x(t)[1])", D(sys.arr.x[1])),
("D(arr₊x(t)[1])", D(sys.arr.x[1])),
# other derivative
("arr.xˍt", D(sys.arr.x)),
("arr₊xˍt", D(sys.arr.x)),
("arr.xˍt(t)", D(sys.arr.x)),
("arr₊xˍt(t)", D(sys.arr.x)),
("arr.xˍt[1]", D(sys.arr.x[1])),
("arr₊xˍt[1]", D(sys.arr.x[1])),
("arr.xˍt(t)[1]", D(sys.arr.x[1])),
("arr₊xˍt(t)[1]", D(sys.arr.x[1])),
("arr.x(t)ˍt", D(sys.arr.x)),
("arr₊x(t)ˍt", D(sys.arr.x)),
("arr.x(t)ˍt[1]", D(sys.arr.x[1])),
("arr₊x(t)ˍt[1]", D(sys.arr.x[1])),
# array parameter
("arr.p", sys.arr.p),
("arr₊p", sys.arr.p),
("arr.p[1, 2]", sys.arr.p[1, 2]),
("arr₊p[1, 2]", sys.arr.p[1, 2])
]
isequal(parse_variable(sys, str), var)
end
end
Loading