|
261 | 261 | @test sim_clock_override.temporal_state.last_run[ModelKey(scope, "Leaf", :mrclocksource)] == 4.0 |
262 | 262 | @test sim_clock_override.temporal_state.last_run[ModelKey(scope, "Leaf", :mrclockconsumer)] == 3.0 |
263 | 263 |
|
| 264 | + # Expectation 7b: non-sequential executors warn and fall back to sequential behavior. |
| 265 | + mapping_clock_fallback_seq = Dict( |
| 266 | + "Leaf" => ( |
| 267 | + ModelSpec(MRClockSourceModel(Ref(0))) |> TimeStepModel(1.0), |
| 268 | + ModelSpec(MRClockConsumerModel()) |> |
| 269 | + InputBindings(; X=(process=:mrclocksource, var=:X)), |
| 270 | + ), |
| 271 | + ) |
| 272 | + sim_clock_fallback_seq = PlantSimEngine.GraphSimulation(mtg, mapping_clock_fallback_seq, nsteps=4, check=true, outputs=Dict("Leaf" => (:X, :Y))) |
| 273 | + out_fallback_seq = run!(sim_clock_fallback_seq, meteo4, multirate=true, executor=SequentialEx()) |
| 274 | + out_fallback_seq_df = convert_outputs(out_fallback_seq, DataFrame) |
| 275 | + |
| 276 | + mapping_clock_fallback_threaded = Dict( |
| 277 | + "Leaf" => ( |
| 278 | + ModelSpec(MRClockSourceModel(Ref(0))) |> TimeStepModel(1.0), |
| 279 | + ModelSpec(MRClockConsumerModel()) |> |
| 280 | + InputBindings(; X=(process=:mrclocksource, var=:X)), |
| 281 | + ), |
| 282 | + ) |
| 283 | + sim_clock_fallback_threaded = PlantSimEngine.GraphSimulation(mtg, mapping_clock_fallback_threaded, nsteps=4, check=true, outputs=Dict("Leaf" => (:X, :Y))) |
| 284 | + @test_logs (:warn, r"Multi-rate MTG runs currently execute sequentially") begin |
| 285 | + out_fallback_threaded = run!(sim_clock_fallback_threaded, meteo4, multirate=true, executor=ThreadedEx()) |
| 286 | + out_fallback_threaded_df = convert_outputs(out_fallback_threaded, DataFrame) |
| 287 | + @test out_fallback_threaded_df["Leaf"][:, :X] == out_fallback_seq_df["Leaf"][:, :X] |
| 288 | + @test out_fallback_threaded_df["Leaf"][:, :Y] == out_fallback_seq_df["Leaf"][:, :Y] |
| 289 | + end |
| 290 | + |
264 | 291 | # Expectation 8: cross-scale hold-last resolution works with different clocks. |
265 | 292 | # Leaf producer runs each step; Plant consumer runs every 2 steps (1, 3) and reads Leaf XS through multiscale mapping. |
266 | 293 | source_counter_3 = Ref(0) |
|
462 | 489 | @test out_daily_period_df["Leaf"][25:26, :YD] == [2.0, 2.0] |
463 | 490 | @test sim_daily_period.temporal_state.last_run[ModelKey(scope, "Leaf", :mrdailysource)] == 25.0 |
464 | 491 |
|
465 | | - # Expectation 16: invalid mapping-level API configuration fails during GraphSimulation init. |
| 492 | + # Expectation 16: model timesteps shorter than meteo base step are rejected. |
| 493 | + mapping_substep_period = Dict( |
| 494 | + "Leaf" => ( |
| 495 | + ModelSpec(MRDailySourceModel(Ref(0))) |> TimeStepModel(Dates.Minute(30)), |
| 496 | + ), |
| 497 | + ) |
| 498 | + sim_substep_period = PlantSimEngine.GraphSimulation(mtg, mapping_substep_period, nsteps=26, check=true, outputs=Dict("Leaf" => (:XD,))) |
| 499 | + @test_throws "shorter than simulation base step" run!(sim_substep_period, meteo_hourly, multirate=true, executor=SequentialEx()) |
| 500 | + |
| 501 | + # Expectation 17: invalid mapping-level API configuration fails during GraphSimulation init. |
466 | 502 | mapping_bad_input = Dict( |
467 | 503 | "Leaf" => ( |
468 | 504 | MRSourceModel(), |
|
0 commit comments