Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
11 changes: 9 additions & 2 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@
- Source systems must list scalar unknowns (scalarized array elements or `mtkcompile`
output). A `System` built without explicit unknowns collects both an array variable and
its indexed elements, which no problem constructor accepts either.
- Both `DAEProblem(rom, nothing, tspan; build_initializeprob = false)` and
`ODEProblem(mtkcompile(rom), nothing, tspan; build_initializeprob = false)` are supported
- Both `DAEProblem(rom, nothing; build_initializeprob = false)` and
`ODEProblem(mtkcompile(rom), nothing; build_initializeprob = false)` are supported
and tested. Reconstruct fields with `SymbolicIndexingInterface.observed`; the `sol[field]`
convenience path hits https://github.com/SciML/ModelingToolkit.jl/issues/5072.
- The reduced equation is array linear algebra: projected matrices, the stencil rows of the
Expand All @@ -41,3 +41,10 @@
collide with a name a user typed. Collisions with names inherited from the source system,
which happens when reducing an already-reduced model, are resolved by appending a counter.
Closes https://github.com/SciML/ModelOrderReduction.jl/issues/28.
- The reduced system inherits a time span, for both `deim` and `pod`: from `prob.tspan` for
the problem method, and from `get_tspan(sys)` for the system method. A source without a
time span must yield a reduced system without one rather than a fabricated interval, and a
new reduction entry point has to thread `tspan` into `_assemble_reduced_system` the same
way. This needs the `tspan` field on `ModelingToolkit.System`, added in
ModelingToolkitBase 1.69.0 and guaranteed only from ModelingToolkit 11.42.0.
Closes https://github.com/SciML/ModelOrderReduction.jl/issues/29.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ sol = solve(full_prob)

# POD Galerkin: one array reduced equation, O(1) symbolic residual in the grid size
rom = pod(full_prob, sol, 4)
rom_prob = DAEProblem(rom, nothing, full_prob.tspan; build_initializeprob = false)
rom_prob = DAEProblem(rom, nothing; build_initializeprob = false)
rom_sol = solve(rom_prob)
z_approx = [SII.observed(rom, z)(u, rom_prob.p, tt) for (u, tt) in zip(rom_sol.u, rom_sol.t)]
```
Expand Down
22 changes: 16 additions & 6 deletions src/pod.jl
Original file line number Diff line number Diff line change
Expand Up @@ -307,7 +307,8 @@ end

function _pod(
sys::System, snapshot::AbstractMatrix, pod_dim::Integer, name::Symbol;
snapshot_times = nothing, training_parameters = Dict{Any, Any}(), kwargs...
snapshot_times = nothing, training_parameters = Dict{Any, Any}(),
tspan = nothing, kwargs...
)
rows = length(ModelingToolkit.unknowns(sys))
size(snapshot, 1) == rows || throw(
Expand Down Expand Up @@ -339,7 +340,7 @@ function _pod(
)
return _assemble_reduced_system(
fom, snapshot, state_basis, reduced_state, rhs, array_parameters,
array_values, taken, name, initial_derivative; kwargs...
array_values, taken, name, initial_derivative; tspan, kwargs...
)
end
end
Expand All @@ -353,7 +354,7 @@ function _pod(
)
return _assemble_reduced_system(
fom, snapshot, state_basis, reduced_state, rhs, array_parameters, array_values,
taken, name, initial_derivative; kwargs...
taken, name, initial_derivative; tspan, kwargs...
)
end

Expand Down Expand Up @@ -387,6 +388,10 @@ needed. The returned system always has one array differential equation for the r
state and one reconstruction observed equation per source field. Unknowns eliminated during
simplification are reconstructed by a least-squares fit to `snapshot`.

The reduced system inherits the time span of `sys`, so a problem can be built from it
without repeating one. Passing a time span explicitly still overrides it, and a source
system without one produces a reduced system without one.

Construct a `DAEProblem` with `build_initializeprob = false` from the returned system to
keep array code generation, or call `ModelingToolkit.mtkcompile` on it and construct an
`ODEProblem` to generate scalar code for the reduced equation. Reconstruct fields with
Expand Down Expand Up @@ -421,7 +426,10 @@ function pod(
sys::System, snapshot::AbstractMatrix, pod_dim::Integer;
name::Symbol = Symbol(nameof(sys), :_pod), snapshot_times = nothing, kwargs...
)::System
return _pod(sys, snapshot, pod_dim, name; snapshot_times, kwargs...)
return _pod(
sys, snapshot, pod_dim, name;
snapshot_times, tspan = ModelingToolkit.get_tspan(sys), kwargs...
)
end

"""
Expand All @@ -440,6 +448,8 @@ saved solutions `sol` as the training snapshot.
`DAEProblem` returned by MethodOfLines. The saved states are the snapshot columns, the
saved times supply the independent variable for time-dependent terms, and the parameter
values of `prob` are used for training and become the defaults of the reduced system.
The reduced system also inherits the time span of `prob`, which is the interval it was
trained on, so a problem can be built from it without repeating one.
`sol` may be the `SciMLBase.PDETimeSeriesSolution` returned by MethodOfLines or its
underlying `SciMLBase.AbstractODESolution`. See the system method for the reduction itself
and for how to construct problems from the returned system.
Expand Down Expand Up @@ -469,7 +479,7 @@ full_problem = discretize(pde_system, discretization; fallback = false)
full_solution = solve(full_problem)
reduced_system = pod(full_problem, full_solution, 4)
reduced_problem = DAEProblem(
reduced_system, nothing, full_problem.tspan; build_initializeprob = false
reduced_system, nothing; build_initializeprob = false
)
```
"""
Expand All @@ -496,7 +506,7 @@ function pod(
reduced_name = isnothing(name) ? Symbol(nameof(sys), :_pod) : name
return _pod(
sys, snapshot, pod_dim, reduced_name;
snapshot_times = sol.t, training_parameters, kwargs...
snapshot_times = sol.t, training_parameters, tspan = prob.tspan, kwargs...
)
end

Expand Down
7 changes: 3 additions & 4 deletions test/deim.jl
Original file line number Diff line number Diff line change
Expand Up @@ -71,10 +71,9 @@ reconstruction_parameters = filter(ModelingToolkit.get_ps(deim_sys)) do paramete
end
@test length(reconstruction_parameters) == 1

deim_prob = DAEProblem(
deim_sys, nothing, full_prob.tspan;
build_initializeprob = false,
)
# No time span here: the reduced system carries the one it was trained on.
deim_prob = DAEProblem(deim_sys, nothing; build_initializeprob = false)
@test deim_prob.tspan == full_prob.tspan

deim_sol = solve(deim_prob; saveat = 1.0)
@test successful_retcode(deim_sol)
Expand Down
25 changes: 25 additions & 0 deletions test/pod.jl
Original file line number Diff line number Diff line change
Expand Up @@ -128,3 +128,28 @@ end
)
@test norm(reconstruction - truth) / norm(truth) < 0.01
end

@testset "the POD reduced system carries a tspan" begin
@variables g(t)[1:6]
g0 = [0.4, 0.3, 0.2, 0.1, -0.2, 0.5]
gstates = ModelingToolkit.Symbolics.unwrap.(ModelingToolkit.Symbolics.scalarize(g))
span = (0.0, 0.5)
@named source = System(
[D(g) ~ -g - g .^ 3], t, gstates, [];
initial_conditions = [g => g0, D(g) => -g0 - g0 .^ 3],
)
source_problem = DAEProblem(complete(source), nothing, span; build_initializeprob = false)
source_solution = solve(source_problem; saveat = 0.05, abstol = 1.0e-10, reltol = 1.0e-10)
@test successful_retcode(source_solution)

from_problem = pod(source_problem, source_solution, 3)
@test ModelingToolkit.get_tspan(from_problem) == span
@test DAEProblem(from_problem, nothing; build_initializeprob = false).tspan == span

snapshot = [sin(0.3 * i * j) + cos(0.2 * i * (j + 1)) for i in 1:6, j in 1:8]
@mtkcompile with_span = System([D(g) ~ -g - g .^ 3], t; tspan = span)
@test ModelingToolkit.get_tspan(pod(with_span, snapshot, 2)) == span

@mtkcompile without_span = System([D(g) ~ -g - g .^ 3], t)
@test ModelingToolkit.get_tspan(pod(without_span, snapshot, 2)) === nothing
end
Loading