diff --git a/python/ngen_conf/src/ngen/config/_version.py b/python/ngen_conf/src/ngen/config/_version.py index 13a85f77..44b18069 100644 --- a/python/ngen_conf/src/ngen/config/_version.py +++ b/python/ngen_conf/src/ngen/config/_version.py @@ -1 +1 @@ -__version__ = '0.2.5' +__version__ = '0.2.6' diff --git a/python/ngen_conf/src/ngen/config/all_formulations.py b/python/ngen_conf/src/ngen/config/all_formulations.py index c815de81..b26b9e46 100644 --- a/python/ngen_conf/src/ngen/config/all_formulations.py +++ b/python/ngen_conf/src/ngen/config/all_formulations.py @@ -1,16 +1,19 @@ from typing import Union from ngen.config.cfe import CFE -from ngen.config.pet import PET +from ngen.config.lgar import LGAR from ngen.config.lstm import LSTM -from ngen.config.noahowp import NoahOWP from ngen.config.multi import MultiBMI -from ngen.config.topmod import Topmod +from ngen.config.noahowp import NoahOWP +from ngen.config.pet import PET from ngen.config.sloth import SLOTH +from ngen.config.soil_freeze_thaw import SoilFreezeThaw +from ngen.config.soil_moisture_profile import SoilMoistureProfile +from ngen.config.topmod import Topmod #NOTE the order of this union is important for validation #unless the model class is using smart_union! -KnownFormulations = Union[Topmod, CFE, PET, NoahOWP, LSTM, SLOTH, MultiBMI] +KnownFormulations = Union[Topmod, CFE, PET, NoahOWP, LSTM, SLOTH, LGAR, SoilFreezeThaw, SoilMoistureProfile, MultiBMI] #See notes in multi.py and formulation.py about the recursive #type of MultiBMI modules and how the forward_refs are handled. diff --git a/python/ngen_conf/src/ngen/config/lgar.py b/python/ngen_conf/src/ngen/config/lgar.py new file mode 100644 index 00000000..8dbb125e --- /dev/null +++ b/python/ngen_conf/src/ngen/config/lgar.py @@ -0,0 +1,18 @@ +from pydantic import Field + +from .bmi_formulation import BMICxx + + +class LGAR(BMICxx): + """A BMIC++ implementation for LGAR module""" + + registration_function: str = "none" + main_output_variable: str = "precipitation_rate" + model_name: str = Field("LGAR", const=True, alias="model_type_name") + + _variable_names_map = { + # QINSUR from noah owp modular + "precipitation_rate": "QINSUR", + # EVAPOTRANS from noah owp modular + "potential_evapotranspiration_rate": "EVAPOTRANS", + } diff --git a/python/ngen_conf/src/ngen/config/soil_freeze_thaw.py b/python/ngen_conf/src/ngen/config/soil_freeze_thaw.py new file mode 100644 index 00000000..3a51f459 --- /dev/null +++ b/python/ngen_conf/src/ngen/config/soil_freeze_thaw.py @@ -0,0 +1,27 @@ +from typing import Optional + +from pydantic import BaseModel, Field + +from .bmi_formulation import BMICxx + + +class SoilFreezeThawParams(BaseModel): + """Class for validating Soil Freeze Thaw Parameters""" + + smcmax: Optional[float] + b: Optional[float] + satpsi: Optional[float] + + +class SoilFreezeThaw(BMICxx): + """A BMIC++ implementation for Soil Freeze Thaw module""" + + model_params: Optional[SoilFreezeThawParams] + registration_function: str = "none" + main_output_variable: str = "num_cells" + model_name: str = Field("SoilFreezeThaw", const=True, alias="model_type_name") + + _variable_names_map = { + # TG from noah owp modular + "ground_temperature": "TG" + } diff --git a/python/ngen_conf/src/ngen/config/soil_moisture_profile.py b/python/ngen_conf/src/ngen/config/soil_moisture_profile.py new file mode 100644 index 00000000..c4ddf726 --- /dev/null +++ b/python/ngen_conf/src/ngen/config/soil_moisture_profile.py @@ -0,0 +1,29 @@ +from typing import Optional + +from pydantic import BaseModel, Field + +from .bmi_formulation import BMICxx + + +class SoilMoistureProfileParams(BaseModel): + """Class for validating Soil Moisture Profile Parameters""" + + smcmax: Optional[float] + b: Optional[float] + satpsi: Optional[float] + + +class SoilMoistureProfile(BMICxx): + """A BMIC++ implementation for Soil Moisture Profile module""" + + model_params: Optional[SoilMoistureProfileParams] + registration_function: str = "none" + main_output_variable: str = "soil_water_table" + model_name: str = Field("SoilMoistureProfile", const=True, alias="model_type_name") + + _variable_names_map = { + # SOIL_STORAGE from cfe + "soil_storage": "SOIL_STORAGE", + # SOIL_STORAGE_CHANGE from cfe + "soil_storage_change": "SOIL_STORAGE_CHANGE", + } diff --git a/python/ngen_conf/tests/conftest.py b/python/ngen_conf/tests/conftest.py index bb9f1599..cce565f8 100644 --- a/python/ngen_conf/tests/conftest.py +++ b/python/ngen_conf/tests/conftest.py @@ -5,12 +5,15 @@ from ngen.config.cfe import CFE from ngen.config.sloth import SLOTH from ngen.config.noahowp import NoahOWP +from ngen.config.lgar import LGAR +from ngen.config.soil_freeze_thaw import SoilFreezeThaw +from ngen.config.soil_moisture_profile import SoilMoistureProfile from ngen.config.topmod import Topmod from ngen.config.lstm import LSTM from ngen.config.multi import MultiBMI -#set the workdir relative to this test config -#and use that to look for test data +# set the workdir relative to this test config +# and use that to look for test data _workdir=Path(__file__).parent _datadir = _workdir / "data" _cfe_config_data_path = _datadir / "init_config_data" / "cat_87_bmi_config_cfe.ini" @@ -107,6 +110,45 @@ def lstm_params(): 'config': "{{id}}_config.txt"} return data +@pytest.fixture +def lgar_params(): + path = _workdir.joinpath("data/dne/") + data = { + 'model_type_name': 'LGAR', + 'name': 'bmi_c++', + 'registration_function': 'none', + 'library': 'libfakecfe.so', + 'config_prefix': path, + 'config': "{{id}}_config.txt", + } + return data + +@pytest.fixture +def soil_freeze_thaw_params(): + path = _workdir.joinpath("data/dne/") + data = { + 'model_type_name': 'SoilFreezeThaw', + 'name': 'bmi_c++', + 'registration_function': 'none', + 'library': 'libfakecfe.so', + 'config_prefix': path, + 'config': "{{id}}_config.txt", + } + return data + +@pytest.fixture +def soil_moisture_profile_params(): + path = _workdir.joinpath("data/dne/") + data = { + 'model_type_name': 'SoilMoistureProfile', + 'name': 'bmi_c++', + 'registration_function': 'none', + 'library': 'libfakecfe.so', + 'config_prefix': path, + 'config': "{{id}}_config.txt", + } + return data + @pytest.fixture def cfe(cfe_params): return CFE(**cfe_params) @@ -127,6 +169,18 @@ def noahowp(noahowp_params): def lstm(lstm_params): return LSTM(**lstm_params) +@pytest.fixture +def lgar(lgar_params): + return LGAR(**lgar_params) + +@pytest.fixture +def soil_freeze_thaw(soil_freeze_thaw_params): + return SoilFreezeThaw(**soil_freeze_thaw_params) + +@pytest.fixture +def soil_moisture_profile(soil_moisture_profile_params): + return SoilMoistureProfile(**soil_moisture_profile_params) + @pytest.fixture def multi(cfe, noahowp): cfe.allow_exceed_end_time=True diff --git a/python/ngen_conf/tests/test_lgar.py b/python/ngen_conf/tests/test_lgar.py new file mode 100644 index 00000000..ecf77ca0 --- /dev/null +++ b/python/ngen_conf/tests/test_lgar.py @@ -0,0 +1,23 @@ +from ngen.config.formulation import Formulation +from ngen.config.lgar import LGAR + + +def test_init(lgar_params): + LGAR(**lgar_params) + + +def test_name_map(lgar_params): + lgar = LGAR(**lgar_params) + assert lgar.name_map["precipitation_rate"] == "QINSUR" + assert lgar.name_map["potential_evapotranspiration_rate"] == "EVAPOTRANS" + + +def test_lgar_formulation(lgar_params): + lgar = LGAR(**lgar_params) + f = {"params": lgar, "name": "bmi_c++"} + lgar_formulation = Formulation(**f) + _lgar = lgar_formulation.params + assert _lgar.name == "bmi_c++" + assert _lgar.model_name == "LGAR" + serialized = _lgar.dict(by_alias=True) + assert serialized["model_type_name"] == "LGAR" diff --git a/python/ngen_conf/tests/test_soil_freeze_thaw.py b/python/ngen_conf/tests/test_soil_freeze_thaw.py new file mode 100644 index 00000000..95b53f8b --- /dev/null +++ b/python/ngen_conf/tests/test_soil_freeze_thaw.py @@ -0,0 +1,22 @@ +from ngen.config.formulation import Formulation +from ngen.config.soil_freeze_thaw import SoilFreezeThaw + + +def test_init(soil_freeze_thaw_params): + SoilFreezeThaw(**soil_freeze_thaw_params) + + +def test_name_map(soil_freeze_thaw_params): + soil_freeze_thaw = SoilFreezeThaw(**soil_freeze_thaw_params) + assert soil_freeze_thaw.name_map["ground_temperature"] == "TG" + + +def test_soil_freeze_thaw_formulation(soil_freeze_thaw_params): + soil_freeze_thaw = SoilFreezeThaw(**soil_freeze_thaw_params) + f = {"params": soil_freeze_thaw, "name": "bmi_c++"} + soil_freeze_thaw_formulation = Formulation(**f) + _soil_freeze_thaw = soil_freeze_thaw_formulation.params + assert _soil_freeze_thaw.name == "bmi_c++" + assert _soil_freeze_thaw.model_name == "SoilFreezeThaw" + serialized = _soil_freeze_thaw.dict(by_alias=True) + assert serialized["model_type_name"] == "SoilFreezeThaw" diff --git a/python/ngen_conf/tests/test_soil_moisture_profile.py b/python/ngen_conf/tests/test_soil_moisture_profile.py new file mode 100644 index 00000000..df3a83c8 --- /dev/null +++ b/python/ngen_conf/tests/test_soil_moisture_profile.py @@ -0,0 +1,23 @@ +from ngen.config.formulation import Formulation +from ngen.config.soil_moisture_profile import SoilMoistureProfile + + +def test_init(soil_moisture_profile_params): + SoilMoistureProfile(**soil_moisture_profile_params) + + +def test_name_map(soil_moisture_profile_params): + soil_moisture_profile = SoilMoistureProfile(**soil_moisture_profile_params) + assert soil_moisture_profile.name_map["soil_storage"] == "SOIL_STORAGE" + assert soil_moisture_profile.name_map["soil_storage_change"] == "SOIL_STORAGE_CHANGE" + + +def test_soil_moisture_profile_formulation(soil_moisture_profile_params): + soil_moisture_profile = SoilMoistureProfile(**soil_moisture_profile_params) + f = {"params": soil_moisture_profile, "name": "bmi_c++"} + soil_moisture_profile_formulation = Formulation(**f) + _soil_moisture_profile = soil_moisture_profile_formulation.params + assert _soil_moisture_profile.name == "bmi_c++" + assert _soil_moisture_profile.model_name == "SoilMoistureProfile" + serialized = _soil_moisture_profile.dict(by_alias=True) + assert serialized["model_type_name"] == "SoilMoistureProfile"