floodmodeller-api 0.4.2__py3-none-any.whl → 0.4.3__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.
- floodmodeller_api/__init__.py +8 -9
- floodmodeller_api/_base.py +184 -176
- floodmodeller_api/backup.py +273 -273
- floodmodeller_api/dat.py +909 -838
- floodmodeller_api/diff.py +136 -119
- floodmodeller_api/ied.py +307 -311
- floodmodeller_api/ief.py +647 -646
- floodmodeller_api/ief_flags.py +253 -253
- floodmodeller_api/inp.py +266 -268
- floodmodeller_api/libs/libifcoremd.dll +0 -0
- floodmodeller_api/libs/libifcoremt.so.5 +0 -0
- floodmodeller_api/libs/libifport.so.5 +0 -0
- floodmodeller_api/{libmmd.dll → libs/libimf.so} +0 -0
- floodmodeller_api/libs/libintlc.so.5 +0 -0
- floodmodeller_api/libs/libmmd.dll +0 -0
- floodmodeller_api/libs/libsvml.so +0 -0
- floodmodeller_api/libs/libzzn_read.so +0 -0
- floodmodeller_api/libs/zzn_read.dll +0 -0
- floodmodeller_api/logs/__init__.py +2 -2
- floodmodeller_api/logs/lf.py +320 -314
- floodmodeller_api/logs/lf_helpers.py +354 -346
- floodmodeller_api/logs/lf_params.py +643 -529
- floodmodeller_api/mapping.py +84 -0
- floodmodeller_api/test/__init__.py +4 -4
- floodmodeller_api/test/conftest.py +9 -8
- floodmodeller_api/test/test_backup.py +117 -117
- floodmodeller_api/test/test_dat.py +221 -92
- floodmodeller_api/test/test_data/All Units 4_6.DAT +1081 -1081
- floodmodeller_api/test/test_data/All Units 4_6.feb +1081 -1081
- floodmodeller_api/test/test_data/BRIDGE.DAT +926 -926
- floodmodeller_api/test/test_data/Culvert_Inlet_Outlet.dat +36 -36
- floodmodeller_api/test/test_data/Culvert_Inlet_Outlet.feb +36 -36
- floodmodeller_api/test/test_data/DamBreakADI.xml +52 -52
- floodmodeller_api/test/test_data/DamBreakFAST.xml +58 -58
- floodmodeller_api/test/test_data/DamBreakFAST_dy.xml +53 -53
- floodmodeller_api/test/test_data/DamBreakTVD.xml +55 -55
- floodmodeller_api/test/test_data/DefenceBreach.xml +53 -53
- floodmodeller_api/test/test_data/DefenceBreachFAST.xml +60 -60
- floodmodeller_api/test/test_data/DefenceBreachFAST_dy.xml +55 -55
- floodmodeller_api/test/test_data/Domain1+2_QH.xml +76 -76
- floodmodeller_api/test/test_data/Domain1_H.xml +41 -41
- floodmodeller_api/test/test_data/Domain1_Q.xml +41 -41
- floodmodeller_api/test/test_data/Domain1_Q_FAST.xml +48 -48
- floodmodeller_api/test/test_data/Domain1_Q_FAST_dy.xml +48 -48
- floodmodeller_api/test/test_data/Domain1_Q_xml_expected.json +263 -0
- floodmodeller_api/test/test_data/Domain1_W.xml +41 -41
- floodmodeller_api/test/test_data/EX1.DAT +321 -321
- floodmodeller_api/test/test_data/EX1.ext +107 -107
- floodmodeller_api/test/test_data/EX1.feb +320 -320
- floodmodeller_api/test/test_data/EX1.gxy +107 -107
- floodmodeller_api/test/test_data/EX17.DAT +421 -422
- floodmodeller_api/test/test_data/EX17.ext +213 -213
- floodmodeller_api/test/test_data/EX17.feb +422 -422
- floodmodeller_api/test/test_data/EX18.DAT +375 -375
- floodmodeller_api/test/test_data/EX18_DAT_expected.json +3876 -0
- floodmodeller_api/test/test_data/EX2.DAT +302 -302
- floodmodeller_api/test/test_data/EX3.DAT +926 -926
- floodmodeller_api/test/test_data/EX3_DAT_expected.json +16235 -0
- floodmodeller_api/test/test_data/EX3_IEF_expected.json +61 -0
- floodmodeller_api/test/test_data/EX6.DAT +2084 -2084
- floodmodeller_api/test/test_data/EX6.ext +532 -532
- floodmodeller_api/test/test_data/EX6.feb +2084 -2084
- floodmodeller_api/test/test_data/EX6_DAT_expected.json +31647 -0
- floodmodeller_api/test/test_data/Event Data Example.DAT +336 -336
- floodmodeller_api/test/test_data/Event Data Example.ext +107 -107
- floodmodeller_api/test/test_data/Event Data Example.feb +336 -336
- floodmodeller_api/test/test_data/Linked1D2D.xml +52 -52
- floodmodeller_api/test/test_data/Linked1D2DFAST.xml +53 -53
- floodmodeller_api/test/test_data/Linked1D2DFAST_dy.xml +48 -48
- floodmodeller_api/test/test_data/Linked1D2D_xml_expected.json +313 -0
- floodmodeller_api/test/test_data/blockage.dat +50 -50
- floodmodeller_api/test/test_data/blockage.ext +45 -45
- floodmodeller_api/test/test_data/blockage.feb +9 -9
- floodmodeller_api/test/test_data/blockage.gxy +71 -71
- floodmodeller_api/test/test_data/defaultUnits.dat +127 -127
- floodmodeller_api/test/test_data/defaultUnits.ext +45 -45
- floodmodeller_api/test/test_data/defaultUnits.feb +9 -9
- floodmodeller_api/test/test_data/defaultUnits.fmpx +58 -58
- floodmodeller_api/test/test_data/defaultUnits.gxy +85 -85
- floodmodeller_api/test/test_data/ex3.ief +20 -20
- floodmodeller_api/test/test_data/ex3.lf1 +2800 -2800
- floodmodeller_api/test/test_data/ex4.DAT +1374 -1374
- floodmodeller_api/test/test_data/ex4_changed.DAT +1374 -1374
- floodmodeller_api/test/test_data/example1.inp +329 -329
- floodmodeller_api/test/test_data/example2.inp +158 -158
- floodmodeller_api/test/test_data/example3.inp +297 -297
- floodmodeller_api/test/test_data/example4.inp +388 -388
- floodmodeller_api/test/test_data/example5.inp +147 -147
- floodmodeller_api/test/test_data/example6.inp +154 -154
- floodmodeller_api/test/test_data/jump.dat +176 -176
- floodmodeller_api/test/test_data/network.dat +1374 -1374
- floodmodeller_api/test/test_data/network.ext +45 -45
- floodmodeller_api/test/test_data/network.exy +1 -1
- floodmodeller_api/test/test_data/network.feb +45 -45
- floodmodeller_api/test/test_data/network.ied +45 -45
- floodmodeller_api/test/test_data/network.ief +20 -20
- floodmodeller_api/test/test_data/network.inp +147 -147
- floodmodeller_api/test/test_data/network.pxy +57 -57
- floodmodeller_api/test/test_data/network.zzd +122 -122
- floodmodeller_api/test/test_data/network_dat_expected.json +21837 -0
- floodmodeller_api/test/test_data/network_from_tabularCSV.csv +87 -87
- floodmodeller_api/test/test_data/network_ied_expected.json +287 -0
- floodmodeller_api/test/test_data/rnweir.dat +9 -9
- floodmodeller_api/test/test_data/rnweir.ext +45 -45
- floodmodeller_api/test/test_data/rnweir.feb +9 -9
- floodmodeller_api/test/test_data/rnweir.gxy +45 -45
- floodmodeller_api/test/test_data/rnweir_default.dat +74 -74
- floodmodeller_api/test/test_data/rnweir_default.ext +45 -45
- floodmodeller_api/test/test_data/rnweir_default.feb +9 -9
- floodmodeller_api/test/test_data/rnweir_default.fmpx +58 -58
- floodmodeller_api/test/test_data/rnweir_default.gxy +53 -53
- floodmodeller_api/test/test_data/unit checks.dat +16 -16
- floodmodeller_api/test/test_ied.py +29 -29
- floodmodeller_api/test/test_ief.py +125 -24
- floodmodeller_api/test/test_inp.py +47 -48
- floodmodeller_api/test/test_json.py +114 -0
- floodmodeller_api/test/test_logs_lf.py +48 -51
- floodmodeller_api/test/test_tool.py +165 -154
- floodmodeller_api/test/test_toolbox_structure_log.py +234 -239
- floodmodeller_api/test/test_xml2d.py +151 -156
- floodmodeller_api/test/test_zzn.py +36 -34
- floodmodeller_api/to_from_json.py +218 -0
- floodmodeller_api/tool.py +332 -330
- floodmodeller_api/toolbox/__init__.py +5 -5
- floodmodeller_api/toolbox/example_tool.py +45 -45
- floodmodeller_api/toolbox/model_build/__init__.py +2 -2
- floodmodeller_api/toolbox/model_build/add_siltation_definition.py +100 -94
- floodmodeller_api/toolbox/model_build/structure_log/__init__.py +1 -1
- floodmodeller_api/toolbox/model_build/structure_log/structure_log.py +287 -289
- floodmodeller_api/toolbox/model_build/structure_log_definition.py +76 -72
- floodmodeller_api/units/__init__.py +10 -10
- floodmodeller_api/units/_base.py +214 -209
- floodmodeller_api/units/boundaries.py +467 -469
- floodmodeller_api/units/comment.py +52 -55
- floodmodeller_api/units/conduits.py +382 -403
- floodmodeller_api/units/helpers.py +123 -132
- floodmodeller_api/units/iic.py +107 -101
- floodmodeller_api/units/losses.py +305 -308
- floodmodeller_api/units/sections.py +444 -445
- floodmodeller_api/units/structures.py +1690 -1684
- floodmodeller_api/units/units.py +93 -102
- floodmodeller_api/units/unsupported.py +44 -44
- floodmodeller_api/units/variables.py +87 -89
- floodmodeller_api/urban1d/__init__.py +11 -11
- floodmodeller_api/urban1d/_base.py +188 -177
- floodmodeller_api/urban1d/conduits.py +93 -85
- floodmodeller_api/urban1d/general_parameters.py +58 -58
- floodmodeller_api/urban1d/junctions.py +81 -79
- floodmodeller_api/urban1d/losses.py +81 -74
- floodmodeller_api/urban1d/outfalls.py +114 -107
- floodmodeller_api/urban1d/raingauges.py +111 -108
- floodmodeller_api/urban1d/subsections.py +92 -93
- floodmodeller_api/urban1d/xsections.py +147 -141
- floodmodeller_api/util.py +77 -21
- floodmodeller_api/validation/parameters.py +660 -660
- floodmodeller_api/validation/urban_parameters.py +388 -404
- floodmodeller_api/validation/validation.py +110 -112
- floodmodeller_api/version.py +1 -1
- floodmodeller_api/xml2d.py +688 -684
- floodmodeller_api/xml2d_template.py +37 -37
- floodmodeller_api/zzn.py +387 -365
- {floodmodeller_api-0.4.2.dist-info → floodmodeller_api-0.4.3.dist-info}/LICENSE.txt +13 -13
- {floodmodeller_api-0.4.2.dist-info → floodmodeller_api-0.4.3.dist-info}/METADATA +82 -82
- floodmodeller_api-0.4.3.dist-info/RECORD +179 -0
- {floodmodeller_api-0.4.2.dist-info → floodmodeller_api-0.4.3.dist-info}/WHEEL +1 -1
- floodmodeller_api-0.4.3.dist-info/entry_points.txt +3 -0
- floodmodeller_api/libifcoremd.dll +0 -0
- floodmodeller_api/test/test_data/EX3.bmp +0 -0
- floodmodeller_api/test/test_data/test_output.csv +0 -87
- floodmodeller_api/zzn_read.dll +0 -0
- floodmodeller_api-0.4.2.data/scripts/fmapi-add_siltation.bat +0 -2
- floodmodeller_api-0.4.2.data/scripts/fmapi-add_siltation.py +0 -3
- floodmodeller_api-0.4.2.data/scripts/fmapi-structure_log.bat +0 -2
- floodmodeller_api-0.4.2.data/scripts/fmapi-structure_log.py +0 -3
- floodmodeller_api-0.4.2.data/scripts/fmapi-toolbox.bat +0 -2
- floodmodeller_api-0.4.2.data/scripts/fmapi-toolbox.py +0 -41
- floodmodeller_api-0.4.2.dist-info/RECORD +0 -169
- {floodmodeller_api-0.4.2.dist-info → floodmodeller_api-0.4.3.dist-info}/top_level.txt +0 -0
|
@@ -1,156 +1,151 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
x2d
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
x2d.
|
|
33
|
-
x2d.
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
x2d.
|
|
37
|
-
x2d.
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
x2d.save(
|
|
48
|
-
second_x2d = XML2D(
|
|
49
|
-
assert x2d == second_x2d
|
|
50
|
-
second_output = second_x2d._write()
|
|
51
|
-
assert first_output == second_output
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
assert x2d._write()
|
|
68
|
-
|
|
69
|
-
x2d.
|
|
70
|
-
assert x2d._write()
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
x2d.domains[domain]["roughness"]
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
)
|
|
84
|
-
assert x2d._write()
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
assert x2d._write()
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
domain =
|
|
96
|
-
x2d.domains[domain]["roughness"]
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
)
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
assert x2d._write()
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
"
|
|
122
|
-
"
|
|
123
|
-
"
|
|
124
|
-
"
|
|
125
|
-
"
|
|
126
|
-
"
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
x2d.domains[domain]["computational_area"]["
|
|
130
|
-
x2d.domains[domain]["computational_area"]["
|
|
131
|
-
x2d.domains[domain]["computational_area"]["
|
|
132
|
-
x2d.domains[domain]["computational_area"]["
|
|
133
|
-
x2d.domains[domain]["computational_area"]["
|
|
134
|
-
x2d.domains[domain]["computational_area"]["
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
x2d.domains[domain]["run_data"]["
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
#
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
x2d = XML2D(xml_fp)
|
|
153
|
-
domain = list(x2d.domains)[0]
|
|
154
|
-
x2d.domains[domain]["run_data"]["scheme"] = "TVD"
|
|
155
|
-
|
|
156
|
-
assert x2d._write()
|
|
1
|
+
from pathlib import Path
|
|
2
|
+
|
|
3
|
+
import pytest
|
|
4
|
+
|
|
5
|
+
from floodmodeller_api import XML2D
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
@pytest.fixture
|
|
9
|
+
def xml_fp(test_workspace):
|
|
10
|
+
return Path(test_workspace, "Domain1_Q.xml")
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
@pytest.fixture
|
|
14
|
+
def data_before(xml_fp):
|
|
15
|
+
return XML2D(xml_fp)._write()
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def test_xml2d_str_representation(xml_fp, data_before):
|
|
19
|
+
"""XML2D: Test str representation equal to xml file with no changes"""
|
|
20
|
+
x2d = XML2D(xml_fp)
|
|
21
|
+
assert x2d._write() == data_before
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def test_xml2d_link_dtm_changes(xml_fp, data_before):
|
|
25
|
+
"""XML2D: Test changing and reverting link1d file and dtm makes no changes"""
|
|
26
|
+
x2d = XML2D(xml_fp)
|
|
27
|
+
prev_link = x2d.link1d[0]["link"]
|
|
28
|
+
domain = list(x2d.domains)[0]
|
|
29
|
+
prev_dtm = x2d.domains[domain]["topography"]
|
|
30
|
+
|
|
31
|
+
x2d.link1d[0]["link"] = ["new_link"]
|
|
32
|
+
x2d.domains[domain]["topography"] = ["new_dtm"]
|
|
33
|
+
assert x2d._write() != data_before
|
|
34
|
+
|
|
35
|
+
x2d.link1d[0]["link"] = prev_link
|
|
36
|
+
x2d.domains[domain]["topography"] = prev_dtm
|
|
37
|
+
assert x2d._write() == data_before
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def test_xml2d_all_files(test_workspace, tmpdir):
|
|
41
|
+
"""XML2D: Check all '.xml' files in folder by reading the _write() output into a
|
|
42
|
+
new XML2D instance and checking it stays the same."""
|
|
43
|
+
for xmlfile in Path(test_workspace).glob("*.xml"):
|
|
44
|
+
x2d = XML2D(xmlfile)
|
|
45
|
+
first_output = x2d._write()
|
|
46
|
+
new_path = Path(tmpdir) / "tmp.xml"
|
|
47
|
+
x2d.save(new_path)
|
|
48
|
+
second_x2d = XML2D(new_path)
|
|
49
|
+
assert x2d == second_x2d
|
|
50
|
+
second_output = second_x2d._write()
|
|
51
|
+
assert first_output == second_output
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
# New tests being added for the add/remove functionalility
|
|
55
|
+
def test_xml2d_change_revert_elem_topography():
|
|
56
|
+
"""XML2D: Check that when we change an existing element
|
|
57
|
+
that it is actually adding it and that it is being reverted."""
|
|
58
|
+
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))
|
|
63
|
+
orig_xml = x2d._write()
|
|
64
|
+
x2d.domains[domain]["topography"][0] = "my/new/topography"
|
|
65
|
+
|
|
66
|
+
assert x2d._write() != orig_xml
|
|
67
|
+
assert "my/new/topography" in x2d._write()
|
|
68
|
+
x2d.domains[domain]["topography"] = orig_topography
|
|
69
|
+
assert x2d._write() == orig_xml
|
|
70
|
+
assert "my/new/topography" not in x2d._write()
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def test_xml2d_add_remove_branch_roughness():
|
|
74
|
+
"""XML2D: Check that we can actually add a branch and that
|
|
75
|
+
it is being added and passes validation (i.e write)"""
|
|
76
|
+
x2d = XML2D()
|
|
77
|
+
domain = list(x2d.domains)[0]
|
|
78
|
+
orig_xml = x2d._write()
|
|
79
|
+
x2d.domains[domain]["roughness"] = []
|
|
80
|
+
x2d.domains[domain]["roughness"].append(
|
|
81
|
+
{"type": "file", "law": "manning", "value": "my/roughness/file.shp"},
|
|
82
|
+
)
|
|
83
|
+
assert x2d._write() != orig_xml
|
|
84
|
+
assert "my/roughness/file.shp" in x2d._write()
|
|
85
|
+
del x2d.domains[domain]["roughness"]
|
|
86
|
+
assert x2d._write() == orig_xml
|
|
87
|
+
assert "my/roughness/file.shp" not in x2d._write()
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def test_xml2d_append_remove_branch_roughness():
|
|
91
|
+
"""XML2D: Check that we can append an extra branch to preexisting branch
|
|
92
|
+
so that it passes validation"""
|
|
93
|
+
x2d = XML2D()
|
|
94
|
+
domain = list(x2d.domains)[0]
|
|
95
|
+
x2d.domains[domain]["roughness"] = []
|
|
96
|
+
x2d.domains[domain]["roughness"].append(
|
|
97
|
+
{"type": "file", "law": "manning", "value": "my/roughness/file.shp"},
|
|
98
|
+
)
|
|
99
|
+
xml_1_roughness = x2d._write()
|
|
100
|
+
x2d.domains[domain]["roughness"].append(
|
|
101
|
+
{"type": "file", "law": "manning", "value": "new/roughness/file.shp"},
|
|
102
|
+
)
|
|
103
|
+
|
|
104
|
+
assert x2d._write() != xml_1_roughness
|
|
105
|
+
assert "new/roughness/file.shp" in x2d._write()
|
|
106
|
+
|
|
107
|
+
del x2d.domains[domain]["roughness"][1]
|
|
108
|
+
|
|
109
|
+
assert "new/roughness/file.shp" not in x2d._write()
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
# validation/reordering tests
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
def test_xml2d_reorder_elem_computational_area_wrong_position():
|
|
116
|
+
"""XML2D: Check that if we add ??? in the wrong position does it reorder"""
|
|
117
|
+
x2d = XML2D()
|
|
118
|
+
domain = list(x2d.domains)[0]
|
|
119
|
+
x2d.domains[domain]["computational_area"] = {
|
|
120
|
+
"yll": ...,
|
|
121
|
+
"xll": ...,
|
|
122
|
+
"dx": ...,
|
|
123
|
+
"active_area": ...,
|
|
124
|
+
"ncols": ...,
|
|
125
|
+
"nrows": ...,
|
|
126
|
+
"rotation": ...,
|
|
127
|
+
}
|
|
128
|
+
x2d.domains[domain]["computational_area"]["yll"] = 1.1
|
|
129
|
+
x2d.domains[domain]["computational_area"]["xll"] = 2.6
|
|
130
|
+
x2d.domains[domain]["computational_area"]["dx"] = float(2)
|
|
131
|
+
x2d.domains[domain]["computational_area"]["active_area"] = "path/to/asc/file.asc"
|
|
132
|
+
x2d.domains[domain]["computational_area"]["ncols"] = 12
|
|
133
|
+
x2d.domains[domain]["computational_area"]["nrows"] = 42
|
|
134
|
+
x2d.domains[domain]["computational_area"]["rotation"] = 3.14159
|
|
135
|
+
|
|
136
|
+
x2d.domains[domain]["run_data"]["upwind"] = "upwind value"
|
|
137
|
+
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
|
+
assert x2d._write()
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
def test_xml2d_update_value(xml_fp, data_before):
|
|
146
|
+
"""XML2D: Test changing and reverting link1d file and dtm makes no changes"""
|
|
147
|
+
x2d = XML2D(xml_fp)
|
|
148
|
+
domain = list(x2d.domains)[0]
|
|
149
|
+
x2d.domains[domain]["run_data"]["scheme"] = "TVD"
|
|
150
|
+
|
|
151
|
+
assert x2d._write()
|
|
@@ -1,34 +1,36 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
3
|
-
import pandas as pd
|
|
4
|
-
import pytest
|
|
5
|
-
|
|
6
|
-
from floodmodeller_api import ZZN
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
@pytest.fixture
|
|
10
|
-
def zzn_fp(test_workspace):
|
|
11
|
-
return
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
@pytest.fixture
|
|
15
|
-
def tab_csv_output(test_workspace):
|
|
16
|
-
tab_csv_output = pd.read_csv(
|
|
17
|
-
tab_csv_output["Max State"] = tab_csv_output["Max State"].astype("float64")
|
|
18
|
-
return tab_csv_output
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
def test_load_zzn_using_dll(zzn_fp, tab_csv_output,
|
|
22
|
-
"""ZZN: Check loading zzn okay using dll"""
|
|
23
|
-
zzn = ZZN(zzn_fp)
|
|
24
|
-
zzn.export_to_csv(
|
|
25
|
-
result_type="max",
|
|
26
|
-
save_location=
|
|
27
|
-
)
|
|
28
|
-
output = pd.read_csv(
|
|
29
|
-
output = round(output, 3)
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
1
|
+
from pathlib import Path
|
|
2
|
+
|
|
3
|
+
import pandas as pd
|
|
4
|
+
import pytest
|
|
5
|
+
|
|
6
|
+
from floodmodeller_api import IEF, ZZN
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
@pytest.fixture
|
|
10
|
+
def zzn_fp(test_workspace):
|
|
11
|
+
return Path(test_workspace, "network.zzn")
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
@pytest.fixture
|
|
15
|
+
def tab_csv_output(test_workspace):
|
|
16
|
+
tab_csv_output = pd.read_csv(Path(test_workspace, "network_from_tabularCSV.csv"))
|
|
17
|
+
tab_csv_output["Max State"] = tab_csv_output["Max State"].astype("float64")
|
|
18
|
+
return tab_csv_output
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def test_load_zzn_using_dll(zzn_fp, tab_csv_output, tmpdir):
|
|
22
|
+
"""ZZN: Check loading zzn okay using dll"""
|
|
23
|
+
zzn = ZZN(zzn_fp)
|
|
24
|
+
zzn.export_to_csv(
|
|
25
|
+
result_type="max",
|
|
26
|
+
save_location=Path(tmpdir, "test_output.csv"),
|
|
27
|
+
)
|
|
28
|
+
output = pd.read_csv(Path(tmpdir, "test_output.csv"))
|
|
29
|
+
output = round(output, 3)
|
|
30
|
+
pd.testing.assert_frame_equal(output, tab_csv_output, rtol=0.0001)
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def test_load_zzn_using_ief(zzn_fp):
|
|
34
|
+
zzn = ZZN(zzn_fp).to_dataframe()
|
|
35
|
+
zzn_from_ief = IEF(zzn_fp.with_suffix(".ief")).get_results().to_dataframe()
|
|
36
|
+
pd.testing.assert_frame_equal(zzn, zzn_from_ief)
|
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Flood Modeller Python API
|
|
3
|
+
Copyright (C) 2024 Jacobs U.K. Limited
|
|
4
|
+
|
|
5
|
+
This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License
|
|
6
|
+
as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
|
|
7
|
+
|
|
8
|
+
This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
|
|
9
|
+
of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
|
10
|
+
|
|
11
|
+
You should have received a copy of the GNU General Public License along with this program. If not, see https://www.gnu.org/licenses/.
|
|
12
|
+
|
|
13
|
+
If you have any query about this program or this License, please contact us at support@floodmodeller.com or write to the following
|
|
14
|
+
address: Jacobs UK Limited, Flood Modeller, Cottons Centre, Cottons Lane, London, SE1 2QG, United Kingdom.
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
import json
|
|
18
|
+
from pathlib import Path
|
|
19
|
+
from typing import Any, Union
|
|
20
|
+
|
|
21
|
+
import pandas as pd
|
|
22
|
+
from pandas import Index
|
|
23
|
+
|
|
24
|
+
from .version import __version__
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def to_json(obj: Any) -> str:
|
|
28
|
+
"""
|
|
29
|
+
Function to convert any flood modeller object into a JSON object
|
|
30
|
+
|
|
31
|
+
Args:
|
|
32
|
+
obj (object): Any flood modeller object (dat, ied, ief, cross sections...)
|
|
33
|
+
|
|
34
|
+
Returns:
|
|
35
|
+
A JSON formatted string.
|
|
36
|
+
"""
|
|
37
|
+
return json.dumps(recursive_to_json(obj), indent=2)
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def is_jsonable(obj: Any) -> bool:
|
|
41
|
+
try:
|
|
42
|
+
json.dumps(obj)
|
|
43
|
+
return True
|
|
44
|
+
except Exception:
|
|
45
|
+
return False
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def recursive_to_json(obj: Any, is_top_level: bool = True) -> Any: # noqa: PLR0911
|
|
49
|
+
"""
|
|
50
|
+
Function to undertake a recursion through the different elements of the python object
|
|
51
|
+
|
|
52
|
+
Args:
|
|
53
|
+
Obj (object): Any flood modeller object (dat, ied, ief, cross sections...)
|
|
54
|
+
|
|
55
|
+
Returns:
|
|
56
|
+
if the object is serializable, it creates the object to go to the function to_json and to create the JSON file,
|
|
57
|
+
otherwise, it will move back through this function recursively until the object is finally serializable.
|
|
58
|
+
"""
|
|
59
|
+
from ._base import FMFile
|
|
60
|
+
from .backup import File
|
|
61
|
+
from .units import IIC
|
|
62
|
+
from .units._base import Unit
|
|
63
|
+
from .urban1d._base import UrbanSubsection, UrbanUnit
|
|
64
|
+
|
|
65
|
+
if is_jsonable(obj):
|
|
66
|
+
return obj
|
|
67
|
+
|
|
68
|
+
if isinstance(obj, pd.DataFrame):
|
|
69
|
+
return {"class": "pandas.DataFrame", "object": obj.to_dict()}
|
|
70
|
+
if isinstance(obj, pd.Series):
|
|
71
|
+
return {
|
|
72
|
+
"class": "pandas.Series",
|
|
73
|
+
"variable_name": obj.name,
|
|
74
|
+
"index_name": obj.index.name,
|
|
75
|
+
"object": obj.to_dict(),
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
# To convert WindowsPath, no serializable, objects to string, serializable.
|
|
79
|
+
if isinstance(obj, Path):
|
|
80
|
+
return str(obj)
|
|
81
|
+
|
|
82
|
+
if isinstance(obj, set):
|
|
83
|
+
# create list and append
|
|
84
|
+
return {"python_set": [recursive_to_json(item, is_top_level=False) for item in sorted(obj)]}
|
|
85
|
+
|
|
86
|
+
# Case list or dict of non-jsonable stuff
|
|
87
|
+
if isinstance(obj, list):
|
|
88
|
+
# create list and append
|
|
89
|
+
return [recursive_to_json(item, is_top_level=False) for item in obj]
|
|
90
|
+
|
|
91
|
+
# Dictionary to the all the serializable objects
|
|
92
|
+
if isinstance(obj, dict):
|
|
93
|
+
return {key: recursive_to_json(value, is_top_level=False) for key, value in obj.items()}
|
|
94
|
+
|
|
95
|
+
# Either a type of FM API Class
|
|
96
|
+
if isinstance(obj, (FMFile, Unit, IIC, File, UrbanSubsection, UrbanUnit)): # noqa: RET503
|
|
97
|
+
# Information from the flood modeller object will be included in the JSON output
|
|
98
|
+
# slicing undertaken to remove quotation marks
|
|
99
|
+
return_dict: dict[str, Any] = {"API Class": str(obj.__class__)[8:-2]}
|
|
100
|
+
if is_top_level:
|
|
101
|
+
return_dict["API Version"] = __version__
|
|
102
|
+
|
|
103
|
+
return_dict["Object Attributes"] = {
|
|
104
|
+
key: recursive_to_json(value, is_top_level=False) for key, value in obj.__dict__.items()
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
return return_dict
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
def from_json(obj: Union[str, dict]) -> dict:
|
|
111
|
+
"""
|
|
112
|
+
Function to convert a JSON string back into Python objects
|
|
113
|
+
|
|
114
|
+
Args:
|
|
115
|
+
json_str (str): A JSON string
|
|
116
|
+
|
|
117
|
+
Returns:
|
|
118
|
+
A FMP object
|
|
119
|
+
"""
|
|
120
|
+
# To convert a JSON string into a python dictionary
|
|
121
|
+
if isinstance(obj, str):
|
|
122
|
+
obj_dict = json.loads(obj)["Object Attributes"]
|
|
123
|
+
else:
|
|
124
|
+
obj_dict = obj["Object Attributes"]
|
|
125
|
+
|
|
126
|
+
return recursive_from_json(obj_dict)
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
def recursive_from_json(obj: Union[dict, Any]) -> Any:
|
|
130
|
+
"""
|
|
131
|
+
Function to undertake a recursion through the different elements of the JSON object
|
|
132
|
+
|
|
133
|
+
Args:
|
|
134
|
+
obj (dict): A JSON dict. IT was converted from str to dict in from_json
|
|
135
|
+
|
|
136
|
+
Returns:
|
|
137
|
+
A FMP object
|
|
138
|
+
"""
|
|
139
|
+
|
|
140
|
+
if "API Class" in obj:
|
|
141
|
+
class_type = obj["API Class"]
|
|
142
|
+
from .mapping import api_class_mapping
|
|
143
|
+
|
|
144
|
+
return api_class_mapping[class_type].from_json(obj)
|
|
145
|
+
|
|
146
|
+
if "class" in obj and obj["class"] == "pandas.DataFrame":
|
|
147
|
+
df = pd.DataFrame.from_dict(obj["object"])
|
|
148
|
+
df.index = convert_dataframe_index(df.index)
|
|
149
|
+
return df
|
|
150
|
+
if "class" in obj and obj["class"] == "pandas.Series":
|
|
151
|
+
sr = pd.Series(obj["object"])
|
|
152
|
+
sr.index = convert_dataframe_index(sr.index)
|
|
153
|
+
sr.index.name = obj["index_name"]
|
|
154
|
+
sr.name = obj["variable_name"]
|
|
155
|
+
return sr
|
|
156
|
+
|
|
157
|
+
if "python_set" in obj:
|
|
158
|
+
return set(obj["python_set"])
|
|
159
|
+
|
|
160
|
+
for key, value in obj.items():
|
|
161
|
+
if isinstance(value, dict):
|
|
162
|
+
obj[key] = recursive_from_json(value)
|
|
163
|
+
elif isinstance(value, list):
|
|
164
|
+
new_list = []
|
|
165
|
+
for item in value:
|
|
166
|
+
if isinstance(item, dict):
|
|
167
|
+
new_list.append(recursive_from_json(item))
|
|
168
|
+
else:
|
|
169
|
+
new_list.append(item)
|
|
170
|
+
|
|
171
|
+
obj[key] = new_list
|
|
172
|
+
|
|
173
|
+
return obj
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
def convert_dataframe_index(index: Index) -> Index:
|
|
177
|
+
try:
|
|
178
|
+
return index.astype("int")
|
|
179
|
+
except ValueError:
|
|
180
|
+
pass
|
|
181
|
+
try:
|
|
182
|
+
return index.astype("float")
|
|
183
|
+
except ValueError:
|
|
184
|
+
return index
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
class Jsonable:
|
|
188
|
+
"""Base class used to provide underlying to_json and from_json methods"""
|
|
189
|
+
|
|
190
|
+
def __init__(self, **kwargs):
|
|
191
|
+
pass
|
|
192
|
+
|
|
193
|
+
def to_json(self) -> str:
|
|
194
|
+
"""Converts the object instance into a JSON string representation.
|
|
195
|
+
|
|
196
|
+
Returns:
|
|
197
|
+
str: A JSON string representing the object instance.
|
|
198
|
+
"""
|
|
199
|
+
return to_json(self)
|
|
200
|
+
|
|
201
|
+
@classmethod
|
|
202
|
+
def from_json(cls, json_string: str):
|
|
203
|
+
"""Creates an object instance from a JSON string.
|
|
204
|
+
|
|
205
|
+
Args:
|
|
206
|
+
json_string (str): A JSON string representation of the object.
|
|
207
|
+
|
|
208
|
+
Returns:
|
|
209
|
+
An object instance of the class.
|
|
210
|
+
"""
|
|
211
|
+
object_dict = from_json(json_string)
|
|
212
|
+
api_object = cls(from_json=True)
|
|
213
|
+
|
|
214
|
+
# Loop through the dictionary and update the object
|
|
215
|
+
for key, value in object_dict.items():
|
|
216
|
+
setattr(api_object, key, value)
|
|
217
|
+
|
|
218
|
+
return api_object
|