Skip to content

Commit c5c68ca

Browse files
committed
Simple single-variable hacky ad hoc connector test ; as a model it's awkward
1 parent b63e003 commit c5c68ca

4 files changed

Lines changed: 217 additions & 7 deletions

File tree

src/Abstract_model_structs.jl

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,4 +25,47 @@ model_(m::AbstractModel) = m
2525
get_models(m::AbstractModel) = [model_(m)] # Get the models of an AbstractModel
2626
# Note: it is returning a vector of models, because in this case the user provided a single model instead of a vector of.
2727
get_status(m::AbstractModel) = nothing
28-
get_mapped_variables(m::AbstractModel) = Pair{Symbol,String}[]
28+
get_mapped_variables(m::AbstractModel) = Pair{Symbol,String}[]
29+
30+
31+
#using Dates
32+
struct TimestepRange
33+
lower_bound::Period
34+
upper_bound::Period
35+
end
36+
37+
# Default, no specified range, meaning the model either doesn't depend on time or uses the simulation's default (eg smallest) timestep
38+
TimestepRange() = TimestepRange(Second(0), Second(0))
39+
# Only a single timestep type possible
40+
TimestepRange(p::Period) = TimestepRange(p, p)
41+
42+
"""
43+
timestep_range_(tsr::TimestepRange)
44+
45+
Return the model's valid range for timesteps (which corresponds to the simulation base timestep in the default case).
46+
"""
47+
function timestep_range_(model::AbstractModel)
48+
return TimestepRange()
49+
end
50+
51+
"""
52+
timestep_valid(tsr::TimestepRange)
53+
54+
Checks whether a TimestepRange
55+
"""
56+
timestep_valid(tsr::TimestepRange) = tsr.lower_bound <= tsr.upper_bound
57+
58+
function model_timestep_range_compatible_with_timestep(tsr::TimestepRange, p::Period)
59+
if !timestep_valid(tsr)
60+
return false
61+
end
62+
63+
# 0 means any timestep is valid, no timestep constraints
64+
if tsr.upper_bound == Seconds(0)
65+
return true
66+
end
67+
68+
return p >= tsr.lower_bound && p <= tsr.lower_bound
69+
end
70+
71+
# TODO should i set all timestep ranges to default and hope the modeler gets it right or should i force them to write something ?

src/mtg/GraphSimulation.jl

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ A type that holds all information for a simulation over a graph.
1616
- `models`: a dictionary of models
1717
- `outputs`: a dictionary of outputs
1818
"""
19-
struct GraphSimulation{T,S,U,O,V}
19+
struct GraphSimulation{T,S,U,O,V,W}
2020
graph::T
2121
statuses::S
2222
status_templates::Dict{String,Dict{Symbol,Any}}
@@ -26,10 +26,12 @@ struct GraphSimulation{T,S,U,O,V}
2626
models::Dict{String,U}
2727
outputs::Dict{String,O}
2828
outputs_index::Dict{String, Int}
29+
default_timestep::Int # TODO make it a period ?
30+
model_timesteps::Dict{W, Int} #where {W <: AbstractModel}
2931
end
3032

31-
function GraphSimulation(graph, mapping; nsteps=1, outputs=nothing, type_promotion=nothing, check=true, verbose=false)
32-
GraphSimulation(init_simulation(graph, mapping; nsteps=nsteps, outputs=outputs, type_promotion=type_promotion, check=check, verbose=verbose)...)
33+
function GraphSimulation(graph, mapping; nsteps=1, outputs=nothing, type_promotion=nothing, check=true, verbose=false, default_timestep=1, model_timesteps=Dict{String, Int}())
34+
GraphSimulation(init_simulation(graph, mapping; nsteps=nsteps, outputs=outputs, type_promotion=type_promotion, check=check, verbose=verbose)..., default_timestep, model_timesteps)
3335
end
3436

3537
dep(g::GraphSimulation) = g.dependency_graph

src/run.jl

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -365,17 +365,21 @@ function run!(
365365
meteo=nothing,
366366
constants=PlantMeteo.Constants(),
367367
extra=nothing;
368+
orchestrator::Orchestrator=nothing,
368369
nsteps=nothing,
369370
tracked_outputs=nothing,
370371
check=true,
371-
executor=ThreadedEx()
372+
executor=ThreadedEx(),
373+
default_timestep::Int,
374+
model_timesteps::Dict{T, Int} where {T}
375+
372376
)
373377
isnothing(nsteps) && (nsteps = get_nsteps(meteo))
374378
meteo_adjusted = adjust_weather_timesteps_to_given_length(nsteps, meteo)
375379

376380
# NOTE : replace_mapping_status_vectors_with_generated_models is assumed to have already run if used
377381
# otherwise there might be vector length conflicts with timesteps
378-
sim = GraphSimulation(object, mapping, nsteps=nsteps, check=check, outputs=tracked_outputs)
382+
sim = GraphSimulation(object, mapping, nsteps=nsteps, check=check, outputs=tracked_outputs, default_timestep=default_timestep, model_timesteps=model_timesteps)
379383
run!(
380384
sim,
381385
meteo_adjusted,
@@ -455,6 +459,18 @@ function run_node_multiscale!(
455459
return nothing
456460
end
457461

462+
model_timestep = object.model_timesteps[typeof(node.value)]
463+
464+
if model_timestep != object.default_timestep
465+
# do accumulation
466+
467+
468+
# run if necessary
469+
if i % model_timestep != 0
470+
return nothing
471+
end
472+
end
473+
458474
node_statuses = status(object)[node.scale] # Get the status of the nodes at the current scale
459475
models_at_scale = models[node.scale]
460476

test/test-simulation.jl

Lines changed: 150 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -292,4 +292,153 @@ end;
292292
end
293293
end
294294
end
295-
end
295+
end
296+
297+
298+
299+
300+
using PlantSimEngine
301+
# Include the example dummy processes:
302+
using PlantSimEngine.Examples
303+
using Test, Aqua
304+
using Tables, DataFrames, CSV
305+
using MultiScaleTreeGraph
306+
using PlantMeteo, Statistics
307+
using Documenter # for doctests
308+
309+
using PlantMeteo.Dates
310+
include("helper-functions.jl")
311+
312+
313+
314+
# These models might be worth exposing in the future ?
315+
PlantSimEngine.@process "basic_current_timestep" verbose = false
316+
317+
struct HelperCurrentTimestepModel <: AbstractBasic_Current_TimestepModel
318+
end
319+
320+
PlantSimEngine.inputs_(::HelperCurrentTimestepModel) = (next_timestep=1,)
321+
PlantSimEngine.outputs_(m::HelperCurrentTimestepModel) = (current_timestep=1,)
322+
323+
function PlantSimEngine.run!(m::HelperCurrentTimestepModel, models, status, meteo, constants=nothing, extra=nothing)
324+
status.current_timestep = status.next_timestep
325+
end
326+
327+
PlantSimEngine.ObjectDependencyTrait(::Type{<:HelperCurrentTimestepModel}) = PlantSimEngine.IsObjectDependent()
328+
PlantSimEngine.TimeStepDependencyTrait(::Type{<:HelperCurrentTimestepModel}) = PlantSimEngine.IsTimeStepDependent()
329+
330+
PlantSimEngine.timestep_range_(m::HelperCurrentTimestepModel) = Day(1)
331+
332+
333+
PlantSimEngine.@process "basic_next_timestep" verbose = false
334+
struct HelperNextTimestepModel <: AbstractBasic_Next_TimestepModel
335+
end
336+
337+
PlantSimEngine.inputs_(::HelperNextTimestepModel) = (current_timestep=1,)
338+
PlantSimEngine.outputs_(m::HelperNextTimestepModel) = (next_timestep=1,)
339+
340+
function PlantSimEngine.run!(m::HelperNextTimestepModel, models, status, meteo, constants=nothing, extra=nothing)
341+
status.next_timestep = status.current_timestep + 1
342+
end
343+
344+
PlantSimEngine.timestep_range_(m::HelperNextTimestepModel) = Day(1)
345+
346+
347+
348+
349+
350+
PlantSimEngine.@process "ToyDay" verbose = false
351+
352+
struct MyToyDayModel <: AbstractToydayModel end
353+
354+
PlantSimEngine.inputs_(m::MyToyDayModel) = (a=1,)
355+
PlantSimEngine.outputs_(m::MyToyDayModel) = (daily_temperature=-Inf,)
356+
357+
function PlantSimEngine.run!(m::MyToyDayModel, models, status, meteo, constants=nothing, extra=nothing)
358+
status.daily_temperature = meteo.T
359+
end
360+
361+
PlantSimEngine.@process "ToyWeek" verbose = false
362+
363+
struct MyToyWeekModel <: AbstractToyweekModel
364+
temperature_threshold::Float64
365+
end
366+
367+
MyToyWeekModel() = MyToyWeekModel(30.0)
368+
function PlantSimEngine.inputs_(::MyToyWeekModel)
369+
(weekly_max_temperature=-Inf,)
370+
end
371+
PlantSimEngine.outputs_(m::MyToyWeekModel) = (hot = false,)
372+
373+
function PlantSimEngine.run!(m::MyToyWeekModel, models, status, meteo, constants=nothing, extra=nothing)
374+
status.hot = status.weekly_max_temperature > m.temperature_threshold
375+
end
376+
377+
PlantSimEngine.timestep_range_(m::MyToyWeekModel) = Week(1)
378+
379+
380+
381+
PlantSimEngine.@process "DWConnector" verbose = false
382+
383+
struct MyDwconnectorModel <: AbstractDwconnectorModel
384+
T_daily::Array{Float64}
385+
end
386+
387+
MyDwconnectorModel() = MyDwconnectorModel(Array{Float64}(undef, 7))
388+
389+
function PlantSimEngine.inputs_(::MyDwconnectorModel)
390+
(daily_temperature=-Inf, current_timestep=1,)
391+
end
392+
PlantSimEngine.outputs_(m::MyDwconnectorModel) = (weekly_max_temperature = 0.0,)
393+
394+
function PlantSimEngine.run!(m::MyDwconnectorModel, models, status, meteo, constants=nothing, extra=nothing)
395+
m.T_daily[1 + (status.current_timestep % 7)] = status.daily_temperature
396+
397+
if(status.current_timestep % 7 == 1)
398+
status.weekly_max_temperature = sum(m.T_daily)/7.0
399+
else
400+
status.weekly_max_temperature = 0
401+
end
402+
end
403+
404+
PlantSimEngine.timestep_range_(m::MyDwconnectorModel) = Day(1)
405+
406+
407+
408+
409+
410+
meteo_day = read_weather(joinpath(pkgdir(PlantSimEngine), "examples/meteo_day.csv"), duration=Day)
411+
412+
m = Dict("Default" => (
413+
MyToyDayModel(),
414+
MyToyWeekModel(),
415+
MyDwconnectorModel(),
416+
HelperNextTimestepModel(),
417+
MultiScaleModel(
418+
model=HelperCurrentTimestepModel(),
419+
mapped_variables=[PreviousTimeStep(:next_timestep),],
420+
),
421+
Status(a=1,)))
422+
423+
to_initialize(m)
424+
425+
models_timestep = Dict(MyToyDayModel=>1, MyDwconnectorModel => 1, MyToyWeekModel =>7, HelperNextTimestepModel => 1, HelperCurrentTimestepModel => 1)
426+
427+
mtg = Node(MultiScaleTreeGraph.NodeMTG("/", "Default", 1, 1))
428+
429+
out = run!(mtg, m, meteo_day, default_timestep=1, model_timesteps=models_timestep)
430+
431+
@testset "Test varying timestep" begin
432+
433+
434+
@test
435+
@test
436+
437+
end
438+
439+
440+
# NOTE : replace_mapping_status_vectors_with_generated_models is assumed to have already run if used
441+
# otherwise there might be vector length conflicts with timesteps
442+
sim = @enter PlantSimEngine.GraphSimulation(mtg, m, nsteps=nothing, check=true, outputs=nothing, default_timestep=1, model_timesteps=models_timestep)
443+
444+
using PlantSimEngine

0 commit comments

Comments
 (0)