diff --git a/models/models.toml b/models/models.toml index 9479ee9..6cc4c47 100644 --- a/models/models.toml +++ b/models/models.toml @@ -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 diff --git a/src/comparison.jl b/src/comparison.jl index f2e7de0..eb2ef8e 100644 --- a/src/comparison.jl +++ b/src/comparison.jl @@ -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 diff --git a/src/runner.jl b/src/runner.jl index 6b75ccf..2ff2abe 100644 --- a/src/runner.jl +++ b/src/runner.jl @@ -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 @@ -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 @@ -415,7 +385,7 @@ 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). @@ -423,11 +393,6 @@ function _run_model_phases(model_name::String, 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 diff --git a/src/types.jl b/src/types.jl index 61432e6..4323658 100644 --- a/src/types.jl +++ b/src/types.jl @@ -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