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
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
from typing import Any
|
|
2
|
+
|
|
3
|
+
from . import DAT, IED, IEF, INP, LF1, LF2, XML2D, ZZN
|
|
4
|
+
from .backup import File
|
|
5
|
+
from .units import (
|
|
6
|
+
BLOCKAGE,
|
|
7
|
+
BRIDGE,
|
|
8
|
+
COMMENT,
|
|
9
|
+
CONDUIT,
|
|
10
|
+
CRUMP,
|
|
11
|
+
CULVERT,
|
|
12
|
+
FLAT_V_WEIR,
|
|
13
|
+
HTBDY,
|
|
14
|
+
IIC,
|
|
15
|
+
INTERPOLATE,
|
|
16
|
+
ORIFICE,
|
|
17
|
+
OUTFALL,
|
|
18
|
+
QHBDY,
|
|
19
|
+
QTBDY,
|
|
20
|
+
REFHBDY,
|
|
21
|
+
REPLICATE,
|
|
22
|
+
RIVER,
|
|
23
|
+
RNWEIR,
|
|
24
|
+
SLUICE,
|
|
25
|
+
SPILL,
|
|
26
|
+
UNSUPPORTED,
|
|
27
|
+
WEIR,
|
|
28
|
+
Variables,
|
|
29
|
+
)
|
|
30
|
+
from .urban1d.conduits import CONDUIT as CONDUIT_URBAN
|
|
31
|
+
from .urban1d.conduits import CONDUITS as CONDUITS_URBAN
|
|
32
|
+
from .urban1d.junctions import JUNCTION, JUNCTIONS
|
|
33
|
+
from .urban1d.losses import LOSS, LOSSES
|
|
34
|
+
from .urban1d.outfalls import OUTFALL as OUTFALL_URBAN
|
|
35
|
+
from .urban1d.outfalls import OUTFALLS as OUTFALLS_URBAN
|
|
36
|
+
from .urban1d.raingauges import RAINGAUGE, RAINGAUGES
|
|
37
|
+
from .urban1d.xsections import XSECTION, XSECTIONS
|
|
38
|
+
|
|
39
|
+
api_class_mapping: dict[str, Any] = {
|
|
40
|
+
"floodmodeller_api.dat.DAT": DAT,
|
|
41
|
+
"floodmodeller_api.ied.IED": IED,
|
|
42
|
+
"floodmodeller_api.ief.IEF": IEF,
|
|
43
|
+
"floodmodeller_api.inp.INP": INP,
|
|
44
|
+
"floodmodeller_api.lf.LF1": LF1,
|
|
45
|
+
"floodmodeller_api.lf.LF2": LF2,
|
|
46
|
+
"floodmodeller_api.xml2d.XML2D": XML2D,
|
|
47
|
+
"floodmodeller_api.zzn.ZZN": ZZN,
|
|
48
|
+
"floodmodeller_api.backup.File": File,
|
|
49
|
+
"floodmodeller_api.urban1d.junctions.JUNCTIONS": JUNCTIONS,
|
|
50
|
+
"floodmodeller_api.urban1d.junctions.JUNCTION": JUNCTION,
|
|
51
|
+
"floodmodeller_api.urban1d.outfalls.OUTFALLS": OUTFALLS_URBAN,
|
|
52
|
+
"floodmodeller_api.urban1d.outfalls.OUTFALL": OUTFALL_URBAN,
|
|
53
|
+
"floodmodeller_api.urban1d.raingauges.RAINGAUGES": RAINGAUGES,
|
|
54
|
+
"floodmodeller_api.urban1d.raingauges.RAINGAUGE": RAINGAUGE,
|
|
55
|
+
"floodmodeller_api.urban1d.conduits.CONDUITS": CONDUITS_URBAN,
|
|
56
|
+
"floodmodeller_api.urban1d.conduits.CONDUIT": CONDUIT_URBAN,
|
|
57
|
+
"floodmodeller_api.urban1d.losses.LOSSES": LOSSES,
|
|
58
|
+
"floodmodeller_api.urban1d.losses.LOSS": LOSS,
|
|
59
|
+
"floodmodeller_api.urban1d.xsections.XSECTIONS": XSECTIONS,
|
|
60
|
+
"floodmodeller_api.urban1d.xsections.XSECTION": XSECTION,
|
|
61
|
+
"floodmodeller_api.units.boundaries.HTBDY": HTBDY,
|
|
62
|
+
"floodmodeller_api.units.boundaries.QHBDY": QHBDY,
|
|
63
|
+
"floodmodeller_api.units.boundaries.QTBDY": QTBDY,
|
|
64
|
+
"floodmodeller_api.units.boundaries.REFHBDY": REFHBDY,
|
|
65
|
+
"floodmodeller_api.units.comment.COMMENT": COMMENT,
|
|
66
|
+
"floodmodeller_api.units.conduits.CONDUIT": CONDUIT,
|
|
67
|
+
"floodmodeller_api.units.iic.IIC": IIC,
|
|
68
|
+
"floodmodeller_api.units.losses.BLOCKAGE": BLOCKAGE,
|
|
69
|
+
"floodmodeller_api.units.losses.CULVERT": CULVERT,
|
|
70
|
+
"floodmodeller_api.units.sections.INTERPOLATE": INTERPOLATE,
|
|
71
|
+
"floodmodeller_api.units.sections.REPLICATE": REPLICATE,
|
|
72
|
+
"floodmodeller_api.units.sections.RIVER": RIVER,
|
|
73
|
+
"floodmodeller_api.units.structures.BRIDGE": BRIDGE,
|
|
74
|
+
"floodmodeller_api.units.structures.CRUMP": CRUMP,
|
|
75
|
+
"floodmodeller_api.units.structures.FLAT_V_WEIR": FLAT_V_WEIR,
|
|
76
|
+
"floodmodeller_api.units.structures.ORIFICE": ORIFICE,
|
|
77
|
+
"floodmodeller_api.units.structures.OUTFALL": OUTFALL,
|
|
78
|
+
"floodmodeller_api.units.structures.RNWEIR": RNWEIR,
|
|
79
|
+
"floodmodeller_api.units.structures.SLUICE": SLUICE,
|
|
80
|
+
"floodmodeller_api.units.structures.SPILL": SPILL,
|
|
81
|
+
"floodmodeller_api.units.structures.WEIR": WEIR,
|
|
82
|
+
"floodmodeller_api.units.unsupported.UNSUPPORTED": UNSUPPORTED,
|
|
83
|
+
"floodmodeller_api.units.variables.Variables": Variables,
|
|
84
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import os
|
|
2
|
-
import sys
|
|
3
|
-
|
|
4
|
-
sys.path.insert(0, os.path.dirname(os.path.dirname(__file__)))
|
|
1
|
+
import os
|
|
2
|
+
import sys
|
|
3
|
+
|
|
4
|
+
sys.path.insert(0, os.path.dirname(os.path.dirname(__file__)))
|
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import os
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
1
|
+
import os
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
|
|
4
|
+
import pytest
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
@pytest.fixture(scope="session")
|
|
8
|
+
def test_workspace():
|
|
9
|
+
return Path(os.path.dirname(__file__), "test_data")
|
|
@@ -1,117 +1,117 @@
|
|
|
1
|
-
from pathlib import Path
|
|
2
|
-
|
|
3
|
-
import pandas as pd
|
|
4
|
-
import pytest
|
|
5
|
-
|
|
6
|
-
from floodmodeller_api.backup import BackupControl, File
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
@pytest.fixture
|
|
10
|
-
def backup_control():
|
|
11
|
-
# Use a different directory for testing
|
|
12
|
-
return BackupControl()
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
@pytest.fixture
|
|
16
|
-
def file(test_workspace):
|
|
17
|
-
test_file = Path(test_workspace, "EX1.DAT")
|
|
18
|
-
file = File(test_file)
|
|
19
|
-
# Make a backup to clear in test
|
|
20
|
-
file.backup()
|
|
21
|
-
return file
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
def test_init_backup(backup_control):
|
|
25
|
-
"""Has the backup been initialised correctly?"""
|
|
26
|
-
assert backup_control.backup_dir.exists()
|
|
27
|
-
assert backup_control.backup_csv_path.exists()
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
def test_generate_file_id(file, test_workspace):
|
|
31
|
-
"""Does this generate a consistent file ID for the same file on disk?"""
|
|
32
|
-
# Test that the file ID is the same for the same path input
|
|
33
|
-
file1 = File(Path(test_workspace, "EX1.DAT"))
|
|
34
|
-
file2 = File(Path(test_workspace, "EX1.DAT"))
|
|
35
|
-
assert file1.file_id == file2.file_id
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
def test_clear_backup(file, test_workspace):
|
|
39
|
-
"""
|
|
40
|
-
Does the the clear_backup method work correctly
|
|
41
|
-
"""
|
|
42
|
-
# Clearing backup -------------------
|
|
43
|
-
# Load a different file to check it isn't affected by the
|
|
44
|
-
other_file = File(Path(test_workspace, "EX3.DAT"))
|
|
45
|
-
# Assert there is a backup for the other file
|
|
46
|
-
other_file.backup()
|
|
47
|
-
# Clear the backups for the file to test backup functionality
|
|
48
|
-
file.clear_backup()
|
|
49
|
-
# Assert that clearing the backup has worked - there aren't any backups for the file
|
|
50
|
-
assert len(file.list_backups()) == 0
|
|
51
|
-
# And that clearing it hasn't affected backups for the other file
|
|
52
|
-
assert len(other_file.list_backups()) > 0
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
def test_backup_locations(file):
|
|
56
|
-
"""
|
|
57
|
-
Does it make a backup in the right place?
|
|
58
|
-
"""
|
|
59
|
-
# Making a backup --------------------
|
|
60
|
-
file.clear_backup()
|
|
61
|
-
# make a backup and check if file exists
|
|
62
|
-
file.backup()
|
|
63
|
-
backup_file_path = Path(file.backup_dir, file.backup_filename)
|
|
64
|
-
assert backup_file_path.exists()
|
|
65
|
-
# check if contents of backup file match the original file
|
|
66
|
-
with open(backup_file_path
|
|
67
|
-
assert f1.read() == f2.read()
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
def test_no_duplicate_backup(file, test_workspace):
|
|
71
|
-
"""The backup method should only backup if the file has changed"""
|
|
72
|
-
# Don't Make Duplicate -------------------
|
|
73
|
-
# Check that the file isn't backed up again if it hasn't changed
|
|
74
|
-
the_same_file = File(Path(test_workspace, "EX1.DAT"))
|
|
75
|
-
# Append something to the dttm string to ensure the filename is different to the previous backup
|
|
76
|
-
# If the two File objects are created in the same second then then will have identical file names
|
|
77
|
-
# The function should check for equivalence between file contents.
|
|
78
|
-
the_same_file.dttm_str = the_same_file.dttm_str + "_1"
|
|
79
|
-
# Generate a new file name
|
|
80
|
-
the_same_file._generate_file_name()
|
|
81
|
-
# Attempt a backup
|
|
82
|
-
the_same_file.backup()
|
|
83
|
-
# Check that the file hasn't been created
|
|
84
|
-
duplicate_backup_path = Path(the_same_file.backup_dir, the_same_file.backup_filename)
|
|
85
|
-
assert not duplicate_backup_path.exists()
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
def test_backup_logs(file):
|
|
89
|
-
"""Are backups being logged in the CSV?"""
|
|
90
|
-
# Clear the backup
|
|
91
|
-
file.clear_backup()
|
|
92
|
-
# There shouldn't be any edits in the csv
|
|
93
|
-
backup_logs = pd.read_csv(file.backup_csv_path)
|
|
94
|
-
backup_count = backup_logs[
|
|
95
|
-
(backup_logs.file_id == file.file_id) & (backup_logs.dttm == file.dttm_str)
|
|
96
|
-
].shape[0]
|
|
97
|
-
assert backup_count == 0
|
|
98
|
-
# Make a backup and assert it is in the CSV
|
|
99
|
-
file.backup()
|
|
100
|
-
# Check edits to the backup CSV
|
|
101
|
-
# Check a row has been added to the csv for the file & version
|
|
102
|
-
backup_logs = pd.read_csv(file.backup_csv_path)
|
|
103
|
-
backup_count = backup_logs[
|
|
104
|
-
(backup_logs.file_id == file.file_id) & (backup_logs.dttm == file.dttm_str)
|
|
105
|
-
].shape[0]
|
|
106
|
-
assert backup_count == 1
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
def test_list_backups(file):
|
|
110
|
-
"""Does the list backups method work correctly?"""
|
|
111
|
-
# First clear any backups that exist
|
|
112
|
-
file.clear_backup()
|
|
113
|
-
# make a backup and check if it appears in the backup list
|
|
114
|
-
file.backup()
|
|
115
|
-
backups = file.list_backups()
|
|
116
|
-
expected_backup = Path(file.backup_dir, file.backup_filename)
|
|
117
|
-
assert expected_backup in [backup.path for backup in backups]
|
|
1
|
+
from pathlib import Path
|
|
2
|
+
|
|
3
|
+
import pandas as pd
|
|
4
|
+
import pytest
|
|
5
|
+
|
|
6
|
+
from floodmodeller_api.backup import BackupControl, File
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
@pytest.fixture
|
|
10
|
+
def backup_control():
|
|
11
|
+
# Use a different directory for testing
|
|
12
|
+
return BackupControl()
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
@pytest.fixture
|
|
16
|
+
def file(test_workspace):
|
|
17
|
+
test_file = Path(test_workspace, "EX1.DAT")
|
|
18
|
+
file = File(test_file)
|
|
19
|
+
# Make a backup to clear in test
|
|
20
|
+
file.backup()
|
|
21
|
+
return file
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def test_init_backup(backup_control):
|
|
25
|
+
"""Has the backup been initialised correctly?"""
|
|
26
|
+
assert backup_control.backup_dir.exists()
|
|
27
|
+
assert backup_control.backup_csv_path.exists()
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def test_generate_file_id(file, test_workspace):
|
|
31
|
+
"""Does this generate a consistent file ID for the same file on disk?"""
|
|
32
|
+
# Test that the file ID is the same for the same path input
|
|
33
|
+
file1 = File(Path(test_workspace, "EX1.DAT"))
|
|
34
|
+
file2 = File(Path(test_workspace, "EX1.DAT"))
|
|
35
|
+
assert file1.file_id == file2.file_id
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def test_clear_backup(file, test_workspace):
|
|
39
|
+
"""
|
|
40
|
+
Does the the clear_backup method work correctly
|
|
41
|
+
"""
|
|
42
|
+
# Clearing backup -------------------
|
|
43
|
+
# Load a different file to check it isn't affected by the
|
|
44
|
+
other_file = File(Path(test_workspace, "EX3.DAT"))
|
|
45
|
+
# Assert there is a backup for the other file
|
|
46
|
+
other_file.backup()
|
|
47
|
+
# Clear the backups for the file to test backup functionality
|
|
48
|
+
file.clear_backup()
|
|
49
|
+
# Assert that clearing the backup has worked - there aren't any backups for the file
|
|
50
|
+
assert len(file.list_backups()) == 0
|
|
51
|
+
# And that clearing it hasn't affected backups for the other file
|
|
52
|
+
assert len(other_file.list_backups()) > 0
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def test_backup_locations(file):
|
|
56
|
+
"""
|
|
57
|
+
Does it make a backup in the right place?
|
|
58
|
+
"""
|
|
59
|
+
# Making a backup --------------------
|
|
60
|
+
file.clear_backup()
|
|
61
|
+
# make a backup and check if file exists
|
|
62
|
+
file.backup()
|
|
63
|
+
backup_file_path = Path(file.backup_dir, file.backup_filename)
|
|
64
|
+
assert backup_file_path.exists()
|
|
65
|
+
# check if contents of backup file match the original file
|
|
66
|
+
with open(backup_file_path) as f1, open(file.path) as f2:
|
|
67
|
+
assert f1.read() == f2.read()
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def test_no_duplicate_backup(file, test_workspace):
|
|
71
|
+
"""The backup method should only backup if the file has changed"""
|
|
72
|
+
# Don't Make Duplicate -------------------
|
|
73
|
+
# Check that the file isn't backed up again if it hasn't changed
|
|
74
|
+
the_same_file = File(Path(test_workspace, "EX1.DAT"))
|
|
75
|
+
# Append something to the dttm string to ensure the filename is different to the previous backup
|
|
76
|
+
# If the two File objects are created in the same second then then will have identical file names
|
|
77
|
+
# The function should check for equivalence between file contents.
|
|
78
|
+
the_same_file.dttm_str = the_same_file.dttm_str + "_1"
|
|
79
|
+
# Generate a new file name
|
|
80
|
+
the_same_file._generate_file_name()
|
|
81
|
+
# Attempt a backup
|
|
82
|
+
the_same_file.backup()
|
|
83
|
+
# Check that the file hasn't been created
|
|
84
|
+
duplicate_backup_path = Path(the_same_file.backup_dir, the_same_file.backup_filename)
|
|
85
|
+
assert not duplicate_backup_path.exists()
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
def test_backup_logs(file):
|
|
89
|
+
"""Are backups being logged in the CSV?"""
|
|
90
|
+
# Clear the backup
|
|
91
|
+
file.clear_backup()
|
|
92
|
+
# There shouldn't be any edits in the csv
|
|
93
|
+
backup_logs = pd.read_csv(file.backup_csv_path)
|
|
94
|
+
backup_count = backup_logs[
|
|
95
|
+
(backup_logs.file_id == file.file_id) & (backup_logs.dttm == file.dttm_str)
|
|
96
|
+
].shape[0]
|
|
97
|
+
assert backup_count == 0
|
|
98
|
+
# Make a backup and assert it is in the CSV
|
|
99
|
+
file.backup()
|
|
100
|
+
# Check edits to the backup CSV
|
|
101
|
+
# Check a row has been added to the csv for the file & version
|
|
102
|
+
backup_logs = pd.read_csv(file.backup_csv_path)
|
|
103
|
+
backup_count = backup_logs[
|
|
104
|
+
(backup_logs.file_id == file.file_id) & (backup_logs.dttm == file.dttm_str)
|
|
105
|
+
].shape[0]
|
|
106
|
+
assert backup_count == 1
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
def test_list_backups(file):
|
|
110
|
+
"""Does the list backups method work correctly?"""
|
|
111
|
+
# First clear any backups that exist
|
|
112
|
+
file.clear_backup()
|
|
113
|
+
# make a backup and check if it appears in the backup list
|
|
114
|
+
file.backup()
|
|
115
|
+
backups = file.list_backups()
|
|
116
|
+
expected_backup = Path(file.backup_dir, file.backup_filename)
|
|
117
|
+
assert expected_backup in [backup.path for backup in backups]
|
|
@@ -1,92 +1,221 @@
|
|
|
1
|
-
import
|
|
2
|
-
from
|
|
3
|
-
|
|
4
|
-
import pytest
|
|
5
|
-
|
|
6
|
-
from floodmodeller_api import DAT
|
|
7
|
-
from floodmodeller_api.units import QTBDY
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
@pytest.fixture
|
|
11
|
-
def dat_fp(test_workspace):
|
|
12
|
-
return
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
@pytest.fixture
|
|
16
|
-
def data_before(dat_fp):
|
|
17
|
-
return DAT(dat_fp)._write()
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
1
|
+
from pathlib import Path
|
|
2
|
+
from unittest.mock import patch
|
|
3
|
+
|
|
4
|
+
import pytest
|
|
5
|
+
|
|
6
|
+
from floodmodeller_api import DAT
|
|
7
|
+
from floodmodeller_api.units import QTBDY
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
@pytest.fixture
|
|
11
|
+
def dat_fp(test_workspace):
|
|
12
|
+
return Path(test_workspace, "network.dat")
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
@pytest.fixture
|
|
16
|
+
def data_before(dat_fp):
|
|
17
|
+
return DAT(dat_fp)._write()
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
@pytest.fixture()
|
|
21
|
+
def dat_ex3(test_workspace):
|
|
22
|
+
return DAT(Path(test_workspace, "EX3.DAT"))
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
@pytest.fixture()
|
|
26
|
+
def units(dat_ex3):
|
|
27
|
+
unit_1 = dat_ex3.sections["20"]
|
|
28
|
+
unit_2 = dat_ex3.sections["40"]
|
|
29
|
+
unit_3 = dat_ex3.sections["60"]
|
|
30
|
+
return [unit_1, unit_2, unit_3]
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
@pytest.fixture()
|
|
34
|
+
def dat_ex6(test_workspace):
|
|
35
|
+
dat = DAT(Path(test_workspace, "EX6.DAT"))
|
|
36
|
+
with (
|
|
37
|
+
patch.object(dat, "_update_raw_data", wraps=dat._update_raw_data),
|
|
38
|
+
patch.object(dat, "_update_dat_struct", wraps=dat._update_dat_struct),
|
|
39
|
+
):
|
|
40
|
+
yield dat
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def test_dat_str_not_changed_by_write(dat_fp, data_before):
|
|
44
|
+
# TODO: Update this test - it isn't really testing anything since the behaviour of the fixture is exactly the same
|
|
45
|
+
"""DAT: Test str representation equal to dat file with no changes"""
|
|
46
|
+
dat = DAT(dat_fp)
|
|
47
|
+
assert dat._write() == data_before
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def test_changing_section_and_dist_works(dat_fp, data_before):
|
|
51
|
+
"""DAT: Test changing and reverting section name and dist to next makes no changes"""
|
|
52
|
+
dat = DAT(dat_fp)
|
|
53
|
+
prev_name = dat.sections["CSRD10"].name
|
|
54
|
+
prev_dist = dat.sections["CSRD10"].dist_to_next
|
|
55
|
+
dat.sections["CSRD10"].name = "check"
|
|
56
|
+
dat.sections["CSRD10"].dist_to_next = 0.0
|
|
57
|
+
assert dat._write() != data_before
|
|
58
|
+
|
|
59
|
+
dat.sections["check"].name = prev_name
|
|
60
|
+
dat.sections["check"].dist_to_next = prev_dist
|
|
61
|
+
assert dat._write() == data_before
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def test_changing_and_reverting_qtbdy_hydrograph_works(dat_fp, data_before):
|
|
65
|
+
"""DAT: Test changing and reverting QTBDY hydrograph makes no changes"""
|
|
66
|
+
dat = DAT(dat_fp)
|
|
67
|
+
prev_qt = {}
|
|
68
|
+
for name, unit in dat.boundaries.items():
|
|
69
|
+
if isinstance(unit, QTBDY):
|
|
70
|
+
prev_qt[name] = unit.data.copy()
|
|
71
|
+
unit.data *= 2 # Multiply QT flow data by 2
|
|
72
|
+
assert dat._write() != data_before
|
|
73
|
+
|
|
74
|
+
for name, qt in prev_qt.items():
|
|
75
|
+
dat.boundaries[name].data = qt # replace QT flow data with original
|
|
76
|
+
assert dat._write() == data_before
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def test_dat_read_doesnt_change_data(test_workspace, tmpdir):
|
|
80
|
+
"""DAT: Check all '.dat' files in folder by reading the _write() output into a new DAT instance and checking it stays the same."""
|
|
81
|
+
for datfile in Path(test_workspace).glob("*.dat"):
|
|
82
|
+
dat = DAT(datfile)
|
|
83
|
+
first_output = dat._write()
|
|
84
|
+
new_path = Path(tmpdir) / "tmp.dat"
|
|
85
|
+
dat.save(new_path)
|
|
86
|
+
second_dat = DAT(new_path)
|
|
87
|
+
assert dat == second_dat # Checks equivalence on the class itself
|
|
88
|
+
second_output = second_dat._write()
|
|
89
|
+
assert first_output == second_output
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def test_insert_unit_before(units, dat_ex6):
|
|
93
|
+
dat_ex6.insert_unit(units[0], add_before=dat_ex6.sections["P4000"])
|
|
94
|
+
assert "20" in dat_ex6.sections
|
|
95
|
+
assert dat_ex6._all_units[8:10] == [units[0], dat_ex6.sections["P4000"]]
|
|
96
|
+
dat_ex6._update_raw_data.assert_called_once()
|
|
97
|
+
dat_ex6._update_dat_struct.assert_called_once()
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
def test_insert_unit_after(units, dat_ex6):
|
|
101
|
+
dat_ex6.insert_unit(units[0], add_after=dat_ex6.sections["P4000"])
|
|
102
|
+
assert "20" in dat_ex6.sections
|
|
103
|
+
assert dat_ex6._all_units[8:10] == [dat_ex6.sections["P4000"], units[0]]
|
|
104
|
+
dat_ex6._update_raw_data.assert_called_once()
|
|
105
|
+
dat_ex6._update_dat_struct.assert_called_once()
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
def test_insert_unit_at(units, dat_ex6):
|
|
109
|
+
dat_ex6.insert_unit(units[0], add_at=2)
|
|
110
|
+
assert "20" in dat_ex6.sections
|
|
111
|
+
assert dat_ex6._all_units[2] == units[0]
|
|
112
|
+
dat_ex6._update_raw_data.assert_called_once()
|
|
113
|
+
dat_ex6._update_dat_struct.assert_called_once()
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
@pytest.mark.parametrize(
|
|
117
|
+
"kwargs",
|
|
118
|
+
[
|
|
119
|
+
{"add_at": 1, "add_before": 2},
|
|
120
|
+
{"add_at": 1, "add_after": 3},
|
|
121
|
+
{"add_before": 2, "add_after": 3},
|
|
122
|
+
{"add_at": 1, "add_before": 2, "add_after": 3},
|
|
123
|
+
],
|
|
124
|
+
)
|
|
125
|
+
def test_insert_unit_too_many_arguments_error(dat_ex6, units, kwargs):
|
|
126
|
+
msg = (
|
|
127
|
+
r"\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"
|
|
128
|
+
r"\nAPI Error: Problem encountered when trying to insert unit DAT file .*\."
|
|
129
|
+
r"\n"
|
|
130
|
+
r"\nDetails: .*-floodmodeller_api/dat\.py-\d+"
|
|
131
|
+
r"\nMsg: Only one of add_at, add_before, or add_after required"
|
|
132
|
+
r"\n"
|
|
133
|
+
r"\nFor additional support, go to: https://github\.com/People-Places-Solutions/floodmodeller-api"
|
|
134
|
+
)
|
|
135
|
+
with pytest.raises(Exception, match=msg):
|
|
136
|
+
dat_ex6.insert_unit(units[0], **kwargs)
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
def test_insert_unit_no_arguments_error(dat_ex6, units):
|
|
140
|
+
msg = (
|
|
141
|
+
r"\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"
|
|
142
|
+
r"\nAPI Error: Problem encountered when trying to insert unit DAT file .*\."
|
|
143
|
+
r"\n"
|
|
144
|
+
r"\nDetails: .*-floodmodeller_api/dat\.py-\d+"
|
|
145
|
+
r"\nMsg: No positional argument given\. Please provide either add_before, add_at or add_after"
|
|
146
|
+
r"\n"
|
|
147
|
+
r"\nFor additional support, go to: https://github\.com/People-Places-Solutions/floodmodeller-api"
|
|
148
|
+
)
|
|
149
|
+
with pytest.raises(Exception, match=msg):
|
|
150
|
+
dat_ex6.insert_unit(units[0])
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
def test_insert_units_before(units, dat_ex6):
|
|
154
|
+
dat_ex6.insert_units(units, add_before=dat_ex6.sections["P4000"])
|
|
155
|
+
assert "20" in dat_ex6.sections
|
|
156
|
+
assert "40" in dat_ex6.sections
|
|
157
|
+
assert "60" in dat_ex6.sections
|
|
158
|
+
assert dat_ex6._all_units[8:12] == [*units, dat_ex6.sections["P4000"]]
|
|
159
|
+
dat_ex6._update_raw_data.assert_called_once()
|
|
160
|
+
dat_ex6._update_dat_struct.assert_called_once()
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
def test_insert_units_after(units, dat_ex6):
|
|
164
|
+
dat_ex6.insert_units(units, add_after=dat_ex6.sections["P4000"])
|
|
165
|
+
assert "20" in dat_ex6.sections
|
|
166
|
+
assert "40" in dat_ex6.sections
|
|
167
|
+
assert "60" in dat_ex6.sections
|
|
168
|
+
assert dat_ex6._all_units[8:12] == [dat_ex6.sections["P4000"], *units]
|
|
169
|
+
dat_ex6._update_raw_data.assert_called_once()
|
|
170
|
+
dat_ex6._update_dat_struct.assert_called_once()
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
def test_insert_units_at(units, dat_ex6):
|
|
174
|
+
dat_ex6.insert_units(units, add_at=2)
|
|
175
|
+
assert "20" in dat_ex6.sections
|
|
176
|
+
assert "40" in dat_ex6.sections
|
|
177
|
+
assert "60" in dat_ex6.sections
|
|
178
|
+
assert dat_ex6._all_units[2:5] == units
|
|
179
|
+
dat_ex6._update_raw_data.assert_called_once()
|
|
180
|
+
dat_ex6._update_dat_struct.assert_called_once()
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
def test_insert_units_at_end(units, dat_ex6):
|
|
184
|
+
dat_ex6.insert_units(units, add_at=-1)
|
|
185
|
+
assert "20" in dat_ex6.sections
|
|
186
|
+
assert "40" in dat_ex6.sections
|
|
187
|
+
assert "60" in dat_ex6.sections
|
|
188
|
+
assert dat_ex6._all_units[-3:] == units
|
|
189
|
+
dat_ex6._update_raw_data.assert_called_once()
|
|
190
|
+
dat_ex6._update_dat_struct.assert_called_once()
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
def test_remove_unit(dat_ex3):
|
|
194
|
+
unit = dat_ex3.sections["20"]
|
|
195
|
+
prev_dat_struct_len = len(dat_ex3._dat_struct)
|
|
196
|
+
dat_ex3.remove_unit(unit)
|
|
197
|
+
assert "20" not in dat_ex3.sections
|
|
198
|
+
assert unit not in dat_ex3._all_units
|
|
199
|
+
assert dat_ex3._dat_struct
|
|
200
|
+
assert (prev_dat_struct_len - len(dat_ex3._dat_struct)) == 1
|
|
201
|
+
|
|
202
|
+
|
|
203
|
+
def test_diff(test_workspace, capsys):
|
|
204
|
+
dat_ex4 = DAT(Path(test_workspace, "ex4.DAT"))
|
|
205
|
+
dat_ex4_changed = DAT(Path(test_workspace, "ex4_changed.DAT"))
|
|
206
|
+
dat_ex4.diff(dat_ex4_changed)
|
|
207
|
+
assert capsys.readouterr().out == (
|
|
208
|
+
"Files not equivalent, 12 difference(s) found:\n"
|
|
209
|
+
" DAT->structures->MILLAu->RNWEIR..MILLAu->upstream_crest_height: 1.07 != 1.37\n"
|
|
210
|
+
" DAT->structures->MILLBu->RNWEIR..MILLBu->upstream_crest_height: 0.43 != 0.73\n"
|
|
211
|
+
" DAT->structures->ROAD1->RNWEIR..ROAD1->upstream_crest_height: 2.02 != 2.32\n"
|
|
212
|
+
" DAT->structures->RAILRDu->RNWEIR..RAILRDu->upstream_crest_height: 1.75 != 2.05\n"
|
|
213
|
+
" DAT->structures->CSRD01u->RNWEIR..CSRD01u->upstream_crest_height: 0.81 != 1.11\n"
|
|
214
|
+
" DAT->structures->FOOTa->RNWEIR..FOOTa->upstream_crest_height: 2.47 != 2.77\n"
|
|
215
|
+
" DAT->_all_units->itm[28]->RNWEIR..MILLAu->upstream_crest_height: 1.07 != 1.37\n"
|
|
216
|
+
" DAT->_all_units->itm[29]->RNWEIR..MILLBu->upstream_crest_height: 0.43 != 0.73\n"
|
|
217
|
+
" DAT->_all_units->itm[42]->RNWEIR..ROAD1->upstream_crest_height: 2.02 != 2.32\n"
|
|
218
|
+
" DAT->_all_units->itm[57]->RNWEIR..RAILRDu->upstream_crest_height: 1.75 != 2.05\n"
|
|
219
|
+
" DAT->_all_units->itm[61]->RNWEIR..CSRD01u->upstream_crest_height: 0.81 != 1.11\n"
|
|
220
|
+
" DAT->_all_units->itm[73]->RNWEIR..FOOTa->upstream_crest_height: 2.47 != 2.77\n"
|
|
221
|
+
)
|