Skip to content

Commit ee6a7e5

Browse files
authored
Merge branch 'SciML:master' into iss3707
2 parents 90534fa + f0f412b commit ee6a7e5

File tree

10 files changed

+118
-32
lines changed

10 files changed

+118
-32
lines changed

Project.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
name = "ModelingToolkit"
22
uuid = "961ee093-0014-501f-94e3-6117800e7a78"
33
authors = ["Yingbo Ma <[email protected]>", "Chris Rackauckas <[email protected]> and contributors"]
4-
version = "10.6.0"
4+
version = "10.7.0"
55

66
[deps]
77
ADTypes = "47edcb42-4c32-4615-8424-f2b9edc5f35b"
@@ -106,7 +106,7 @@ DiffEqBase = "6.170.1"
106106
DiffEqCallbacks = "2.16, 3, 4"
107107
DiffEqNoiseProcess = "5"
108108
DiffRules = "0.1, 1.0"
109-
DifferentiationInterface = "0.6.47"
109+
DifferentiationInterface = "0.6.47, 0.7"
110110
Distributed = "1"
111111
Distributions = "0.23, 0.24, 0.25"
112112
DocStringExtensions = "0.7, 0.8, 0.9"

docs/src/tutorials/fmi.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ We can interpolate the solution object to obtain values at arbitrary time points
8585
just like a normal solution.
8686

8787
```@repl fmi
88-
sol(0.0:0.1:1.0; idxs = sys.mass_a)
88+
sol(0.0:0.1:1.0; idxs = sys.mass__a)
8989
```
9090

9191
FMUs following version 3 of the specification can be simulated with almost the same process. This time,

src/linearization.jl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -174,13 +174,13 @@ struct PreparedJacobian{iip, P, F, B, A}
174174
end
175175

176176
function PreparedJacobian{true}(f, buf, autodiff, args...)
177-
prep = DI.prepare_jacobian(f, buf, autodiff, args...)
177+
prep = DI.prepare_jacobian(f, buf, autodiff, args...; strict = Val(false))
178178
return PreparedJacobian{true, typeof(prep), typeof(f), typeof(buf), typeof(autodiff)}(
179179
prep, f, buf, autodiff)
180180
end
181181

182182
function PreparedJacobian{false}(f, autodiff, args...)
183-
prep = DI.prepare_jacobian(f, autodiff, args...)
183+
prep = DI.prepare_jacobian(f, autodiff, args...; strict = Val(false))
184184
return PreparedJacobian{true, typeof(prep), typeof(f), Nothing, typeof(autodiff)}(
185185
prep, f, nothing)
186186
end

src/systems/abstractsystem.jl

Lines changed: 22 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -820,6 +820,9 @@ function refreshed_metadata(meta::Base.ImmutableDict)
820820
end
821821
newmeta = Base.ImmutableDict(newmeta, k => v)
822822
end
823+
if !haskey(newmeta, MutableCacheKey)
824+
newmeta = Base.ImmutableDict(newmeta, MutableCacheKey => MutableCacheT())
825+
end
823826
return newmeta
824827
end
825828

@@ -2683,22 +2686,27 @@ function Symbolics.substitute(sys::AbstractSystem, rules::Union{Vector{<:Pair},
26832686
elseif sys isa System
26842687
rules = todict(map(r -> Symbolics.unwrap(r[1]) => Symbolics.unwrap(r[2]),
26852688
collect(rules)))
2686-
eqs = fast_substitute(get_eqs(sys), rules)
2687-
pdeps = fast_substitute(get_parameter_dependencies(sys), rules)
2688-
defs = Dict(fast_substitute(k, rules) => fast_substitute(v, rules)
2689+
newsys = @set sys.eqs = fast_substitute(get_eqs(sys), rules)
2690+
@set! newsys.unknowns = map(get_unknowns(sys)) do var
2691+
get(rules, var, var)
2692+
end
2693+
@set! newsys.ps = map(get_ps(sys)) do var
2694+
get(rules, var, var)
2695+
end
2696+
@set! newsys.parameter_dependencies = fast_substitute(
2697+
get_parameter_dependencies(sys), rules)
2698+
@set! newsys.defaults = Dict(fast_substitute(k, rules) => fast_substitute(v, rules)
26892699
for (k, v) in get_defaults(sys))
2690-
guess = Dict(fast_substitute(k, rules) => fast_substitute(v, rules)
2700+
@set! newsys.guesses = Dict(fast_substitute(k, rules) => fast_substitute(v, rules)
26912701
for (k, v) in get_guesses(sys))
2692-
noise_eqs = fast_substitute(get_noise_eqs(sys), rules)
2693-
costs = fast_substitute(get_costs(sys), rules)
2694-
observed = fast_substitute(get_observed(sys), rules)
2695-
initialization_eqs = fast_substitute(get_initialization_eqs(sys), rules)
2696-
cstrs = fast_substitute(get_constraints(sys), rules)
2697-
subsys = map(s -> substitute(s, rules), get_systems(sys))
2698-
newsys = System(eqs, get_iv(sys); name = nameof(sys), defaults = defs,
2699-
guesses = guess, systems = subsys, noise_eqs,
2700-
observed, initialization_eqs, constraints = cstrs)
2701-
@set! newsys.parameter_dependencies = pdeps
2702+
@set! newsys.noise_eqs = fast_substitute(get_noise_eqs(sys), rules)
2703+
@set! newsys.costs = Vector{Union{Real, BasicSymbolic}}(fast_substitute(
2704+
get_costs(sys), rules))
2705+
@set! newsys.observed = fast_substitute(get_observed(sys), rules)
2706+
@set! newsys.initialization_eqs = fast_substitute(
2707+
get_initialization_eqs(sys), rules)
2708+
@set! newsys.constraints = fast_substitute(get_constraints(sys), rules)
2709+
@set! newsys.systems = map(s -> substitute(s, rules), get_systems(sys))
27022710
else
27032711
error("substituting symbols is not supported for $(typeof(sys))")
27042712
end

src/systems/nonlinear/initializesystem.jl

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ end
4141
"""
4242
$(TYPEDSIGNATURES)
4343
44-
Generate `System` of nonlinear equations which initializes a problem from specified initial conditions of an `AbstractTimeDependentSystem`.
44+
Generate `System` of nonlinear equations which initializes a problem from specified initial conditions of a time-dependent `AbstractSystem`.
4545
"""
4646
function generate_initializesystem_timevarying(sys::AbstractSystem;
4747
op = Dict(),
@@ -71,7 +71,6 @@ function generate_initializesystem_timevarying(sys::AbstractSystem;
7171
eqs_ics = Equation[]
7272
defs = copy(defaults(sys)) # copy so we don't modify sys.defaults
7373
additional_guesses = anydict(guesses)
74-
additional_initialization_eqs = Vector{Equation}(initialization_eqs)
7574
guesses = merge(get_guesses(sys), additional_guesses)
7675
idxs_diff = isdiffeq.(eqs)
7776

@@ -190,11 +189,13 @@ function generate_initializesystem_timevarying(sys::AbstractSystem;
190189
push!(pars, get_iv(sys))
191190

192191
# 8) use observed equations for guesses of observed variables if not provided
192+
guessed = Set(keys(defs)) # x(t), D(x(t)), ...
193+
guessed = union(guessed, Set(default_toterm.(guessed))) # x(t), D(x(t)), xˍt(t), ...
193194
for eq in trueobs
194-
haskey(defs, eq.lhs) && continue
195-
any(x -> isequal(default_toterm(x), eq.lhs), keys(defs)) && continue
196-
197-
defs[eq.lhs] = eq.rhs
195+
if !(eq.lhs in guessed)
196+
defs[eq.lhs] = eq.rhs
197+
#push!(guessed, eq.lhs) # should not encounter eq.lhs twice, so don't need to track it
198+
end
198199
end
199200
append!(eqs_ics, trueobs)
200201

@@ -216,7 +217,7 @@ end
216217
"""
217218
$(TYPEDSIGNATURES)
218219
219-
Generate `System` of nonlinear equations which initializes a problem from specified initial conditions of an `AbstractTimeDependentSystem`.
220+
Generate `System` of nonlinear equations which initializes a problem from specified initial conditions of a time-independent `AbstractSystem`.
220221
"""
221222
function generate_initializesystem_timeindependent(sys::AbstractSystem;
222223
op = Dict(),
@@ -228,12 +229,10 @@ function generate_initializesystem_timeindependent(sys::AbstractSystem;
228229
eqs = equations(sys)
229230
trueobs, eqs = unhack_observed(observed(sys), eqs)
230231
vars = unique([unknowns(sys); getfield.(trueobs, :lhs)])
231-
vars_set = Set(vars) # for efficient in-lookup
232232

233233
eqs_ics = Equation[]
234234
defs = copy(defaults(sys)) # copy so we don't modify sys.defaults
235235
additional_guesses = anydict(guesses)
236-
additional_initialization_eqs = Vector{Equation}(initialization_eqs)
237236
guesses = merge(get_guesses(sys), additional_guesses)
238237

239238
# PREPROCESSING
@@ -738,6 +737,7 @@ function SciMLBase.late_binding_update_u0_p(
738737
end
739738
newp = setp_oop(sys, syms)(newp, vals)
740739
else
740+
allsyms = nothing
741741
# if `p` is not provided or is symbolic
742742
p === missing || eltype(p) <: Pair || return newu0, newp
743743
(newu0 === nothing || isempty(newu0)) && return newu0, newp
@@ -755,6 +755,9 @@ function SciMLBase.late_binding_update_u0_p(
755755
if eltype(p) <: Pair
756756
syms = []
757757
vals = []
758+
if allsyms === nothing
759+
allsyms = all_symbols(sys)
760+
end
758761
for (k, v) in p
759762
v === nothing && continue
760763
(symbolic_type(v) == NotSymbolic() && !is_array_of_symbolics(v)) || continue

src/systems/system.jl

Lines changed: 50 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -411,7 +411,7 @@ function System(eqs::Vector{Equation}, iv, dvs, ps, brownians = [];
411411
end
412412
metadata = meta
413413
end
414-
metadata = Base.ImmutableDict(metadata, MutableCacheKey => MutableCacheT())
414+
metadata = refreshed_metadata(metadata)
415415
System(Threads.atomic_add!(SYSTEM_COUNT, UInt(1)), eqs, noise_eqs, jumps, constraints,
416416
costs, consolidate, dvs, ps, brownians, iv, observed, Equation[],
417417
var_to_name, name, description, defaults, guesses, systems, initialization_eqs,
@@ -1097,3 +1097,52 @@ function supports_initialization(sys::System)
10971097
return isempty(jumps(sys)) && _iszero(cost(sys)) &&
10981098
isempty(constraints(sys))
10991099
end
1100+
1101+
safe_eachrow(::Nothing) = nothing
1102+
safe_eachrow(x::AbstractArray) = eachrow(x)
1103+
1104+
safe_issetequal(::Nothing, ::Nothing) = true
1105+
safe_issetequal(::Nothing, x) = false
1106+
safe_issetequal(x, ::Nothing) = false
1107+
safe_issetequal(x, y) = issetequal(x, y)
1108+
1109+
"""
1110+
$(TYPEDSIGNATURES)
1111+
1112+
Check if two systems are about equal, to the extent that ModelingToolkit.jl supports. Note
1113+
that if this returns `true`, the systems are not guaranteed to be exactly equivalent
1114+
(unless `sysa === sysb`) but are highly likely to represent a similar mathematical problem.
1115+
If this returns `false`, the systems are very likely to be different.
1116+
"""
1117+
function Base.isapprox(sysa::System, sysb::System)
1118+
sysa === sysb && return true
1119+
return nameof(sysa) == nameof(sysb) &&
1120+
isequal(get_iv(sysa), get_iv(sysb)) &&
1121+
issetequal(get_eqs(sysa), get_eqs(sysb)) &&
1122+
safe_issetequal(
1123+
safe_eachrow(get_noise_eqs(sysa)), safe_eachrow(get_noise_eqs(sysb))) &&
1124+
issetequal(get_jumps(sysa), get_jumps(sysb)) &&
1125+
issetequal(get_constraints(sysa), get_constraints(sysb)) &&
1126+
issetequal(get_costs(sysa), get_costs(sysb)) &&
1127+
isequal(get_consolidate(sysa), get_consolidate(sysb)) &&
1128+
issetequal(get_unknowns(sysa), get_unknowns(sysb)) &&
1129+
issetequal(get_ps(sysa), get_ps(sysb)) &&
1130+
issetequal(get_brownians(sysa), get_brownians(sysb)) &&
1131+
issetequal(get_observed(sysa), get_observed(sysb)) &&
1132+
issetequal(get_parameter_dependencies(sysa), get_parameter_dependencies(sysb)) &&
1133+
isequal(get_description(sysa), get_description(sysb)) &&
1134+
isequal(get_defaults(sysa), get_defaults(sysb)) &&
1135+
isequal(get_guesses(sysa), get_guesses(sysb)) &&
1136+
issetequal(get_initialization_eqs(sysa), get_initialization_eqs(sysb)) &&
1137+
issetequal(get_continuous_events(sysa), get_continuous_events(sysb)) &&
1138+
issetequal(get_discrete_events(sysa), get_discrete_events(sysb)) &&
1139+
isequal(get_connector_type(sysa), get_connector_type(sysb)) &&
1140+
isequal(get_assertions(sysa), get_assertions(sysb)) &&
1141+
isequal(get_metadata(sysa), get_metadata(sysb)) &&
1142+
isequal(get_is_dde(sysa), get_is_dde(sysb)) &&
1143+
issetequal(get_tstops(sysa), get_tstops(sysb)) &&
1144+
safe_issetequal(get_ignored_connections(sysa), get_ignored_connections(sysb)) &&
1145+
isequal(get_is_initializesystem(sysa), get_is_initializesystem(sysb)) &&
1146+
isequal(get_is_discrete(sysa), get_is_discrete(sysb)) &&
1147+
isequal(get_isscheduled(sysa), get_isscheduled(sysb))
1148+
end

src/systems/systemstructure.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -431,7 +431,7 @@ function TearingState(sys; quick_cancel = false, check = true, sort_eqs = true)
431431
if sort_eqs
432432
# sort equations lexicographically to reduce simplification issues
433433
# depending on order due to NP-completeness of tearing.
434-
sortidxs = Base.sortperm(eqs, by = string)
434+
sortidxs = Base.sortperm(string.(eqs)) # "by = string" creates more strings
435435
eqs = eqs[sortidxs]
436436
original_eqs = original_eqs[sortidxs]
437437
symbolic_incidence = symbolic_incidence[sortidxs]

src/utils.jl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -606,7 +606,7 @@ end
606606
"""
607607
$(TYPEDSIGNATURES)
608608
609-
Indicate whether the given equation type (Equation, Pair, etc) supports `collect_vars!`.
609+
Indicate whether the given equation type (Equation, Pair, etc) supports `collect_vars!`.
610610
Can be dispatched by higher-level libraries to indicate support.
611611
"""
612612
eqtype_supports_collect_vars(eq) = false
@@ -791,7 +791,7 @@ Check if `T` is an appropriate symtype for a symbolic variable representing a fl
791791
point number or array of such numbers.
792792
"""
793793
function is_floatingpoint_symtype(T::Type)
794-
return T == Real || T == Number || T <: AbstractFloat ||
794+
return T == Real || T == Number || T == Complex || T <: AbstractFloat ||
795795
T <: AbstractArray && is_floatingpoint_symtype(eltype(T))
796796
end
797797

test/odesystem.jl

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1574,3 +1574,28 @@ end
15741574
prob = ODEProblem(sys, [x => 1.0], (0.0, 1.0))
15751575
@test prob.problem_type == "A"
15761576
end
1577+
1578+
@testset "`substitute` retains events and metadata" begin
1579+
@parameters p(t) = 1.0
1580+
@variables x(t) = 0.0
1581+
event = [0.5] => [p ~ Pre(t)]
1582+
event2 = [x ~ 0.75] => [p ~ 2 * Pre(t)]
1583+
1584+
struct TestMeta end
1585+
1586+
eq = [
1587+
D(x) ~ p
1588+
]
1589+
@named sys = System(eq, t, [x], [p], discrete_events = [event],
1590+
continuous_events = [event2], metadata = Dict(TestMeta => "test"))
1591+
1592+
@variables x2(t) = 0.0
1593+
sys2 = substitute(sys, [x => x2])
1594+
1595+
@test length(ModelingToolkit.get_discrete_events(sys)) == 1
1596+
@test length(ModelingToolkit.get_discrete_events(sys2)) == 1
1597+
@test length(ModelingToolkit.get_continuous_events(sys)) == 1
1598+
@test length(ModelingToolkit.get_continuous_events(sys2)) == 1
1599+
@test getmetadata(sys, TestMeta, nothing) == "test"
1600+
@test getmetadata(sys2, TestMeta, nothing) == "test"
1601+
end

test/serialization.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ str = String(take!(io))
2828

2929
sys = include_string(@__MODULE__, str)
3030
rc2 = expand_connections(rc_model)
31+
@test isapprox(sys, rc2)
3132
@test issetequal(equations(sys), equations(rc2))
3233
@test issetequal(unknowns(sys), unknowns(rc2))
3334
@test issetequal(parameters(sys), parameters(rc2))

0 commit comments

Comments
 (0)