Skip to content

Commit 26be22e

Browse files
Merge branch 'main' into python-313
2 parents 8bb8627 + 93573a3 commit 26be22e

File tree

5 files changed

+56
-16
lines changed

5 files changed

+56
-16
lines changed

hydrolib/core/dflowfm/ini/models.py

Lines changed: 2 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
from abc import ABC
33
from enum import Enum
44
from math import isnan
5-
from re import compile
65
from typing import (
76
Any,
87
Callable,
@@ -29,6 +28,7 @@
2928
ParsableFileModel,
3029
)
3130

31+
from ...utils import FortranUtils
3232
from ..ini.io_models import CommentBlock, Document, Property, Section
3333
from .parser import Parser
3434
from .serializer import (
@@ -58,9 +58,6 @@ class INIBasedModel(BaseModel, ABC):
5858

5959
_header: str = ""
6060
_file_path_style_converter = FilePathStyleConverter()
61-
_scientific_notation_regex = compile(
62-
r"([\d.]+)([dD])([+-]?\d{1,3})"
63-
) # matches a float: 1d9, 1D-3, 1.D+4, etc.
6461

6562
class Config:
6663
extra = Extra.ignore
@@ -159,18 +156,7 @@ def replace_fortran_scientific_notation_for_floats(cls, value, field):
159156
if field.type_ != float:
160157
return value
161158

162-
return cls._replace_fortran_scientific_notation(value)
163-
164-
@classmethod
165-
def _replace_fortran_scientific_notation(cls, value):
166-
if isinstance(value, str):
167-
return cls._scientific_notation_regex.sub(r"\1e\3", value)
168-
if isinstance(value, list):
169-
for i, v in enumerate(value):
170-
if isinstance(v, str):
171-
value[i] = cls._scientific_notation_regex.sub(r"\1e\3", v)
172-
173-
return value
159+
return FortranUtils.replace_fortran_scientific_notation(value)
174160

175161
@classmethod
176162
def validate(cls: Type["INIBasedModel"], value: Any) -> "INIBasedModel":

hydrolib/core/dflowfm/tim/models.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
from pydantic.v1.class_validators import validator
55

66
from hydrolib.core.basemodel import BaseModel, ModelSaveSettings, ParsableFileModel
7+
from hydrolib.core.utils import FortranUtils
78

89
from .parser import TimParser
910
from .serializer import TimSerializer, TimSerializerConfig
@@ -49,6 +50,26 @@ def _get_serializer(
4950
def _get_parser(cls) -> Callable[[Path], Dict]:
5051
return TimParser.parse
5152

53+
@validator("timeseries", pre=True, check_fields=True, allow_reuse=True)
54+
def replace_fortran_scientific_notation_for_floats(cls, value, field):
55+
for record in value:
56+
if isinstance(record, dict):
57+
record["time"] = FortranUtils.replace_fortran_scientific_notation(
58+
record["time"]
59+
)
60+
record["data"] = FortranUtils.replace_fortran_scientific_notation(
61+
record["data"]
62+
)
63+
elif isinstance(record, TimRecord):
64+
record.time = FortranUtils.replace_fortran_scientific_notation(
65+
record.time
66+
)
67+
record.data = FortranUtils.replace_fortran_scientific_notation(
68+
record.data
69+
)
70+
71+
return value
72+
5273
@validator("timeseries")
5374
@classmethod
5475
def _validate_timeseries_values(cls, v: List[TimRecord]) -> List[TimRecord]:

hydrolib/core/utils.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66
from pathlib import Path
77
from typing import Any, Callable, List, Optional
88

9+
from pydantic import validator
10+
from pydantic.v1.fields import ModelField
911
from strenum import StrEnum
1012

1113

@@ -332,3 +334,23 @@ def _calculate_md5_checksum(filepath: Path) -> str:
332334
for chunk in iter(lambda: file.read(4096), b""):
333335
md5_hash.update(chunk)
334336
return md5_hash.hexdigest()
337+
338+
339+
class FortranUtils:
340+
"""Utility class for Fortran specific conventions."""
341+
342+
_scientific_exp_d_notation_regex = re.compile(
343+
r"([\d.]+)([dD])([+-]?\d{1,3})"
344+
) # matches a float: 1d9, 1D-3, 1.D+4, etc.
345+
346+
@staticmethod
347+
def replace_fortran_scientific_notation(value):
348+
"""Replace Fortran scientific notation ("D" in exponent) with standard
349+
scientific notation ("e" in exponent).
350+
"""
351+
if isinstance(value, str):
352+
return FortranUtils._scientific_exp_d_notation_regex.sub(r"\1e\3", value)
353+
elif isinstance(value, list):
354+
return list(map(FortranUtils.replace_fortran_scientific_notation, value))
355+
356+
return value

tests/data/input/tim/unimagdir.wnd

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
0.0 1.00 270.00
2+
9d9 1.00 270.00

tests/dflowfm/test_tim.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import math
12
from pathlib import Path
23

34
import pytest
@@ -223,6 +224,14 @@ def test_validate_data_for_timeseries_throws_exception_for_incorrect_data(
223224

224225
assert expected_error_msg in str(error.value)
225226

227+
def test_test_fortran_d_exponent_supported(self):
228+
input_path = Path(test_input_dir / "tim" / "unimagdir.wnd")
229+
tim_model = TimModel(input_path)
230+
assert tim_model.timeseries[0].time == 0
231+
assert tim_model.timeseries[0].data == [1.0, 270.0]
232+
assert math.isclose(tim_model.timeseries[1].time, 9e9)
233+
assert tim_model.timeseries[1].data == [1.0, 270.0]
234+
226235

227236
class TestTimRecord:
228237
def test_initialization(self):

0 commit comments

Comments
 (0)