@@ -15,6 +15,8 @@ function parse_static_and_time(
15
15
time:: Union{StructVector, Nothing} = nothing ,
16
16
defaults:: NamedTuple = (; active = true ),
17
17
time_interpolatables:: Vector{Symbol} = Symbol[],
18
+ interpolation_type:: Type{<:AbstractInterpolation} = LinearInterpolation,
19
+ is_complete:: Bool = true ,
18
20
):: Tuple{NamedTuple, Bool}
19
21
# E.g. `PumpStatic`
20
22
static_type = eltype (static)
@@ -42,7 +44,16 @@ function parse_static_and_time(
42
44
# If the type is a union, then the associated parameter is optional and
43
45
# the type is of the form Union{Missing,ActualType}
44
46
parameter_type = if parameter_name in time_interpolatables
45
- ScalarInterpolation
47
+ # We need the concrete type to store in the parameters
48
+ # The interpolation_type is not concrete because they don't have the
49
+ # constructors we use
50
+ if interpolation_type == LinearInterpolation
51
+ ScalarInterpolation
52
+ elseif interpolation_type == ConstantInterpolation
53
+ ScalarConstantInterpolation
54
+ else
55
+ error (" Unknown interpolation type." )
56
+ end
46
57
elseif isa (parameter_type, Union)
47
58
nonmissingtype (parameter_type)
48
59
else
@@ -120,7 +131,7 @@ function parse_static_and_time(
120
131
val = defaults[parameter_name]
121
132
end
122
133
if parameter_name in time_interpolatables
123
- val = LinearInterpolation (
134
+ val = interpolation_type (
124
135
[val, val],
125
136
trivial_timespan;
126
137
cache_parameters = true ,
@@ -149,19 +160,16 @@ function parse_static_and_time(
149
160
for parameter_name in parameter_names
150
161
# If the parameter is interpolatable, create an interpolation object
151
162
if parameter_name in time_interpolatables
152
- val, is_valid = get_scalar_interpolation (
163
+ val = get_scalar_interpolation (
153
164
config. starttime,
154
165
t_end,
155
166
time,
156
167
node_id,
157
168
parameter_name;
158
169
default_value = hasproperty (defaults, parameter_name) ?
159
170
defaults[parameter_name] : NaN ,
171
+ interpolation_type,
160
172
)
161
- if ! is_valid
162
- errors = true
163
- @error " A $parameter_name time series for $node_id has repeated times, this can not be interpolated."
164
- end
165
173
else
166
174
# Activity of transient nodes is assumed to be true
167
175
if parameter_name == :active
@@ -173,6 +181,19 @@ function parse_static_and_time(
173
181
end
174
182
getfield (out, parameter_name)[node_id. idx] = val
175
183
end
184
+ elseif ! is_complete
185
+ # Apply the defaults just like if it was in static but missing
186
+ for parameter_name in parameter_names
187
+ val = defaults[parameter_name]
188
+ if parameter_name in time_interpolatables
189
+ val = interpolation_type (
190
+ [val, val],
191
+ trivial_timespan;
192
+ cache_parameters = true ,
193
+ )
194
+ end
195
+ getfield (out, parameter_name)[node_id. idx] = val
196
+ end
176
197
else
177
198
@error " $node_id data not in any table."
178
199
errors = true
@@ -651,24 +672,20 @@ function ConcentrationData(
651
672
for group in IterTools. groupby (row -> row. substance, data_id)
652
673
first_row = first (group)
653
674
substance = first_row. substance
654
- itp, no_duplication = get_scalar_interpolation (
675
+ itp = get_scalar_interpolation (
655
676
config. starttime,
656
677
t_end,
657
678
StructVector (group),
658
679
NodeID (:Basin , first_row. node_id, 0 ),
659
- :concentration ,
680
+ :concentration ;
681
+ interpolation_type = LinearInterpolation,
660
682
)
661
683
concentration_external_id[" concentration_external.$substance " ] = itp
662
684
if any (itp. u .< 0 )
663
685
errors = true
664
686
@error " Found negative concentration(s) in `Basin / concentration_external`." node_id =
665
687
id, substance
666
688
end
667
- if ! no_duplication
668
- errors = true
669
- @error " There are repeated time values for in `Basin / concentration_external`." node_id =
670
- id substance
671
- end
672
689
end
673
690
push! (concentration_external, concentration_external_id)
674
691
end
@@ -691,26 +708,52 @@ function ConcentrationData(
691
708
end
692
709
693
710
function Basin (db:: DB , config:: Config , graph:: MetaGraph ):: Basin
694
- node_id = get_node_ids (db, NodeType. Basin)
695
- n = length (node_id)
696
-
697
711
# both static and time are optional, but we need fallback defaults
698
712
static = load_structvector (db, config, BasinStaticV1)
699
713
time = load_structvector (db, config, BasinTimeV1)
700
714
state = load_structvector (db, config, BasinStateV1)
701
715
702
- # Forcing
703
- precipitation = zeros (n)
704
- potential_evaporation = zeros (n)
705
- drainage = zeros (n)
706
- infiltration = zeros (n)
707
- table = (; precipitation, potential_evaporation, drainage, infiltration)
716
+ _, _, node_id, valid =
717
+ static_and_time_node_ids (db, static, time, NodeType. Basin; is_complete = false )
718
+ if ! valid
719
+ error (" Problems encountered when parsing Basin static and time node IDs." )
720
+ end
721
+
722
+ time_interpolatables =
723
+ [:precipitation , :potential_evaporation , :drainage , :infiltration ]
724
+ parsed_parameters, valid = parse_static_and_time (
725
+ db,
726
+ config,
727
+ Basin;
728
+ static,
729
+ time,
730
+ time_interpolatables,
731
+ interpolation_type = ConstantInterpolation,
732
+ defaults = (;
733
+ precipitation = NaN ,
734
+ potential_evaporation = NaN ,
735
+ drainage = NaN ,
736
+ infiltration = NaN ,
737
+ ),
738
+ is_complete = false ,
739
+ )
708
740
709
- set_static_value! (table, node_id, static)
710
- set_current_value! (table, node_id, time, config. starttime)
711
- check_no_nans (table, " Basin" )
741
+ forcing = BasinForcing (;
742
+ parsed_parameters. precipitation,
743
+ parsed_parameters. potential_evaporation,
744
+ parsed_parameters. drainage,
745
+ parsed_parameters. infiltration,
746
+ )
712
747
713
- vertical_flux = ComponentVector (; table... )
748
+ # Current forcing is stored as separate array for BMI access
749
+ # These are updated from the interpolation objects at runtime
750
+ n = length (node_id)
751
+ vertical_flux = ComponentVector (;
752
+ precipitation = zeros (n),
753
+ potential_evaporation = zeros (n),
754
+ drainage = zeros (n),
755
+ infiltration = zeros (n),
756
+ )
714
757
715
758
# Profiles
716
759
area, level = create_storage_tables (db, config)
@@ -736,11 +779,14 @@ function Basin(db::DB, config::Config, graph::MetaGraph)::Basin
736
779
vertical_flux,
737
780
storage_to_level,
738
781
level_to_area,
739
- time ,
782
+ forcing ,
740
783
concentration_data,
741
784
concentration_time,
742
785
)
743
786
787
+ # Ensure the initial data is loaded at t0 for BMI
788
+ update_basin! (basin, 0.0 )
789
+
744
790
storage0 = get_storages_from_levels (basin, state. level)
745
791
@assert length (storage0) == n " Basin / state length differs from number of Basins"
746
792
basin. storage0 .= storage0
@@ -1074,39 +1120,30 @@ function user_demand_time!(
1074
1120
1075
1121
active[user_demand_idx] = true
1076
1122
demand_from_timeseries[user_demand_idx] = true
1077
- return_factor_itp, is_valid_return = get_scalar_interpolation (
1123
+ return_factor_itp = get_scalar_interpolation (
1078
1124
config. starttime,
1079
1125
t_end,
1080
1126
StructVector (group),
1081
1127
NodeID (:UserDemand , first_row. node_id, 0 ),
1082
1128
:return_factor ;
1129
+ interpolation_type = LinearInterpolation,
1083
1130
)
1084
- if is_valid_return
1085
- return_factor[user_demand_idx] = return_factor_itp
1086
- else
1087
- @error " The return_factor(t) relationship for UserDemand $(first_row. node_id) from the time table has repeated timestamps, this can not be interpolated."
1088
- errors = true
1089
- end
1131
+ return_factor[user_demand_idx] = return_factor_itp
1090
1132
1091
1133
min_level[user_demand_idx] = first_row. min_level
1092
1134
1093
1135
priority_idx = findsorted (priorities, first_row. priority)
1094
- demand_p_itp, is_valid_demand = get_scalar_interpolation (
1136
+ demand_p_itp = get_scalar_interpolation (
1095
1137
config. starttime,
1096
1138
t_end,
1097
1139
StructVector (group),
1098
1140
NodeID (:UserDemand , first_row. node_id, 0 ),
1099
1141
:demand ;
1100
1142
default_value = 0.0 ,
1143
+ interpolation_type = LinearInterpolation,
1101
1144
)
1102
1145
demand[user_demand_idx, priority_idx] = demand_p_itp (0.0 )
1103
-
1104
- if is_valid_demand
1105
- demand_itp[user_demand_idx][priority_idx] = demand_p_itp
1106
- else
1107
- @error " The demand(t) relationship for UserDemand $(first_row. node_id) of priority $(first_row. priority_idx) from the time table has repeated timestamps, this can not be interpolated."
1108
- errors = true
1109
- end
1146
+ demand_itp[user_demand_idx][priority_idx] = demand_p_itp
1110
1147
end
1111
1148
return errors
1112
1149
end
0 commit comments