diff --git a/core/src/callback.jl b/core/src/callback.jl index ee49c6086..0ac0701d4 100644 --- a/core/src/callback.jl +++ b/core/src/callback.jl @@ -277,7 +277,10 @@ function discrete_control_affect_upcrossing!(integrator, condition_idx) # only possibly the du. Parameter changes can change the flow on an edge discontinuously, # giving the possibility of logical paradoxes where certain parameter changes immediately # undo the truth state that caused that parameter change. - is_basin = id_index(basin.node_id, discrete_control.listen_node_id[condition_idx])[1] + listen_node_ids = discrete_control.listen_node_id[condition_idx] + is_basin = + length(listen_node_ids) == 1 ? id_index(basin.node_id, only(listen_node_ids))[1] : + false # NOTE: The above no longer works when listen feature ids can be something other than node ids # I think the more durable option is to give all possible condition types a different variable string, # e.g. basin.level and level_boundary.level @@ -374,7 +377,7 @@ function discrete_control_affect!( discrete_control.logic_mapping[(discrete_control_node_id, truth_state)] else error( - "Control state specified for neither $truth_state_crossing_specific nor $truth_state for DiscreteControl node $discrete_control_node_id.", + "Control state specified for neither $truth_state_crossing_specific nor $truth_state for $discrete_control_node_id.", ) end diff --git a/core/src/validation.jl b/core/src/validation.jl index 202eec6a8..478fca586 100644 --- a/core/src/validation.jl +++ b/core/src/validation.jl @@ -552,29 +552,32 @@ function valid_discrete_control(p::Parameters, config::Config)::Bool end end end - for (Δt, var, node_id) in zip(look_ahead, variable, listen_node_id) - if !iszero(Δt) - node_type = node_id.type - # TODO: If more transient listen variables must be supported, this validation must be more specific - # (e.g. for some node some variables are transient, some not). - if node_type ∉ [NodeType.FlowBoundary, NodeType.LevelBoundary] - errors = true - @error "Look ahead supplied for non-timeseries listen variable '$var' from listen node $node_id." - else - if Δt < 0 + for (look_aheads, variables, listen_node_ids) in + zip(look_ahead, variable, listen_node_id) + for (Δt, var, node_id) in zip(look_aheads, variables, listen_node_ids) + if !iszero(Δt) + node_type = node_id.type + # TODO: If more transient listen variables must be supported, this validation must be more specific + # (e.g. for some node some variables are transient, some not). + if node_type ∉ [NodeType.FlowBoundary, NodeType.LevelBoundary] errors = true - @error "Negative look ahead supplied for listen variable '$var' from listen node $node_id." + @error "Look ahead supplied for non-timeseries listen variable '$var' from listen node $node_id." else - node = getfield(p, graph[node_id].type) - idx = if node_type == NodeType.Basin - id_index(node.node_id, node_id) - else - searchsortedfirst(node.node_id, node_id) - end - interpolation = getfield(node, Symbol(var))[idx] - if t_end + Δt > interpolation.t[end] + if Δt < 0 errors = true - @error "Look ahead for listen variable '$var' from listen node $node_id goes past timeseries end during simulation." + @error "Negative look ahead supplied for listen variable '$var' from listen node $node_id." + else + node = getfield(p, graph[node_id].type) + idx = if node_type == NodeType.Basin + id_index(node.node_id, node_id) + else + searchsortedfirst(node.node_id, node_id) + end + interpolation = getfield(node, Symbol(var))[idx] + if t_end + Δt > interpolation.t[end] + errors = true + @error "Look ahead for listen variable '$var' from listen node $node_id goes past timeseries end during simulation." + end end end end diff --git a/core/test/control_test.jl b/core/test/control_test.jl index fdb2e80f8..55d934a10 100644 --- a/core/test/control_test.jl +++ b/core/test/control_test.jl @@ -54,7 +54,7 @@ end p = model.integrator.p (; discrete_control, flow_boundary) = p - Δt = discrete_control.look_ahead[1] + Δt = discrete_control.look_ahead[1][1] t = Ribasim.tsaves(model) t_control = discrete_control.record.time[2] @@ -78,7 +78,7 @@ end p = model.integrator.p (; discrete_control, level_boundary) = p - Δt = discrete_control.look_ahead[1] + Δt = discrete_control.look_ahead[1][1] t = Ribasim.tsaves(model) t_control = discrete_control.record.time[2]