floodmodeller-api 0.5.0.post1__py3-none-any.whl → 0.5.2__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (119) hide show
  1. floodmodeller_api/__init__.py +11 -1
  2. floodmodeller_api/_base.py +55 -36
  3. floodmodeller_api/backup.py +15 -12
  4. floodmodeller_api/dat.py +191 -121
  5. floodmodeller_api/diff.py +4 -4
  6. floodmodeller_api/hydrology_plus/hydrology_plus_export.py +15 -14
  7. floodmodeller_api/ied.py +8 -10
  8. floodmodeller_api/ief.py +56 -42
  9. floodmodeller_api/ief_flags.py +1 -1
  10. floodmodeller_api/inp.py +7 -10
  11. floodmodeller_api/logs/lf.py +25 -26
  12. floodmodeller_api/logs/lf_helpers.py +20 -20
  13. floodmodeller_api/logs/lf_params.py +1 -5
  14. floodmodeller_api/mapping.py +11 -2
  15. floodmodeller_api/test/__init__.py +2 -2
  16. floodmodeller_api/test/conftest.py +2 -3
  17. floodmodeller_api/test/test_backup.py +2 -2
  18. floodmodeller_api/test/test_conveyance.py +13 -7
  19. floodmodeller_api/test/test_dat.py +168 -20
  20. floodmodeller_api/test/test_data/EX18_DAT_expected.json +164 -144
  21. floodmodeller_api/test/test_data/EX3_DAT_expected.json +6 -2
  22. floodmodeller_api/test/test_data/EX6_DAT_expected.json +12 -46
  23. floodmodeller_api/test/test_data/encoding_test_cp1252.dat +1081 -0
  24. floodmodeller_api/test/test_data/encoding_test_utf8.dat +1081 -0
  25. floodmodeller_api/test/test_data/integrated_bridge/AR_NoSP_NoBl_2O_NO_OneFRC.ied +33 -0
  26. floodmodeller_api/test/test_data/integrated_bridge/AR_vSP_25pc_1O.ied +32 -0
  27. floodmodeller_api/test/test_data/integrated_bridge/PL_vSP_25pc_1O.ied +34 -0
  28. floodmodeller_api/test/test_data/integrated_bridge/SBTwoFRCsStaggered.IED +32 -0
  29. floodmodeller_api/test/test_data/integrated_bridge/US_NoSP_NoBl_OR_RN.ied +28 -0
  30. floodmodeller_api/test/test_data/integrated_bridge/US_SP_NoBl_OR_frc_PT2-5_RN.ied +34 -0
  31. floodmodeller_api/test/test_data/integrated_bridge/US_fSP_NoBl_1O.ied +30 -0
  32. floodmodeller_api/test/test_data/integrated_bridge/US_nSP_NoBl_1O.ied +49 -0
  33. floodmodeller_api/test/test_data/integrated_bridge/US_vSP_NoBl_2O_Para.ied +35 -0
  34. floodmodeller_api/test/test_data/integrated_bridge.dat +40 -0
  35. floodmodeller_api/test/test_data/network.ied +2 -2
  36. floodmodeller_api/test/test_data/network_dat_expected.json +141 -243
  37. floodmodeller_api/test/test_data/network_ied_expected.json +2 -2
  38. floodmodeller_api/test/test_data/network_with_comments.ied +2 -2
  39. floodmodeller_api/test/test_data/structure_logs/EX17_expected.csv +4 -0
  40. floodmodeller_api/test/test_data/structure_logs/EX17_expected.json +69 -0
  41. floodmodeller_api/test/test_data/structure_logs/EX18_expected.csv +20 -0
  42. floodmodeller_api/test/test_data/structure_logs/EX18_expected.json +292 -0
  43. floodmodeller_api/test/test_data/structure_logs/EX6_expected.csv +4 -0
  44. floodmodeller_api/test/test_data/structure_logs/EX6_expected.json +35 -0
  45. floodmodeller_api/test/test_data/tabular_csv_outputs/network_zzn_flow.csv +182 -0
  46. floodmodeller_api/test/test_data/tabular_csv_outputs/network_zzn_fr.csv +182 -0
  47. floodmodeller_api/test/test_data/tabular_csv_outputs/network_zzn_mode.csv +182 -0
  48. floodmodeller_api/test/test_data/tabular_csv_outputs/network_zzn_stage.csv +182 -0
  49. floodmodeller_api/test/test_data/tabular_csv_outputs/network_zzn_state.csv +182 -0
  50. floodmodeller_api/test/test_data/tabular_csv_outputs/network_zzn_velocity.csv +182 -0
  51. floodmodeller_api/test/test_data/tabular_csv_outputs/network_zzx_left_fp_h.csv +182 -0
  52. floodmodeller_api/test/test_data/tabular_csv_outputs/network_zzx_left_fp_mode.csv +182 -0
  53. floodmodeller_api/test/test_data/tabular_csv_outputs/network_zzx_link_inflow.csv +182 -0
  54. floodmodeller_api/test/test_data/tabular_csv_outputs/network_zzx_max.csv +87 -0
  55. floodmodeller_api/test/test_data/tabular_csv_outputs/network_zzx_right_fp_h.csv +182 -0
  56. floodmodeller_api/test/test_data/tabular_csv_outputs/network_zzx_right_fp_mode.csv +182 -0
  57. floodmodeller_api/test/test_flowtimeprofile.py +2 -2
  58. floodmodeller_api/test/test_hydrology_plus_export.py +4 -2
  59. floodmodeller_api/test/test_ied.py +3 -3
  60. floodmodeller_api/test/test_ief.py +12 -4
  61. floodmodeller_api/test/test_inp.py +2 -2
  62. floodmodeller_api/test/test_integrated_bridge.py +159 -0
  63. floodmodeller_api/test/test_json.py +14 -13
  64. floodmodeller_api/test/test_logs_lf.py +50 -29
  65. floodmodeller_api/test/test_read_file.py +1 -0
  66. floodmodeller_api/test/test_river.py +12 -12
  67. floodmodeller_api/test/test_tool.py +8 -5
  68. floodmodeller_api/test/test_toolbox_structure_log.py +148 -158
  69. floodmodeller_api/test/test_xml2d.py +14 -16
  70. floodmodeller_api/test/test_zz.py +143 -0
  71. floodmodeller_api/to_from_json.py +9 -9
  72. floodmodeller_api/tool.py +15 -11
  73. floodmodeller_api/toolbox/example_tool.py +5 -1
  74. floodmodeller_api/toolbox/model_build/add_siltation_definition.py +13 -9
  75. floodmodeller_api/toolbox/model_build/structure_log/structure_log.py +500 -194
  76. floodmodeller_api/toolbox/model_build/structure_log_definition.py +5 -1
  77. floodmodeller_api/units/__init__.py +15 -0
  78. floodmodeller_api/units/_base.py +87 -20
  79. floodmodeller_api/units/_helpers.py +343 -0
  80. floodmodeller_api/units/boundaries.py +59 -71
  81. floodmodeller_api/units/comment.py +1 -1
  82. floodmodeller_api/units/conduits.py +57 -54
  83. floodmodeller_api/units/connectors.py +112 -0
  84. floodmodeller_api/units/controls.py +107 -0
  85. floodmodeller_api/units/conveyance.py +1 -1
  86. floodmodeller_api/units/iic.py +2 -9
  87. floodmodeller_api/units/losses.py +44 -45
  88. floodmodeller_api/units/sections.py +52 -51
  89. floodmodeller_api/units/structures.py +361 -531
  90. floodmodeller_api/units/units.py +27 -26
  91. floodmodeller_api/units/unsupported.py +5 -7
  92. floodmodeller_api/units/variables.py +2 -2
  93. floodmodeller_api/urban1d/_base.py +13 -17
  94. floodmodeller_api/urban1d/conduits.py +11 -21
  95. floodmodeller_api/urban1d/general_parameters.py +1 -1
  96. floodmodeller_api/urban1d/junctions.py +7 -11
  97. floodmodeller_api/urban1d/losses.py +13 -17
  98. floodmodeller_api/urban1d/outfalls.py +18 -22
  99. floodmodeller_api/urban1d/raingauges.py +5 -10
  100. floodmodeller_api/urban1d/subsections.py +5 -4
  101. floodmodeller_api/urban1d/xsections.py +14 -17
  102. floodmodeller_api/util.py +23 -6
  103. floodmodeller_api/validation/parameters.py +7 -3
  104. floodmodeller_api/validation/urban_parameters.py +1 -4
  105. floodmodeller_api/validation/validation.py +11 -5
  106. floodmodeller_api/version.py +1 -1
  107. floodmodeller_api/xml2d.py +27 -31
  108. floodmodeller_api/xml2d_template.py +1 -1
  109. floodmodeller_api/zz.py +539 -0
  110. {floodmodeller_api-0.5.0.post1.dist-info → floodmodeller_api-0.5.2.dist-info}/LICENSE.txt +1 -1
  111. {floodmodeller_api-0.5.0.post1.dist-info → floodmodeller_api-0.5.2.dist-info}/METADATA +30 -16
  112. {floodmodeller_api-0.5.0.post1.dist-info → floodmodeller_api-0.5.2.dist-info}/RECORD +116 -83
  113. {floodmodeller_api-0.5.0.post1.dist-info → floodmodeller_api-0.5.2.dist-info}/WHEEL +1 -1
  114. floodmodeller_api/test/test_zzn.py +0 -36
  115. floodmodeller_api/units/helpers.py +0 -123
  116. floodmodeller_api/zzn.py +0 -414
  117. /floodmodeller_api/test/test_data/{network_from_tabularCSV.csv → tabular_csv_outputs/network_zzn_max.csv} +0 -0
  118. {floodmodeller_api-0.5.0.post1.dist-info → floodmodeller_api-0.5.2.dist-info}/entry_points.txt +0 -0
  119. {floodmodeller_api-0.5.0.post1.dist-info → floodmodeller_api-0.5.2.dist-info}/top_level.txt +0 -0
@@ -3,14 +3,15 @@ from pathlib import Path
3
3
  import pytest
4
4
 
5
5
  from floodmodeller_api import XML2D
6
+ from floodmodeller_api.util import FloodModellerAPIError
6
7
 
7
8
 
8
- @pytest.fixture
9
+ @pytest.fixture()
9
10
  def xml_fp(test_workspace):
10
11
  return Path(test_workspace, "Domain1_Q.xml")
11
12
 
12
13
 
13
- @pytest.fixture
14
+ @pytest.fixture()
14
15
  def data_before(xml_fp):
15
16
  return XML2D(xml_fp)._write()
16
17
 
@@ -25,7 +26,7 @@ def test_xml2d_link_dtm_changes(xml_fp, data_before):
25
26
  """XML2D: Test changing and reverting link1d file and dtm makes no changes"""
26
27
  x2d = XML2D(xml_fp)
27
28
  prev_link = x2d.link1d[0]["link"]
28
- domain = list(x2d.domains)[0]
29
+ domain = next(iter(x2d.domains))
29
30
  prev_dtm = x2d.domains[domain]["topography"]
30
31
 
31
32
  x2d.link1d[0]["link"] = ["new_link"]
@@ -56,10 +57,8 @@ def test_xml2d_change_revert_elem_topography():
56
57
  """XML2D: Check that when we change an existing element
57
58
  that it is actually adding it and that it is being reverted."""
58
59
  x2d = XML2D()
59
- domain = list(x2d.domains)[0]
60
- orig_topography = []
61
- for item in x2d.domains[domain]["topography"]:
62
- orig_topography.append(str(item))
60
+ domain = next(iter(x2d.domains))
61
+ orig_topography = [str(item) for item in x2d.domains[domain]["topography"]]
63
62
  orig_xml = x2d._write()
64
63
  x2d.domains[domain]["topography"][0] = "my/new/topography"
65
64
 
@@ -74,7 +73,7 @@ def test_xml2d_add_remove_branch_roughness():
74
73
  """XML2D: Check that we can actually add a branch and that
75
74
  it is being added and passes validation (i.e write)"""
76
75
  x2d = XML2D()
77
- domain = list(x2d.domains)[0]
76
+ domain = next(iter(x2d.domains))
78
77
  orig_xml = x2d._write()
79
78
  x2d.domains[domain]["roughness"] = []
80
79
  x2d.domains[domain]["roughness"].append(
@@ -91,7 +90,7 @@ def test_xml2d_append_remove_branch_roughness():
91
90
  """XML2D: Check that we can append an extra branch to preexisting branch
92
91
  so that it passes validation"""
93
92
  x2d = XML2D()
94
- domain = list(x2d.domains)[0]
93
+ domain = next(iter(x2d.domains))
95
94
  x2d.domains[domain]["roughness"] = []
96
95
  x2d.domains[domain]["roughness"].append(
97
96
  {"type": "file", "law": "manning", "value": "my/roughness/file.shp"},
@@ -115,7 +114,7 @@ def test_xml2d_append_remove_branch_roughness():
115
114
  def test_xml2d_reorder_elem_computational_area_wrong_position():
116
115
  """XML2D: Check that if we add ??? in the wrong position does it reorder"""
117
116
  x2d = XML2D()
118
- domain = list(x2d.domains)[0]
117
+ domain = next(iter(x2d.domains))
119
118
  x2d.domains[domain]["computational_area"] = {
120
119
  "yll": ...,
121
120
  "xll": ...,
@@ -132,20 +131,19 @@ def test_xml2d_reorder_elem_computational_area_wrong_position():
132
131
  x2d.domains[domain]["computational_area"]["ncols"] = 12
133
132
  x2d.domains[domain]["computational_area"]["nrows"] = 42
134
133
  x2d.domains[domain]["computational_area"]["rotation"] = 3.14159
135
-
136
134
  x2d.domains[domain]["run_data"]["upwind"] = "upwind value"
137
135
  x2d.domains[domain]["run_data"]["wall"] = "Humpty Dumpty"
138
-
139
- # TODO: Add check that this should fail validation if in the wrong order
140
- # # how do I check that something fails?
141
-
142
136
  assert x2d._write()
143
137
 
138
+ x2d.domains[domain]["run_data"]["upwind123"] = "upwind value"
139
+ with pytest.raises(FloodModellerAPIError):
140
+ assert x2d._write()
141
+
144
142
 
145
143
  def test_xml2d_update_value(xml_fp, data_before):
146
144
  """XML2D: Test changing and reverting link1d file and dtm makes no changes"""
147
145
  x2d = XML2D(xml_fp)
148
- domain = list(x2d.domains)[0]
146
+ domain = next(iter(x2d.domains))
149
147
  x2d.domains[domain]["run_data"]["scheme"] = "TVD"
150
148
 
151
149
  assert x2d._write()
@@ -0,0 +1,143 @@
1
+ # type: ignore
2
+ # ignored because the output from _ZZ.to_dataframe() is only a series in special cases
3
+
4
+ from pathlib import Path
5
+
6
+ import pandas as pd
7
+ import pytest
8
+
9
+ from floodmodeller_api import IEF, ZZN, ZZX
10
+
11
+
12
+ @pytest.fixture()
13
+ def zzn(test_workspace: Path) -> ZZN:
14
+ path = test_workspace / "network.zzn"
15
+ return ZZN(path)
16
+
17
+
18
+ @pytest.fixture()
19
+ def zzx(test_workspace: Path) -> ZZX:
20
+ path = test_workspace / "network.zzx"
21
+ return ZZX(path)
22
+
23
+
24
+ @pytest.fixture()
25
+ def ief(test_workspace: Path) -> IEF:
26
+ path = test_workspace / "network.ief"
27
+ return IEF(path)
28
+
29
+
30
+ @pytest.fixture()
31
+ def folder(test_workspace: Path) -> Path:
32
+ return test_workspace / "tabular_csv_outputs"
33
+
34
+
35
+ @pytest.mark.parametrize(
36
+ ("csv", "file"),
37
+ [
38
+ ("network_zzn_max.csv", "zzn"),
39
+ ("network_zzx_max.csv", "zzx"),
40
+ ],
41
+ )
42
+ def test_max(zzn: ZZN, zzx: ZZX, folder: Path, csv: str, file: str):
43
+ file_obj = zzn if file == "zzn" else zzx
44
+ expected = pd.read_csv(folder / csv, index_col=0)
45
+
46
+ actual = file_obj.to_dataframe(result_type="max")
47
+ pd.testing.assert_frame_equal(actual, expected, atol=1e-3, check_dtype=False)
48
+
49
+
50
+ @pytest.mark.parametrize(
51
+ ("variable", "csv", "file"),
52
+ [
53
+ # zzn
54
+ ("Flow", "network_zzn_flow.csv", "zzn"),
55
+ ("Stage", "network_zzn_stage.csv", "zzn"),
56
+ ("Froude", "network_zzn_fr.csv", "zzn"),
57
+ ("Velocity", "network_zzn_velocity.csv", "zzn"),
58
+ ("Mode", "network_zzn_mode.csv", "zzn"),
59
+ ("State", "network_zzn_state.csv", "zzn"),
60
+ # zzx
61
+ ("Left FP h", "network_zzx_left_fp_h.csv", "zzx"),
62
+ ("Link inflow", "network_zzx_link_inflow.csv", "zzx"),
63
+ ("Right FP h", "network_zzx_right_fp_h.csv", "zzx"),
64
+ ("Right FP mode", "network_zzx_right_fp_mode.csv", "zzx"),
65
+ ("Left FP mode", "network_zzx_left_fp_mode.csv", "zzx"),
66
+ ],
67
+ )
68
+ def test_all_timesteps(zzn: ZZN, zzx: ZZX, folder: Path, variable: str, csv: str, file: str):
69
+ file_obj = zzn if file == "zzn" else zzx
70
+ suffix = f"_{variable}"
71
+ expected = pd.read_csv(folder / csv, index_col=0)
72
+
73
+ actual_1 = file_obj.to_dataframe(variable=variable)
74
+ actual_1.index = actual_1.index.round(3)
75
+ pd.testing.assert_frame_equal(actual_1, expected, atol=1e-3, check_dtype=False)
76
+
77
+ actual_2 = file_obj.to_dataframe()[variable]
78
+ actual_2.index = actual_2.index.round(3)
79
+ pd.testing.assert_frame_equal(actual_2, expected, atol=1e-3, check_dtype=False)
80
+
81
+ actual_3 = file_obj.to_dataframe(multilevel_header=False).filter(like=suffix, axis=1)
82
+ actual_3.index = actual_3.index.round(3)
83
+ actual_3.columns = [x.removesuffix(suffix) for x in actual_3.columns]
84
+ pd.testing.assert_frame_equal(actual_3, expected, atol=1e-3, check_dtype=False)
85
+
86
+ actual_4 = file_obj.to_dataframe(variable=variable, multilevel_header=False)
87
+ actual_4.index = actual_4.index.round(3)
88
+ actual_4.columns = [x.removesuffix(suffix) for x in actual_4.columns]
89
+ pd.testing.assert_frame_equal(actual_4, expected, atol=1e-3, check_dtype=False)
90
+
91
+
92
+ def test_zzn_include_time(zzn: ZZN):
93
+ zzn_df = zzn.to_dataframe(result_type="max", variable="flow", include_time=True)
94
+ actual = zzn_df.loc["resin", ["Max Flow", "Max Flow Time(hrs)"]].to_numpy()
95
+ assert actual[0] == pytest.approx(7.296, abs=0.001)
96
+ assert actual[1] == 9
97
+
98
+
99
+ def test_zzn_from_ief(zzn: ZZN, ief: IEF):
100
+ zzn_df = zzn.to_dataframe()
101
+ zzn_from_ief = ief.get_results().to_dataframe()
102
+ pd.testing.assert_frame_equal(zzn_df, zzn_from_ief)
103
+
104
+
105
+ def test_zzn_to_csv(zzn: ZZN, tmp_path: Path, test_workspace: Path):
106
+ # default
107
+ zzn.export_to_csv()
108
+ path = test_workspace / "network.csv"
109
+ assert path.exists()
110
+ path.unlink()
111
+
112
+ # absolute
113
+ zzn.export_to_csv(tmp_path / "test.csv")
114
+ path = tmp_path / "test.csv"
115
+ assert path.exists()
116
+ path.unlink()
117
+
118
+ # relative
119
+ zzn.export_to_csv("test.csv")
120
+ path = zzn.filepath.parent / "test.csv"
121
+ assert path.exists()
122
+ path.unlink()
123
+
124
+ # folder
125
+ zzn.export_to_csv(tmp_path)
126
+ path = tmp_path / "network.csv"
127
+ assert path.exists()
128
+ path.unlink()
129
+
130
+ # doesn't exist
131
+ zzn.export_to_csv("test")
132
+ path = test_workspace / "test/network.csv"
133
+ assert path.exists()
134
+ path.unlink()
135
+
136
+
137
+ def test_meta_is_read_only(zzx: ZZN):
138
+ assert dict(zzx.meta) == zzx._meta
139
+
140
+ with pytest.raises(TypeError):
141
+ zzx.meta["variables"] = "hi"
142
+
143
+ zzx._meta["variables"] = "hi"
@@ -1,6 +1,6 @@
1
1
  """
2
2
  Flood Modeller Python API
3
- Copyright (C) 2024 Jacobs U.K. Limited
3
+ Copyright (C) 2025 Jacobs U.K. Limited
4
4
 
5
5
  This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License
6
6
  as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
@@ -162,15 +162,15 @@ def recursive_from_json(obj: dict | Any) -> Any:
162
162
  return api_class_mapping[class_type].from_json(obj)
163
163
 
164
164
  if "class" in obj and obj["class"] == "pandas.DataFrame":
165
- df = pd.DataFrame.from_dict(obj["object"])
166
- df.index = convert_dataframe_index(df.index)
167
- return df
165
+ reconstructed_df = pd.DataFrame.from_dict(obj["object"])
166
+ reconstructed_df.index = convert_dataframe_index(reconstructed_df.index)
167
+ return reconstructed_df
168
168
  if "class" in obj and obj["class"] == "pandas.Series":
169
- sr = pd.Series(obj["object"])
170
- sr.index = convert_dataframe_index(sr.index)
171
- sr.index.name = obj["index_name"]
172
- sr.name = obj["variable_name"]
173
- return sr
169
+ reconstructed_sr = pd.Series(obj["object"])
170
+ reconstructed_sr.index = convert_dataframe_index(reconstructed_sr.index)
171
+ reconstructed_sr.index.name = obj["index_name"]
172
+ reconstructed_sr.name = obj["variable_name"]
173
+ return reconstructed_sr
174
174
 
175
175
  if "python_set" in obj:
176
176
  return set(obj["python_set"])
floodmodeller_api/tool.py CHANGED
@@ -1,9 +1,11 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  import argparse
4
+ import logging
4
5
  import sys
5
6
  import tkinter as tk
6
7
  from dataclasses import dataclass
8
+ from typing import ClassVar
7
9
 
8
10
 
9
11
  @dataclass()
@@ -150,13 +152,11 @@ class Gui:
150
152
  entry = tk.Entry(self.master, validate="key")
151
153
  entry.config(validatecommand=(entry.register(validate_float), "%P"))
152
154
  else:
153
- raise ValueError("Invalid data type")
155
+ msg = "Invalid data type"
156
+ raise ValueError(msg)
154
157
  entry.pack()
155
158
  self.root_entries[name] = entry
156
159
 
157
- # TODO: Add a progress bar if appropriate
158
- # TODO: Present some useful information: either tool outputs or logs
159
-
160
160
  def run_gui_callback(self):
161
161
  """
162
162
  Method to run the gui callback function.
@@ -209,28 +209,31 @@ class FMTool:
209
209
 
210
210
  """
211
211
 
212
- parameters: list[Parameter] = []
212
+ parameters: ClassVar[list[Parameter]] = []
213
213
 
214
214
  @property
215
215
  def name(self):
216
216
  """
217
217
  A property method to ensure a tool name is provided in child class. Overwritten by child.
218
218
  """
219
- raise NotImplementedError("Tools need a name")
219
+ msg = "Tools need a name"
220
+ raise NotImplementedError(msg)
220
221
 
221
222
  @property
222
223
  def description(self):
223
224
  """
224
225
  A property method to ensure a tool description is provided in child class. Overwritten by child.
225
226
  """
226
- raise NotImplementedError("Tools need a description")
227
+ msg = "Tools need a description"
228
+ raise NotImplementedError(msg)
227
229
 
228
230
  @property
229
231
  def tool_function(self):
230
232
  """
231
233
  A property method to ensure an tool_function is provided in child class. Overwritten by child.
232
234
  """
233
- raise NotImplementedError("You must provide an entry point function")
235
+ msg = "You must provide an entry point function"
236
+ raise NotImplementedError(msg)
234
237
 
235
238
  def __init__(self):
236
239
  self.check_parameters()
@@ -248,7 +251,8 @@ class FMTool:
248
251
  params = []
249
252
  for parameter in self.parameters:
250
253
  if parameter.name in params:
251
- raise ValueError("Parameter names must be unique")
254
+ msg = "Parameter names must be unique"
255
+ raise ValueError(msg)
252
256
  params.append(parameter.name)
253
257
 
254
258
  # This is defined as a class method because of the use of **kwargs
@@ -302,9 +306,9 @@ class FMTool:
302
306
  value = getattr(args, input_param.name)
303
307
  input_kwargs[input_param.name] = input_param.dtype(value)
304
308
 
305
- print(f"Running {self.name}")
309
+ logging.info("Running %s", self.name)
306
310
  self.run(**input_kwargs)
307
- print("Completed")
311
+ logging.info("Completed")
308
312
  # Return nothing
309
313
 
310
314
  def generate_gui(self):
@@ -1,4 +1,8 @@
1
1
  # Import the FMTool and Parameter classes to define the tool and parameters
2
+ from __future__ import annotations
3
+
4
+ from typing import ClassVar
5
+
2
6
  from floodmodeller_api.tool import FMTool, Parameter
3
7
 
4
8
 
@@ -19,7 +23,7 @@ class SumTool(FMTool):
19
23
  # Add the tool description (required)
20
24
  description = "A basic tool to add two numbers together"
21
25
  # Add the parameters (one per function argument):
22
- parameters = [
26
+ parameters: ClassVar[list[Parameter]] = [
23
27
  # Add parameters using the Parameter class
24
28
  Parameter(
25
29
  name="a",
@@ -1,26 +1,30 @@
1
- """ This function allows you to raise the minimum bed level 300mm across all sections in a DAT file (i.e siltation) """
1
+ """This function allows you to raise the minimum bed level 300mm across all sections in a DAT file (i.e siltation)"""
2
2
 
3
- # Import modules
4
- from pathlib import Path
3
+ from __future__ import annotations
4
+
5
+ from typing import TYPE_CHECKING, ClassVar
5
6
 
6
7
  from floodmodeller_api import DAT
7
8
  from floodmodeller_api.tool import FMTool, Parameter
8
9
  from floodmodeller_api.units import RIVER
9
10
 
11
+ if TYPE_CHECKING:
12
+ from pathlib import Path
13
+
10
14
 
11
15
  # Define the function
12
16
  def raise_section_bed_levels(dat_input: Path, dat_output: Path, siltation: float):
13
17
  dat = DAT(dat_input) # Initialise DAT class
14
18
 
15
- for _, section in dat.sections.items(): # iterate through all river sections
19
+ for section in dat.sections.values(): # iterate through all river sections
16
20
  if not isinstance(section, RIVER):
17
21
  # Skip any non river type units (e.g. interpolates)
18
22
  continue
19
- df = section.data # get section data
20
- min_elevation = df["Y"].min() # get minimum cross section elevation
23
+ section_data = section.data # get section data
24
+ min_elevation = section_data["Y"].min() # get minimum cross section elevation
21
25
  raised_bed = min_elevation + siltation # define new lowest bed level
22
- df.loc[
23
- df["Y"] < raised_bed,
26
+ section_data.loc[
27
+ section_data["Y"] < raised_bed,
24
28
  "Y",
25
29
  ] = raised_bed # Raise any levels lower than this to the new lowest level
26
30
 
@@ -70,7 +74,7 @@ class AddSiltation(FMTool):
70
74
  description = (
71
75
  "Tool to add a set amount of siltation to raise bed levels of river sections in a DAT file"
72
76
  )
73
- parameters = [
77
+ parameters: ClassVar[list[Parameter]] = [
74
78
  Parameter(
75
79
  name="dat_input",
76
80
  dtype=str,