Skip to content
Closed
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
9 changes: 4 additions & 5 deletions models/models.toml
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,10 @@ reference_source = "MAP-LIB_ReferenceResults (Dymola, branch v3.2.3)"
# atol/reltol do not apply to it. Use solverReltol /
# solverAtol to loosen the integrator (e.g. near-singular
# Jacobians that force dt below eps under default tolerances).
# observedFilter = ["regex", ...] ([] = OM.simulate default)
# Retain alias/observed variables whose eliminated name matches
# any pattern through structural_simplify, so validation can read
# signals MTK would otherwise prune. Threaded to
# OM.simulate(...; observedFilter = [...]).
# observedFilter = ["regex", ...] ([] = no filter)
# Keep only the observed and alias variables whose name matches
# a pattern (with what they, the system and the events read).
# Threaded to OM.translate and OM.simulate(...; observedFilter = [...]).
#
# Use cases for initializealg:
# - DAE with rank-deficient algebraic Jacobian at t=0 (e.g. flow-state
Expand Down
3 changes: 1 addition & 2 deletions src/comparison.jl
Original file line number Diff line number Diff line change
Expand Up @@ -383,8 +383,7 @@ function validate_against_reference(sol, spec,
end
#= Every listed signal is compared: one the solution lacks fails the validation (it was
skipped, and a model validated on a subset: IdealGasH2O on 1 of 3 signals, MSL 4
CCCV_Stack on 1 of 13). The harness keeps the reference signals observable
(_reference_signal_filter). =#
CCCV_Stack on 1 of 13). =#
if n_skipped > 0
@warn "Validation: $n_skipped/$(length(signal_names)) reference signals are not in the solution"
end
Expand Down
45 changes: 5 additions & 40 deletions src/runner.jl
Original file line number Diff line number Diff line change
Expand Up @@ -285,40 +285,6 @@ function _resolve_solver(name::String)
return NamedTuple()
end

# Escape regex metacharacters so signal names containing `.`, `[`, `]`, etc.
# match literally inside OMBackend's `occursin(Regex(p), name)` filter.
_escape_regex(s::AbstractString) =
replace(s, r"[\\.\[\]\(\)\+\*\?\^\$\|]" => sm -> "\\" * sm)

# Read the reference CSV header and return regex patterns (anchored,
# underscore-form) for every non-`time` column so OMBackend's observedFilter
# preserves the alias-map entries the validate phase will look up.
function _reference_signal_filter(ref_dir::String,
reference_file::String,
signal_mapping::Dict{String, String})::Vector{String}
isempty(reference_file) && return String[]
local csv_path = joinpath(ref_dir, "csv", reference_file * ".csv")
isfile(csv_path) || return String[]
local header_line = ""
try
open(csv_path) do io; header_line = readline(io); end
catch
return String[]
end
local patterns = String[]
local seen = Set{String}()
for col in split_csv_header(header_line)
local raw = strip(col, ['"', ' ', '\t', '\r', '\n'])
(isempty(raw) || raw == "time") && continue
local under = haskey(signal_mapping, String(raw)) ? signal_mapping[String(raw)] : modelica_to_omjl_name(String(raw))
if !(under in seen)
push!(seen, under)
push!(patterns, string("^", _escape_regex(under), "\$"))
end
end
return patterns
end

#= Cap the cost of `showerror`. Some exceptions (notably MTK's
UnsolvableCallbackError / ExtraEquationsSystemException) render the entire
equation / callback system — O(system size) — which can take many minutes to
Expand Down Expand Up @@ -400,6 +366,10 @@ function _run_model_phases(model_name::String,

has_csv_ref = !isempty(reference_file)
has_inline_ref = !isempty(reference)
#= A registry filter goes to the translate and the simulate alike: a simulate by name with another filter
translates again (OM._reusableTranslation). The reference header's signals as the filter, passed to the
simulate only, never applied (the backend phase's translation was reused) and none was missing. =#
local _of::NamedTuple = isempty(observed_filter) ? NamedTuple() : (; observedFilter = observed_filter)

for phase_int in phase_ints
if phase_int == Int(VALIDATE) && !has_csv_ref && !has_inline_ref
Expand All @@ -415,19 +385,14 @@ function _run_model_phases(model_name::String,
if phase_int == Int(FRONTEND)
OM.flatten(model_name; msl...)
elseif phase_int == Int(BACKEND)
OM.translate(model_name; msl...,
OM.translate(model_name; msl..., _of...,
checkSimCode = check_sim_code)
elseif phase_int == Int(SIMULATE)
# Build kwargs: solver + dtmax (if non-zero) + initializealg (if specified).
# Per-model initializealg overrides any default from the solver mapping.
local _sa = isempty(solver_name) ? NamedTuple() : _resolve_solver(solver_name)
local _extra = dtmax > 0.0 ? (; dtmax = dtmax) : NamedTuple()
local _ia = isempty(init_alg) ? NamedTuple() : _resolve_init_alg(init_alg)
#= The reference's signals stay observable (the backend drops eliminated array
elements' aliases by default): a registry filter, else the reference header. =#
local _filter = isempty(observed_filter) ?
_reference_signal_filter(ref_dir, reference_file, signal_mapping) : observed_filter
local _of = isempty(_filter) ? NamedTuple() : (; observedFilter = _filter)
local _mi = (; maxiters = maxiters > 0.0 ? round(Int, maxiters) : DEFAULT_MAXITERS)
local _tol = NamedTuple()
if solver_reltol > 0.0
Expand Down
6 changes: 3 additions & 3 deletions src/types.jl
Original file line number Diff line number Diff line change
Expand Up @@ -80,9 +80,9 @@ struct ModelSpec
# validation tolerances for trajectory comparison. 0.0 = use SciML default.
solverAtol::Float64
solverReltol::Float64
# OMBackend observedFilter regex patterns. Non-empty: passed to OM.simulate to retain
# matching alias/observed variables through structural_simplify so validation can read
# them. Empty = OM.simulate default (no explicit retention).
# OMBackend observedFilter regex patterns. Non-empty: passed to OM.translate and OM.simulate
# (the translation's observed equations and alias observations: those the patterns match and
# what they, the system and the events read). Empty = no filter.
observedFilter::Vector{String}
# Hard cap on integrator iterations, passed to OM.simulate as `maxiters`. 0.0 = use
# the SciML default (1e5). A bounded value (e.g. 50000) lets a stiff / chattering
Expand Down
Loading