Coverage for fixtures\flow_basic_fixture.py: 100%
144 statements
« prev ^ index » next coverage.py v7.5.1, created at 2024-05-08 14:15 +0200
« prev ^ index » next coverage.py v7.5.1, created at 2024-05-08 14:15 +0200
1from typing import Callable, NamedTuple
3import numpy as np
4import pandas as pd
5import pytest
6import xarray as xr
9def make_basic_dis(dz, nrow, ncol):
10 """Basic model discretization"""
11 dx = 10.0
12 dy = -10.0
14 nlay = len(dz)
16 shape = nlay, nrow, ncol
18 xmin = 0.0
19 xmax = dx * ncol
20 ymin = 0.0
21 ymax = abs(dy) * nrow
22 dims = ("layer", "y", "x")
24 layer = np.arange(1, nlay + 1)
25 y = np.arange(ymax, ymin, dy) + 0.5 * dy
26 x = np.arange(xmin, xmax, dx) + 0.5 * dx
27 coords = {"layer": layer, "y": y, "x": x}
29 ibound = xr.DataArray(np.ones(shape, dtype=np.int32), coords=coords, dims=dims)
31 surface = 0.0
32 interfaces = np.insert((surface - np.cumsum(dz)), 0, surface)
34 bottom = xr.DataArray(interfaces[1:], coords={"layer": layer}, dims="layer")
35 top = xr.DataArray(interfaces[:-1], coords={"layer": layer}, dims="layer")
37 return ibound, top, bottom
40@pytest.fixture(scope="module")
41def basic_dis():
42 return make_basic_dis(dz=[5, 30, 100], nrow=9, ncol=9)
45@pytest.fixture(scope="function")
46def basic_dis__topsystem():
47 return make_basic_dis(dz=[1.0, 2.0, 4.0, 10.0], nrow=9, ncol=9)
50class BasicDisSettings(NamedTuple):
51 nlay: int = 1
52 nrow: int = 1
53 ncol: int = 1
54 zstart: float = 0.0
55 zstop: float = -1.0
56 ystart: float = 0.0
57 ystop: float = 1.0
58 xstart: float = 0.0
59 xstop: float = 1.0
60 dx: float = 1.0
61 dy: float = 1.0
62 dz: float = 1.0
63 space_generator: Callable = np.linspace
66@pytest.fixture
67def parameterizable_basic_dis(request):
68 settings = request.param
69 shape = (settings.nlay, settings.nrow, settings.ncol)
71 x = (
72 settings.space_generator(
73 settings.xstart + 1, settings.xstop + 1, settings.ncol + 1, endpoint=True
74 )
75 - 1
76 )
77 y = (
78 settings.space_generator(
79 settings.ystop + 1, settings.ystart + 1, settings.nrow + 1, endpoint=True
80 )
81 - 1
82 )
83 z = (
84 settings.space_generator(
85 settings.zstart - 1, settings.zstop - 1, settings.nlay + 1, endpoint=True
86 )
87 + 1
88 )
90 xc = (x[:-1] + x[1:]) / 2
91 yc = (y[:-1] + y[1:]) / 2
93 dx = x[1:] - x[:-1]
94 dy = y[1:] - y[:-1]
96 layers = np.arange(settings.nlay) + 1
98 idomain = xr.DataArray(
99 np.ones(shape, dtype=np.int32),
100 coords={"layer": layers, "y": yc, "x": xc},
101 name="idomain",
102 )
104 # Assign dx and dy coordinates. They are needed for certain methods like 'coord_reference'
105 if np.all(np.isclose(dx, dx[0])):
106 idomain = idomain.assign_coords({"dx": dx[0]})
107 else:
108 idomain = idomain.assign_coords({"dx": ("x", dx)})
109 if np.all(np.isclose(dy, dy[0])):
110 idomain = idomain.assign_coords({"dy": dy[0]})
111 else:
112 idomain = idomain.assign_coords({"dy": ("y", dy)})
114 top = xr.DataArray(z[:-1], coords={"layer": layers})
115 bottom = xr.DataArray(z[1:], coords={"layer": layers})
117 return idomain, top, bottom
120@pytest.fixture(scope="module")
121def three_days():
122 """Simple time discretization of three days"""
123 return pd.date_range(start="2018-01-01", end="2018-01-03", freq="D")
126@pytest.fixture(scope="module")
127def two_days():
128 """Simple time discretization of two days"""
129 return pd.date_range(start="2018-01-01", end="2018-01-02", freq="D")
132@pytest.fixture(scope="module")
133def well_df(three_days):
134 nrow = 9
135 ncol = 9
136 dx = 10.0
137 dy = -10.0
138 xmin = 0.0
139 xmax = dx * ncol
140 ymin = 0.0
141 ymax = abs(dy) * nrow
143 y = np.arange(ymax, ymin, dy) + 0.5 * dy
144 x = np.arange(xmin, xmax, dx) + 0.5 * dx
146 times = three_days
147 n_repeats = int(len(x) / len(times)) + 1
149 df = pd.DataFrame()
150 df["id_name"] = np.arange(len(x)).astype(str)
151 df["x"] = x
152 df["y"] = y
153 df["rate"] = dx * dy * -1 * 0.5
154 df["time"] = np.tile(times, n_repeats)[: len(x)]
155 df["layer"] = 2
156 return df
159@pytest.fixture(scope="module")
160def get_render_dict():
161 """
162 Helper function to return dict to render.
164 Fixture returns local helper function, so that the helper function
165 is only evaluated when called.
166 See: https://stackoverflow.com/a/51389067
167 """
169 def _get_render_dict(package, directory, globaltimes, nlayer, system_index=1):
170 composition = package.compose(
171 directory,
172 globaltimes,
173 nlayer,
174 system_index=system_index,
175 )
177 return {
178 "pkg_id": package._pkg_id,
179 "name": package.__class__.__name__,
180 "variable_order": package._variable_order,
181 "package_data": composition[package._pkg_id],
182 }
184 return _get_render_dict
187@pytest.fixture(scope="module")
188def metaswap_dict(basic_dis):
189 ibound, _, _ = basic_dis
191 active = ibound.isel(layer=0)
193 d = {}
194 d["boundary"] = active
195 d["landuse"] = active
196 d["rootzone_thickness"] = 1.2
197 d["soil_physical_unit"] = active
198 d["meteo_station_number"] = active
199 d["surface_elevation"] = 0.0
200 d["sprinkling_type"] = active
201 d["sprinkling_layer"] = active
202 d["sprinkling_capacity"] = 1000.0
203 d["wetted_area"] = 30.0
204 d["urban_area"] = 30.0
205 d["ponding_depth_urban"] = 0.02
206 d["ponding_depth_rural"] = 0.005
207 d["runoff_resistance_urban"] = 1.5
208 d["runoff_resistance_rural"] = 1.5
209 d["runon_resistance_urban"] = 1.5
210 d["runon_resistance_rural"] = 1.5
211 d["infiltration_capacity_urban"] = 10.0
212 d["infiltration_capacity_rural"] = 2.0
213 d["perched_water_table"] = 0.5
214 d["soil_moisture_factor"] = 1.0
215 d["conductivity_factor"] = 1.0
217 d["lookup_and_forcing_files"] = [
218 "fact_svat.inp",
219 "luse_svat.inp",
220 "mete_grid.inp",
221 "para_sim.inp",
222 "tiop_sim.inp",
223 "init_svat.inp",
224 "comp_post.inp",
225 "sel_key_svat_per.inp",
226 ]
228 return d
231@pytest.fixture(scope="module")
232def horizontal_flow_barrier_gdf(basic_dis):
233 """GeoDataframe that can be used to initiate HorizontalFlowBarriers"""
234 import geopandas as gpd
235 from shapely.geometry import LineString
237 ibound, _, _ = basic_dis
239 x = ibound.x.values
240 y = ibound.y.values
242 line1 = LineString([(x[1], y[1]), (x[1], y[-2])])
243 line2 = LineString([(x[4], y[1]), (x[4], y[-2])])
245 lines = np.array([line1, line2, line1, line2], dtype="object")
246 hfb_layers = np.array([1, 1, 3, 3])
247 id_name = ["left_upper", "right_upper", "left_lower", "right_lower"]
249 hfb_gdf = gpd.GeoDataFrame(
250 geometry=lines,
251 data={"layer": hfb_layers, "resistance": 100.0, "id_name": id_name},
252 )
254 return hfb_gdf