Skip to content

Commit

Permalink
add quantityunitpair to the Forcingmodel and fix transposed datablock
Browse files Browse the repository at this point in the history
  • Loading branch information
MAfarrag committed Jan 24, 2025
1 parent 4aa6d69 commit 14b3a5b
Show file tree
Hide file tree
Showing 6 changed files with 118 additions and 87 deletions.
103 changes: 95 additions & 8 deletions hydrolib/tools/ext_old_to_new/converters.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
from typing import Any, Dict, List, Union

from hydrolib.core.basemodel import DiskOnlyFileModel
from hydrolib.core.dflowfm.bc.models import ForcingModel, TimeSeries
from hydrolib.core.dflowfm.bc.models import ForcingModel, QuantityUnitPair, TimeSeries
from hydrolib.core.dflowfm.ext.models import (
SOURCE_SINKS_QUANTITIES_VALID_PREFIXES,
Boundary,
Expand Down Expand Up @@ -129,6 +129,51 @@ class BoundaryConditionConverter(BaseConverter):
def __init__(self):
super().__init__()

@staticmethod
def parse_tim_model(tim_files: List[TimModel], forcing: ExtOldForcing) -> TimModel:
"""Parse the boundary condition related time series from the tim files.
Args:
tim_files (List[TimModel]): List of TIM models.
forcing (ExtOldForcing):
Returns:
TimModel: A TimModel object containing the time series data from the TIM files.
"""
time_files_exist = all([tim_file.exists() for tim_file in tim_files])
if not time_files_exist:
raise ValueError(
f"TIM files '{tim_files}' not found for QUANTITY={forcing.quantity}"
)

tim_models = [
TimModel(file, quantities_names=[file.stem]) for file in tim_files
]
# merge all the tim files into one tim model
for tim_model in tim_models[1:]:
data = tim_model.as_dict()
if len(data.keys()) != 1:
raise ValueError(
f"Number of columns in the TIM file '{tim_model.filepath}' should be 1 column."
)
tim_models[0].add_column(
list(data.values())[0], column_name=list(data.keys())[0]
)
return tim_models[0]

@staticmethod
def convert_tim_to_bc(
tim_model: TimModel,
start_time: str,
time_interpolation: str = "linear",
units: List[str] = None,
user_defined_names: List[str] = None,
) -> ForcingModel:
forcing_model = TimToForcingConverter.convert(
tim_model, start_time, time_interpolation, units, user_defined_names
)
return forcing_model

def convert(self, forcing: ExtOldForcing) -> Boundary:
"""Convert an old external forcing block to a boundary forcing block
suitable for inclusion in a new external forcings file.
Expand Down Expand Up @@ -156,11 +201,51 @@ def convert(self, forcing: ExtOldForcing) -> Boundary:
supported by the converter, a ValueError is raised. This ensures
that only compatible forcing blocks are processed, maintaining
data integrity and preventing errors in the conversion process.
Notes:
- The `root_dir` property must be set before calling this method.
"""
from hydrolib.core.dflowfm.polyfile.models import PolyFile

location_file = forcing.filename.filepath
poly_line = forcing.filename
if not isinstance(poly_line, PolyFile):
# path = self.root_dir / location_file
# path.exists()
poly_line = PolyFile(location_file)

num_files = poly_line.number_of_points
if self.root_dir is None:
raise ValueError(
"The 'root_dir' property must be set before calling this method."
)

tim_files = [
self.root_dir
/ poly_line.filepath.with_name(
f"{poly_line.filepath.stem}_000{i + 1}.tim"
).name
for i in range(num_files)
]

tim_model = self.parse_tim_model(tim_files, forcing)
# switch the quantity names from the Tim model (loction names) to quantity names.
user_defined_names = tim_model.quantities_names
tim_model.quantities_names = [forcing.quantity] * len(tim_model.get_units())

# TODO: check the units of the initialtracers
units = tim_model.get_units()

# TODO: get the start name from the mdu file
start_time = "minutes since 2015-01-01 00:00:00"
forcing_model = self.convert_tim_to_bc(
tim_model, start_time, units=units, user_defined_names=user_defined_names
)

data = {
"quantity": forcing.quantity,
"locationfile": forcing.filename.filepath,
"forcingfile": ForcingModel(),
"locationfile": location_file,
"forcingfile": forcing_model,
}

new_block = Boundary(**data)
Expand Down Expand Up @@ -590,12 +675,12 @@ def convert(
{'discharge': [0.0, 0.01, 0.0, -0.01, 0.0, 0.01, 0.0, -0.01, 0.0, 0.01, 0.0, -0.01, 0.0]}
>>> converter = TimToForcingConverter()
>>> forcing_model = converter.convert(
... tim_model, "minutes since 2015-01-01 00:00:00", "linear", ["/s"], ["discharge"]
... tim_model, "minutes since 2015-01-01 00:00:00", "linear", ["m3/s"], ["discharge"]
... )
>>> print(forcing_model.forcing[0].name)
discharge
>>> print(forcing_model.forcing[0].datablock)
[[0.0, 10.0, 20.0, 30.0, 40.0, 50.0, 60.0, 70.0, 80.0, 90.0, 100.0, 110.0, 120.0], [0.0, 0.01, 0.0, -0.01, 0.0, 0.01, 0.0, -0.01, 0.0, 0.01, 0.0, -0.01, 0.0]]
[[0.0, 0.0], [10.0, 0.01], [20.0, 0.0], [30.0, -0.01], [40.0, 0.0], [50.0, 0.01], [60.0, 0.0], [70.0, -0.01], [80.0, 0.0], [90.0, 0.01], [100.0, 0.0], [110.0, -0.01], [120.0, 0.0]]
```
"""
Expand All @@ -618,9 +703,11 @@ def convert(
name=user_defined_names[i],
function="timeseries",
timeinterpolation=time_interpolation,
quantity=["time", column],
unit=[start_time, unit],
datablock=[time_data, vals.values.tolist()],
quantityunitpair=[
QuantityUnitPair(quantity="time", unit=start_time),
QuantityUnitPair(quantity=column, unit=unit),
],
datablock=[[i, j] for i, j in zip(time_data, vals.values.tolist())],
)
forcings_list.append(forcing)

Expand Down
5 changes: 3 additions & 2 deletions hydrolib/tools/ext_old_to_new/main_converter.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,8 +57,8 @@ def __init__(
FileNotFoundError: If the old external forcing file does not exist.
Examples:
>>> converter = ExternalForcingConverter("old-external-forcing.ext")
>>> converter.update()
>>> converter = ExternalForcingConverter("old-external-forcing.ext") #doctest: +SKIP
>>> converter.update() #doctest: +SKIP
"""
if isinstance(extold_model, Path) or isinstance(extold_model, str):
extold_model = self._read_old_file(extold_model)
Expand Down Expand Up @@ -251,6 +251,7 @@ def _convert_forcing(self, forcing) -> Union[Boundary, Lateral, Meteo, SourceSin
forcing, quantities, **temp_salinity_mdu
)
else:
converter_class.root_dir = self.root_dir
new_quantity_block = converter_class.convert(forcing)
except ValueError:
# While this tool is in progress, accept that we do not convert all quantities yet.
Expand Down
16 changes: 0 additions & 16 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -165,25 +165,9 @@ def old_forcing_file_boundary() -> Dict[str, Path]:
),
"quantities": [
"waterlevelbnd",
"dischargebnd",
"velocitybnd",
"tangentialvelocitybnd",
"neumannbnd",
"riemannbnd",
"outflowbnd",
"qhbnd",
"salinitybnd",
],
"locationfile": [
"tfl_01.pli",
"left01.pli",
"vel01.pli",
"tanvelN.pli",
"right01.pli",
"left01.pli",
"right01.pli",
"right01.pli",
"tfl_02_sal.pli",
],
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,53 +66,3 @@ FILENAME =tfl_01.pli
FILETYPE =9
METHOD =3
OPERAND =O


QUANTITY =dischargebnd
FILENAME =left01.pli
FILETYPE =9
METHOD =3
OPERAND =O

QUANTITY =velocitybnd
FILENAME =vel01.pli
FILETYPE =9
METHOD =3
OPERAND =O

QUANTITY =tangentialvelocitybnd
FILENAME =tanvelN.pli
FILETYPE =9
METHOD =3
OPERAND =O

QUANTITY =neumannbnd
FILENAME =right01.pli
FILETYPE =9
METHOD =3
OPERAND =O

QUANTITY =riemannbnd
FILENAME =left01.pli
FILETYPE =9
METHOD =3
OPERAND =O


QUANTITY =outflowbnd
FILENAME =right01.pli
FILETYPE =9
METHOD =3
OPERAND =O

QUANTITY =qhbnd
FILENAME =right01.pli
FILETYPE =9
METHOD =3
OPERAND =O

QUANTITY =salinitybnd
FILENAME =tfl_02_sal.pli
FILETYPE =9
METHOD =3
OPERAND =O
17 changes: 13 additions & 4 deletions tests/tools/test_converters.py
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ def test_default(self):

class TestBoundaryConverter:

def test_default(self):
def test_with_pli(self, input_files_dir):
"""
Old quantity block:
Expand All @@ -101,7 +101,7 @@ def test_default(self):
OPERAND =O
```
"""
file_name = "tests/data/input/boundary-conditions/tfl_01.pli"
file_name = input_files_dir / "boundary-conditions/tfl_01.pli"
forcing = ExtOldForcing(
quantity=ExtOldQuantity.WaterLevelBnd,
filename=file_name,
Expand All @@ -110,11 +110,20 @@ def test_default(self):
operand="O",
)

new_quantity_block = BoundaryConditionConverter().convert(forcing)
converter = BoundaryConditionConverter()
converter.root_dir = input_files_dir / "boundary-conditions"
new_quantity_block = converter.convert(forcing)
assert isinstance(new_quantity_block, Boundary)
assert new_quantity_block.quantity == "waterlevelbnd"
assert new_quantity_block.forcingfile == ForcingModel()
forcing_model = new_quantity_block.forcingfile
assert new_quantity_block.locationfile == DiskOnlyFileModel(file_name)
assert new_quantity_block.nodeid is None
assert new_quantity_block.bndwidth1d is None
assert new_quantity_block.bndbldepth is None
forcings = forcing_model.forcing
assert len(forcing_model.forcing) == 2
names = ["tfl_01_0001", "tfl_01_0002"]
assert all(forcing_model.forcing[i].name == names[i] for i in range(len(names)))
assert all(forcings[i].quantityunitpair[0].quantity == "time" for i in range(2))
assert all(forcings[i].quantityunitpair[1].quantity == "waterlevelbnd" for i in range(2))
assert forcings[0].datablock == [[0, 0.01], [120, 0.01]]
14 changes: 7 additions & 7 deletions tests/tools/test_converters_source_sink.py
Original file line number Diff line number Diff line change
Expand Up @@ -175,13 +175,13 @@ def compare_data(new_quantity_block: SourceSink):
]
# check the values of the data block
# initialtracer_anyname
assert forcing_bases[3].datablock[1] == [4.0, 4.0, 4.0, 4.0, 4.0]
assert forcing_bases[3].as_dataframe().loc[:, 0].to_list() == [4.0, 4.0, 4.0, 4.0, 4.0]
# temperature
assert forcing_bases[2].datablock[1] == [3.0, 3.0, 3.0, 3.0, 3.0]
assert forcing_bases[2].as_dataframe().loc[:, 0].to_list() == [3.0, 3.0, 3.0, 3.0, 3.0]
# salinity
assert forcing_bases[1].datablock[1] == [2.0, 2.0, 2.0, 2.0, 2.0]
assert forcing_bases[1].as_dataframe().loc[:, 0].to_list() == [2.0, 2.0, 2.0, 2.0, 2.0]
# discharge
assert forcing_bases[0].datablock[1] == [1.0, 1.0, 1.0, 1.0, 1.0]
assert forcing_bases[0].as_dataframe().loc[:, 0].to_list() == [1.0, 1.0, 1.0, 1.0, 1.0]


class TestSourceSinkConverter:
Expand Down Expand Up @@ -335,7 +335,7 @@ def test_no_temperature_no_salinity(self):
```
- The tim file file has the following structure:
- The tim file has the following structure:
```
0.0 1.0 4.0
100 1.0 4.0
Expand Down Expand Up @@ -380,6 +380,6 @@ def test_no_temperature_no_salinity(self):
]
# check the values of the data block
# initialtracer_anyname
assert forcing_bases[1].datablock[1] == [4.0, 4.0, 4.0, 4.0, 4.0]
assert forcing_bases[1].as_dataframe().loc[:, 0].to_list() == [4.0, 4.0, 4.0, 4.0, 4.0]
# discharge
assert forcing_bases[0].datablock[1] == [1.0, 1.0, 1.0, 1.0, 1.0]
assert forcing_bases[0].as_dataframe().loc[:, 0].to_list() == [1.0, 1.0, 1.0, 1.0, 1.0]

0 comments on commit 14b3a5b

Please sign in to comment.