Skip to content

Commit 8463e23

Browse files
authored
Merge pull request #254 from ArtesiaWater/dev
Release 0.12.6: bugfix release
2 parents 7762f0f + e276256 commit 8463e23

19 files changed

+9957
-54
lines changed

Diff for: .devcontainer/setup.sh

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
#!/bin/bash
22

33
# Create model environments
4-
pip install -e .
4+
pip install -e .[full]

Diff for: docs/examples/07_fews.nblink

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"path": "../../examples/07_fews.ipynb"
3+
}

Diff for: examples/07_fews.ipynb

+518
Large diffs are not rendered by default.

Diff for: hydropandas/extensions/geo.py

+1-2
Original file line numberDiff line numberDiff line change
@@ -232,8 +232,7 @@ def _get_nearest_geometry(
232232
)
233233
else:
234234
raise ValueError(
235-
"invalid value for multiple_geometries ->"
236-
f"{multiple_geometries}"
235+
f"invalid value for multiple_geometries ->{multiple_geometries}"
237236
)
238237
else:
239238
gdf_obs.loc[i, f"nearest {geometry_type}"] = gdf.iloc[

Diff for: hydropandas/extensions/gwobs.py

+6-8
Original file line numberDiff line numberDiff line change
@@ -179,28 +179,26 @@ def get_modellayer_from_screen_depth(ftop, fbot, zvec, left=-999, right=999):
179179

180180
else:
181181
if lay_fbot == left and lay_ftop == right:
182-
logger.debug("- tube screen spans all layers. " "return nan")
182+
logger.debug("- tube screen spans all layers. return nan")
183183
return np.nan
184184
elif lay_ftop == right:
185185
logger.debug(
186-
"- tube screen top higher than top layer. " f"selected layer {lay_fbot}"
186+
f"- tube screen top higher than top layer. selected layer {lay_fbot}"
187187
)
188188
return lay_fbot
189189

190190
elif lay_fbot == left:
191191
logger.debug(
192-
"- tube screen bot lower than bottom layer. "
193-
f"selected layer {lay_ftop}"
192+
f"- tube screen bot lower than bottom layer. selected layer {lay_ftop}"
194193
)
195194
return lay_ftop
196195

197196
logger.debug(
198-
"- tube screen crosses layer boundary:\n"
199-
f" - layers: {lay_ftop}, {lay_fbot}"
197+
f"- tube screen crosses layer boundary:\n - layers: {lay_ftop}, {lay_fbot}"
200198
)
201199

202200
logger.debug(
203-
f"- tube screen spans {lay_fbot - lay_ftop +1} layers."
201+
f"- tube screen spans {lay_fbot - lay_ftop + 1} layers."
204202
" checking length per layer\n"
205203
" - length per layer:"
206204
)
@@ -215,7 +213,7 @@ def get_modellayer_from_screen_depth(ftop, fbot, zvec, left=-999, right=999):
215213
else:
216214
length_layers[i] = zvec[lay_ftop + i] - zvec[lay_ftop + 1 + i]
217215

218-
logger.debug(f" - lay {lay_ftop+i}: {length_layers[i]:.2f}")
216+
logger.debug(f" - lay {lay_ftop + i}: {length_layers[i]:.2f}")
219217

220218
# choose layer with biggest length
221219
rel_layer = np.argmax(length_layers)

Diff for: hydropandas/io/bro.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -477,7 +477,7 @@ def _get_gmw_from_bro_id(bro_id, retries=0):
477477
valid = req.text[(val_ind + 9) : (val_ind + 14)]
478478
if valid == "false" and retries < max_retries:
479479
logger.debug(
480-
f"got invalid response for {bro_id}, trying again {retries+1}/{max_retries}"
480+
f"got invalid response for {bro_id}, trying again {retries + 1}/{max_retries}"
481481
)
482482
return _get_gmw_from_bro_id(bro_id, retries=retries + 1)
483483
elif valid == "false":

Diff for: hydropandas/io/bronhouderportaal_bro.py

+12-15
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ def get_tube_nrs_from_xml(tree, ns):
3333
# get numbers of individual filters from XML file
3434
all_tube_nrs = []
3535
tubes = tree.findall(
36-
"isgmw:sourceDocument//" "isgmw:GMW_Construction//" "isgmw:monitoringTube", ns
36+
"isgmw:sourceDocument//isgmw:GMW_Construction//isgmw:monitoringTube", ns
3737
)
3838
for tube in tubes:
3939
all_tube_nrs.append(int(tube.find("isgmw:tubeNumber", ns).text))
@@ -200,7 +200,7 @@ def get_metadata_from_gmw(path_xml, tube_nr, full_meta=False):
200200
meta["deliveryContext"] = GMW_c.find("isgmw:deliveryContext", ns).text
201201
if GMW_c.find("isgmw:constructionStandard", ns) is not None:
202202
meta["constructionStandard"] = GMW_c.find(
203-
"isgmw:construction" "Standard", ns
203+
"isgmw:constructionStandard", ns
204204
).text
205205
if GMW_c.find("isgmw:initialFunction", ns) is not None:
206206
meta["initialFunction"] = GMW_c.find("isgmw:initialFunction", ns).text
@@ -220,12 +220,12 @@ def get_metadata_from_gmw(path_xml, tube_nr, full_meta=False):
220220
if full_meta:
221221
if (
222222
GMW_c.find(
223-
"isgmw:deliveredLocation//" "gmwcommon:horizontalPositioningMethod", ns
223+
"isgmw:deliveredLocation//gmwcommon:horizontalPositioningMethod", ns
224224
)
225225
is not None
226226
):
227227
meta["horizontalPositioningMethod"] = GMW_c.find(
228-
"isgmw:deliveredLocation//" "gmwcommon:horizontalPositioningMethod", ns
228+
"isgmw:deliveredLocation//gmwcommon:horizontalPositioningMethod", ns
229229
).text
230230
if (
231231
GMW_c.find(
@@ -241,13 +241,11 @@ def get_metadata_from_gmw(path_xml, tube_nr, full_meta=False):
241241
ns,
242242
).text
243243
if (
244-
GMW_c.find("isgmw:deliveredVerticalPosition//" "gmwcommon:offset", ns)
244+
GMW_c.find("isgmw:deliveredVerticalPosition//gmwcommon:offset", ns)
245245
is not None
246246
):
247247
meta["deliveredVerticalPosition_offset"] = float(
248-
GMW_c.find(
249-
"isgmw:deliveredVerticalPosition//" "gmwcommon:offset", ns
250-
).text
248+
GMW_c.find("isgmw:deliveredVerticalPosition//gmwcommon:offset", ns).text
251249
)
252250
if (
253251
GMW_c.find(
@@ -265,15 +263,15 @@ def get_metadata_from_gmw(path_xml, tube_nr, full_meta=False):
265263

266264
# ground_level
267265
glp_xml = GMW_c.find(
268-
"isgmw:deliveredVerticalPosition//" "gmwcommon:groundLevelPosition", ns
266+
"isgmw:deliveredVerticalPosition//gmwcommon:groundLevelPosition", ns
269267
)
270268
vert_datum = GMW_c.find(
271-
"isgmw:deliveredVerticalPosition//" "gmwcommon:verticalDatum", ns
269+
"isgmw:deliveredVerticalPosition//gmwcommon:verticalDatum", ns
272270
).text
273271
meta["unit"] = glp_xml.attrib["uom"] + " " + vert_datum
274272
if glp_xml.attrib["uom"].lower() != "m":
275273
logger.info(
276-
f'groundlevel unit is unexpected {glp_xml.attrib["uom"]}, ' "m is expected"
274+
f"groundlevel unit is unexpected {glp_xml.attrib['uom']}, m is expected"
277275
)
278276
if vert_datum.lower() != "nap":
279277
logger.info(f"datum has unexpected value {vert_datum}, NAP is expected")
@@ -315,7 +313,7 @@ def get_metadata_from_gmw(path_xml, tube_nr, full_meta=False):
315313
"isgmw:tubeTopPositioningMethod", ns
316314
).text
317315
if (
318-
tube.find("isgmw:materialUsed//" "gmwcommon:tubePackingMaterial", ns)
316+
tube.find("isgmw:materialUsed//gmwcommon:tubePackingMaterial", ns)
319317
is not None
320318
):
321319
meta["tubePackingMaterial"] = tube.find(
@@ -347,8 +345,7 @@ def get_metadata_from_gmw(path_xml, tube_nr, full_meta=False):
347345
screenLength_unit = screenLength_xml.attrib["uom"]
348346
if screenLength_unit != "m":
349347
logger.info(
350-
f'screenLength unit is unexpected {screenLength.attrib["uom"]},'
351-
"m expected"
348+
f"screenLength unit is unexpected {screenLength.attrib['uom']},m expected"
352349
)
353350

354351
plainTubePartLength_xml = tube.find(
@@ -366,7 +363,7 @@ def get_metadata_from_gmw(path_xml, tube_nr, full_meta=False):
366363

367364
if tube.find("isgmw:sedimentSumpPresent", ns).text.lower() in ["ja", "yes"]:
368365
sedimentSumpLength_xml = tube.find(
369-
"isgmw:sedimentSump//" "gmwcommon:sedimentSumpLength", ns
366+
"isgmw:sedimentSump//gmwcommon:sedimentSumpLength", ns
370367
)
371368
sedimentSumpLength = float(sedimentSumpLength_xml.text)
372369
sedimentSumpLength_unit = sedimentSumpLength_xml.attrib["uom"]

Diff for: hydropandas/io/dino.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,7 @@ def _read_dino_groundwater_metadata(f, line):
121121
meta_ts.pop(key)
122122

123123
obs_att = meta_tsi.copy()
124-
obs_att["name"] = f'{obs_att["monitoring_well"]}-{int(obs_att["tube_nr"]):03d}'
124+
obs_att["name"] = f"{obs_att['monitoring_well']}-{int(obs_att['tube_nr']):03d}"
125125
obs_att["metadata_available"] = True
126126
else:
127127
# no metadata

Diff for: hydropandas/io/fews.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -674,7 +674,7 @@ class of the observations, e.g. GroundwaterObs or WaterlvlObs
674674
nfiles = len(fnames)
675675
for j, ixml in enumerate(fnames):
676676
# print message
677-
logger.info(f"{j+1}/{nfiles} read {ixml}")
677+
logger.info(f"{j + 1}/{nfiles} read {ixml}")
678678

679679
# join directory to filename if provided
680680
if directory is None:

Diff for: hydropandas/io/knmi.py

+21-13
Original file line numberDiff line numberDiff line change
@@ -161,7 +161,7 @@ def get_knmi_timeseries_fname(
161161
elif meteo_var is None or meteo_var == "RD":
162162
# neerslagstation
163163
meteo_var = "RD"
164-
add_day = True
164+
add_day = False
165165
elif settings["interval"] == "daily":
166166
# meteo station
167167
add_day = True
@@ -175,7 +175,7 @@ def get_knmi_timeseries_fname(
175175
)
176176
if df.empty:
177177
logger.warning(
178-
f"No data for {meteo_var=} in {fname=} between" f"{start=} and {end=}."
178+
f"No data for {meteo_var=} in {fname=} between{start=} and {end=}."
179179
)
180180
else:
181181
ts, meta = interpret_knmi_file(
@@ -185,6 +185,7 @@ def get_knmi_timeseries_fname(
185185
start=start,
186186
end=end,
187187
add_day=add_day,
188+
add_hour=True,
188189
)
189190

190191
stn = meta["station"]
@@ -314,8 +315,7 @@ def get_knmi_timeseries_stn(
314315
and settings["use_api"]
315316
):
316317
message = (
317-
"No hourly evaporation data available through the api, "
318-
"set use_api=False."
318+
"No hourly evaporation data available through the api, set use_api=False."
319319
)
320320
raise ValueError(message)
321321
elif settings["fill_missing_obs"]:
@@ -346,7 +346,7 @@ def get_knmi_timeseries_stn(
346346
)
347347
if knmi_df.empty:
348348
logger.warning(
349-
f"No data for {meteo_var=} at {stn=} between" f"{start=} and {end=}."
349+
f"No data for {meteo_var=} at {stn=} between{start=} and {end=}."
350350
)
351351
if str(stn) in station_meta.index:
352352
meta = station_meta.loc[f"{stn}"].to_dict()
@@ -596,7 +596,7 @@ def fill_missing_measurements(
596596
)
597597
if new_end < end:
598598
end = new_end
599-
logger.info(f'changing end_date to {end.strftime("%Y-%m-%d")}')
599+
logger.info(f"changing end_date to {end.strftime('%Y-%m-%d')}")
600600

601601
# find missing values
602602
knmi_df = _add_missing_indices(knmi_df, stn, start, end)
@@ -743,7 +743,7 @@ def download_knmi_data(
743743
df, meta = get_knmi_daily_rainfall_api(
744744
stn=stn, start=start, end=end
745745
)
746-
add_day = True
746+
add_day = False
747747
else:
748748
# daily data from meteorological stations
749749
df, meta = get_knmi_daily_meteo_api(
@@ -758,6 +758,7 @@ def download_knmi_data(
758758
start=start,
759759
end=end,
760760
add_day=add_day,
761+
add_hour=True,
761762
)
762763

763764
except (RuntimeError, requests.ConnectionError) as e:
@@ -777,17 +778,20 @@ def download_knmi_data(
777778
elif meteo_var == "RD":
778779
# daily data from rainfall-stations
779780
df, meta = get_knmi_daily_rainfall_url(stn, stn_name)
781+
add_day = True
780782
else:
781783
# daily data from meteorological stations
782784
df, meta = get_knmi_daily_meteo_url(stn=stn)
785+
add_day = True
783786
if not df.empty:
784787
knmi_df, variables = interpret_knmi_file(
785788
df=df,
786789
meta=meta,
787790
meteo_var=meteo_var,
788791
start=start,
789792
end=end,
790-
add_day=True,
793+
add_day=add_day,
794+
add_hour=True,
791795
)
792796
except (ValueError, KeyError, pd.errors.EmptyDataError) as e:
793797
logger.error(f"{e} {msg}")
@@ -843,7 +847,8 @@ def get_knmi_daily_rainfall_api(
843847

844848
@lru_cache()
845849
def get_knmi_daily_rainfall_url(
846-
stn: int, stn_name: str
850+
stn: int,
851+
stn_name: str,
847852
) -> Tuple[pd.DataFrame, Dict[str, Any]]:
848853
"""download and read knmi daily rainfall.
849854
@@ -867,6 +872,7 @@ def get_knmi_daily_rainfall_url(
867872
additional information about the variables
868873
"""
869874

875+
stn = f"{stn:03d}" # make sure there are leading zeros
870876
url = (
871877
"https://cdn.knmi.nl/knmi/map/page/klimatologie/"
872878
f"gegevens/monv_reeksen/neerslaggeg_{stn_name}_{stn}.zip"
@@ -1195,7 +1201,7 @@ def interpret_knmi_file(
11951201
meteo_var: str,
11961202
start: Union[pd.Timestamp, None] = None,
11971203
end: Union[pd.Timestamp, None] = None,
1198-
add_day: bool = True,
1204+
add_day: bool = False,
11991205
add_hour: bool = True,
12001206
) -> Tuple[pd.DataFrame, Dict[str, Any]]:
12011207
"""interpret data from knmi by selecting meteo_var data and meta
@@ -1214,9 +1220,11 @@ def interpret_knmi_file(
12141220
end : pd.TimeStamp or None
12151221
end time of observations.
12161222
add_day : boolean, optional
1217-
add 1 day so that the timestamp is at the end of the period the data describes
1223+
add 1 day so that the timestamp is at the end of the period the data describes,
1224+
default is False, and has to be set per type of file.
12181225
add_hour : boolean, optional
1219-
add 1 hour to convert from UT to UT+1 (standard-time in the Netherlands)
1226+
add 1 hour to convert from UT to UT+1 (standard-time in the Netherlands),
1227+
default is True as this is usually the case.
12201228
12211229
12221230
Returns
@@ -1409,7 +1417,7 @@ def _check_latest_measurement_date_de_bilt(
14091417

14101418
logger.debug(
14111419
f"last {meteo_var} measurement available at the Bilt until {end_str} is from"
1412-
f' {last_measurement_date_debilt.strftime("%Y-%m-%d")}'
1420+
f" {last_measurement_date_debilt.strftime('%Y-%m-%d')}"
14131421
)
14141422
logger.debug(
14151423
f"assuming no {meteo_var} measurements are available at "

Diff for: hydropandas/io/modflow.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ def read_imod_results(
4747

4848
if ml.modelgrid.xoffset == 0 or ml.modelgrid.yoffset == 0:
4949
warnings.warn(
50-
"you probably want to set the xll and/or yll " "attributes of ml.modelgrid"
50+
"you probably want to set the xll and/or yll attributes of ml.modelgrid"
5151
)
5252

5353
if nlay is None:
@@ -130,7 +130,7 @@ def read_modflow_results(
130130
if ml.modelgrid.grid_type == "structured":
131131
if ml.modelgrid.xoffset == 0 or ml.modelgrid.yoffset == 0:
132132
warnings.warn(
133-
"you probably want to set the xll " "and/or yll attributes in DIS!"
133+
"you probably want to set the xll and/or yll attributes in DIS!"
134134
)
135135

136136
if isinstance(hds_arr, xr.DataArray):

Diff for: hydropandas/io/waterinfo.py

+1-3
Original file line numberDiff line numberDiff line change
@@ -73,9 +73,7 @@ def read_waterinfo_file(
7373
elif "WAARNEMINGTIJD" in df.columns:
7474
index_cols += ["WAARNEMINGTIJD"]
7575
else:
76-
raise KeyError(
77-
"expected column with WAARNEMINGSTIJD but could not find one"
78-
)
76+
raise KeyError("expected column with WAARNEMINGTIJD but could not find one")
7977

8078
df.index = pd.to_datetime(
8179
df[index_cols[0]] + " " + df[index_cols[1]], dayfirst=True

Diff for: hydropandas/obs_collection.py

+7-3
Original file line numberDiff line numberDiff line change
@@ -2098,7 +2098,7 @@ def get_obs(self, name=None, **kwargs):
20982098
f"multiple observations for given conditions {selected_obs.index}"
20992099
)
21002100

2101-
def to_excel(self, path, meta_sheet_name="metadata"):
2101+
def to_excel(self, path, meta_sheet_name="metadata", check_consistency=True):
21022102
"""Write an ObsCollection to an excel, the first sheet in the excel contains the
21032103
metadata, the other tabs are the timeseries of each observation.
21042104
@@ -2110,6 +2110,9 @@ def to_excel(self, path, meta_sheet_name="metadata"):
21102110
full path of xlsx file.
21112111
meta_sheet_name : str, optional
21122112
sheetname with metadata. The default is "metadata".
2113+
check_consistency : bool, optional
2114+
If True the consistency of the collection is checked. If set to False the excel file may be unreadable by hydropandas. The
2115+
default is True.
21132116
21142117
Raises
21152118
------
@@ -2129,8 +2132,9 @@ def to_excel(self, path, meta_sheet_name="metadata"):
21292132
If you don't want this consider using the `to_pickle` method.
21302133
"""
21312134

2132-
if not self._is_consistent():
2133-
raise RuntimeError("inconsistent observation collection")
2135+
if check_consistency:
2136+
if not self._is_consistent():
2137+
raise RuntimeError("inconsistent observation collection")
21342138

21352139
oc = self.copy(deep=True)
21362140

0 commit comments

Comments
 (0)