Hi all. I have two models with very different time definitions, and I want to know what my options are for combining them.
I want to add MimiFAIRv2 to the PAGE-2020 model. I previously was able to add MimiFAIRv2 to the META model, by instantiating the MimiFAIR model and then adding on my components, just with a later start time. But PAGE has irregular timestep lengths (it goes 2020, 2030, 2040, 2050, 2075, 2100, 2150, etc.).
One idea I have is to construct the MimiFAIR model as an parameter in my PAGE model. I would feed it data and run it within my PAGE timesteps. The only challenge I can think of is that I would need the ability to run the MimiFAIR one timestep at a time.
Is that doable? Or should I be thinking about this differently?
Hi @jrising, yes, working with two different sub-models with different time steps is tricky here. As you said, if your model is on the same time dimension as MimiFAIRv2, then starting with that get_model() and adding components is a pretty straightforward way to go, that’s how we handle it with MimiGIVE and sounds like you do the same with META.
If they are on different time steps, things are trickier, though I’d like to improve this. In the past I paired MimiIWG, including MimiPAGE2009 with MimiFaIRv162 but in that case used the non-elegant approach of running FaIR first (and twice for the SCC) and then exogenously forcing each of the IWG models in turn with those trajectories. Am I right in saying that will not work in this case because there are feedbacks you need to go back through MimiFaIRv2?
I could see how running it internally would work, albeit slowly, though as you mention we cannot as of now pause a model at a given year and then restart it, so you’d almost need to run it in entirety for each PAGE tilmestep right? Eg.
Run FAIR 1765 - 2020
Run PAGE 2020-2030 and gather new data for FAIR feedbacks
Run FAIR 1765 - 2030 with the new data updates
Run PAGE 2030-2040
and so on. That’s certainly not an appealing solution, but I think it could work? Happy to iterate with you on this or hop on a call.
Hmm looking at the full function signature I think Base.run(mi ...) would only allow you to run a model starting from its first timestep, so I guess you’d need to go within that function and run sets of time steps at once?
function Base.run(mi::ModelInstance, ntimesteps::Int=typemax(Int),
dimkeys::Union{Nothing, Dict{Symbol, Vector{T}} where T <: DimensionKeyTypes}=nothing)
if length(components(mi)) == 0
error("Cannot run the model: no components have been created.")
end
time_keys::Vector{Int} = dimkeys === nothing ? dim_keys(mi.md, :time) : dimkeys[:time]
# truncate time_keys if caller so desires
if ntimesteps < length(time_keys)
time_keys = time_keys[1:ntimesteps]
end
clock = Clock(time_keys)
# Get the dimensions Named Tuple from the dimension dictionary which will be
# passed to run_timestep() and init(), so we can safely implement Base.getproperty(),
# allowing `d.regions` etc.
# All values in the named tuple are vectors of Ints, except the `:time` value, which is a
# vector of AbstractTimesteps, so that `d.time` returns values that can be used for indexing
# into timestep arrays.
dim_val_named_tuple = NamedTuple(name => (name == :time ? timesteps(clock) : collect(values(dim))) for (name, dim) in dim_dict(mi.md))
# recursively initializes all components
init(mi, dim_val_named_tuple)
while ! finished(clock)
run_timestep(mi, clock, dim_val_named_tuple)
advance(clock)
end
nothing
end
Yup, that’s my thought. I’ll have the FAIR model as a parameter of a FAIRManager component (or FAIRGrounds?), and then run the appropriate number of FAIR timesteps each time that component’s run_timestep is called.
Okay, if you don’t see anything obviously wrong with this approach, I’ll try it out.
I don’t see anything obviously wrong with that approach, seems like it could work if you can get it to save state, or in essence pause, and then keep running after you get more inputs. I haven’t tried to do that before, but would be happy to work alongside if you try it out.
One thing that comes to mind with unconventional uses, which I think you know well, is to make sure update the ModelInstance not the ModelDef to make sure you don’t rebuild an slow things down.
Thanks for being a sounding board on this! I’ll let you know how it goes.
One thing you can help me with: What’s the right way to set some values in one of the MimiFAIR parameters? MimiFAIR has a co2_cycle component with an E_co2 parameter (just indexed by time). Before, I could just connect that to one of my components. Now, it will be an unconnected parameter filled in with FAIR’s normal initialization data, and I will want to replace data in the matrix as I go. If I have a ModelInstance, how do I set-- say-- E_co2 for the year 2000?
Ah good question, so maybe what you would want to do there would be to grab default MimiFaIR with any of the default RCP scenarios, which as you said will then initialize on its own. You could make a copy of the matrix of data it gets with something like
fair_co2 = m_fair[:co2_cycle, :E_co2]
and then in the timestep fill in the appropriate slice of fair_co2 and use update_param! on the model instance? To be safe you could also do that at the beginning and update all of the post 2020 values to NaN so you’ll get a sign if you access them? I’ve done that before, I did it with missing but for some reason it really slowed performance so I stuck with NaN.
Does that make some sense? Since update_param! is strict about the size of the data you pass it this would avoid it whining that you aren’t passing the correct size.