Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: warn if soil or veg type is water but not both #118

Merged
merged 4 commits into from
Mar 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion python/ngen_conf/src/ngen/config/_version.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
__version__ = '0.2.5'
__version__ = '0.2.6'
57 changes: 41 additions & 16 deletions python/ngen_conf/src/ngen/config/init_config/noahowp.py
Original file line number Diff line number Diff line change
@@ -1,34 +1,34 @@
from enum import Enum
import warnings
from datetime import datetime
from enum import Enum
from pathlib import Path, PosixPath, WindowsPath
from pydantic import BaseModel, validator, root_validator
from typing import ClassVar, Dict, List, Literal, Union

from ngen.init_config import core
from ngen.init_config import serializer_deserializer as serde
from pydantic import BaseModel, root_validator, validator

from .noahowp_options import (
PrecipPhaseOption,
SnowAlbedoOption,
DynamicVegOption,
RunoffOption,
CanopyStomResistOption,
CropModelOption,
DrainageOption,
FrozenSoilOption,
DynamicVegOption,
DynamicVicOption,
EvapSrfcResistanceOption,
FrozenSoilOption,
PrecipPhaseOption,
RadiativeTransferOption,
RunoffOption,
SfcDragCoeffOption,
CanopyStomResistOption,
CropModelOption,
SnowAlbedoOption,
SnowsoilTempTimeOption,
SoilTempBoundaryOption,
SupercooledWaterOption,
StomatalResistanceOption,
EvapSrfcResistanceOption,
SubsurfaceOption,
SupercooledWaterOption,
)
from .validators import validate_str_len_lt
from .utils import serialize_enum_value

from typing import Dict, ClassVar, List, Literal, Union
from .validators import validate_str_len_lt

MODIFIED_IGBP_MODIS_NOAH_NVEG = 20
USGS_NVEG = 27
Expand Down Expand Up @@ -69,9 +69,10 @@ class Config(serde.NamelistSerializerDeserializer.Config):

@root_validator
def _validate(cls, values: Dict[str, BaseModel]) -> Dict[str, BaseModel]:
parameters: Parameters = values["parameters"] # type: ignore
structure: Structure = values["structure"] # type: ignore
parameters: Parameters = values["parameters"] # type: ignore
structure: Structure = values["structure"] # type: ignore
_set_nveg_based_on_veg_class_name(parameters, structure)
_warn_if_soil_or_veg_type_is_water_but_not_both(parameters, structure)
return values


Expand Down Expand Up @@ -270,4 +271,28 @@ class Config(core.Base.Config):
}


def _warn_if_soil_or_veg_type_is_water_but_not_both(
parameters: "Parameters", structure: "Structure"
):
if parameters.soil_class_name not in ("STAS", "STAS-RUC"):
return
SOIL_TYPE_WATER = 14
soil_type_is_water = structure.isltyp == SOIL_TYPE_WATER

if parameters.veg_class_name == "USGS":
VEG_USGS_WATER = 16
veg_type_is_water = structure.vegtyp == VEG_USGS_WATER
elif parameters.veg_class_name == "MODIFIED_IGBP_MODIS_NOAH":
VEG_MODIS_WATER = 17
veg_type_is_water = structure.vegtyp == VEG_MODIS_WATER
else:
return

# NOTE: ensure arguments to XOR (^) are bools
if (veg_type_is_water) ^ (soil_type_is_water):
warnings.warn(
aaraney marked this conversation as resolved.
Show resolved Hide resolved
f"'isltyp' is {'' if soil_type_is_water else 'not'} water but 'vegtyp' is {'' if veg_type_is_water else 'not'} water"
)


NoahOWP.update_forward_refs()
80 changes: 79 additions & 1 deletion python/ngen_conf/tests/test_init_config_models.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
import warnings

import pytest
from ngen.init_config import utils

from ngen.config.init_config.cfe import CFE
from ngen.config.init_config.pet import PET
from ngen.config.init_config.noahowp import NoahOWP
from ngen.config.init_config.pet import PET


def test_cfe(cfe_init_config: str):
Expand All @@ -21,3 +25,77 @@ def test_pet(pet_init_config: str):
def test_noah_owp(noah_owp_init_config: str):
o = NoahOWP.from_namelist_str(noah_owp_init_config)
assert o.to_namelist_str() == noah_owp_init_config


SOIL_TYPE_WATER = 14
VEG_USGS_WATER = 16
VEG_MODIS_WATER = 17

does_warn_cases = (
("USGS", VEG_USGS_WATER, SOIL_TYPE_WATER + 1),
("USGS", VEG_USGS_WATER + 1, SOIL_TYPE_WATER),
("MODIFIED_IGBP_MODIS_NOAH", VEG_MODIS_WATER, SOIL_TYPE_WATER + 1),
("MODIFIED_IGBP_MODIS_NOAH", VEG_MODIS_WATER + 1, SOIL_TYPE_WATER),
)


@pytest.mark.parametrize("veg_class,veg_type,soil_type", does_warn_cases)
def test_noah_owp_does_warns_if_soil_or_veg_type_are_water_but_not_both(
noah_owp_init_config: str,
veg_class: str,
veg_type: int,
soil_type: int,
):
o = NoahOWP.from_namelist_str(noah_owp_init_config)
o.parameters.veg_class_name = veg_class
o.structure.vegtyp = veg_type
o.structure.isltyp = soil_type

# ensure warning _is_ emitted
with pytest.warns():
NoahOWP.from_namelist_str(o.to_namelist_str())


does_not_warn_cases = (
# positive cases
("USGS", VEG_USGS_WATER, SOIL_TYPE_WATER),
("MODIFIED_IGBP_MODIS_NOAH", VEG_MODIS_WATER, SOIL_TYPE_WATER),
# negative cases
("USGS", VEG_USGS_WATER + 1, SOIL_TYPE_WATER + 1),
("MODIFIED_IGBP_MODIS_NOAH", VEG_MODIS_WATER + 1, SOIL_TYPE_WATER + 1),
)


@pytest.mark.parametrize("veg_class,veg_type,soil_type", does_not_warn_cases)
def test_noah_owp_does_not_warns_if_soil_and_veg_type_are_water_or_neither_water(
noah_owp_init_config: str,
veg_class: str,
veg_type: int,
soil_type: int,
):
o = NoahOWP.from_namelist_str(noah_owp_init_config)
o.parameters.veg_class_name = veg_class
o.structure.vegtyp = veg_type
o.structure.isltyp = soil_type

# ensure warning is not emitted
with warnings.catch_warnings():
warnings.simplefilter("error")
NoahOWP.from_namelist_str(o.to_namelist_str())

# WATER = 14
# o.parameters.veg_class_name = "USGS"
# o.structure.isltyp = WATER
# VEG_USGS_WATER = 16
# o.structure.vegtyp = VEG_USGS_WATER + 1

# # ensure warning is not emitted
# with pytest.warns():
# NoahOWP.from_namelist_str(o.to_namelist_str())

# o.parameters.veg_class_name = "MODIFIED_IGBP_MODIS_NOAH"
# o.structure.isltyp = WATER
# VEG_MODIS_WATER = 17
# o.structure.vegtyp = VEG_MODIS_WATER

# assert o.to_namelist_str() == noah_owp_init_config
Loading