From 60f2bbb9c25852bcaf93b66c20669c6c208d19d9 Mon Sep 17 00:00:00 2001 From: David Huard Date: Thu, 6 Mar 2025 10:11:10 -0500 Subject: [PATCH 01/15] added new eval metrics and state variables --- src/ravenpy/config/options.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/ravenpy/config/options.py b/src/ravenpy/config/options.py index 5d26a6cb..ca9b63be 100644 --- a/src/ravenpy/config/options.py +++ b/src/ravenpy/config/options.py @@ -114,6 +114,7 @@ "LAKE_STORAGE", "SNOW", "SNOW_LIQ", + "TOTAL_SWE", "GLACIER", "GLACIER_ICE", "CONVOLUTION", @@ -272,13 +273,16 @@ class EvaluationMetrics(Enum): RMSE = "RMSE" PCT_BIAS = "PCT_BIAS" ABSERR = "ABSERR" + ABSERR_RUN = "ABSERR_RUN" ABSMAX = "ABSMAX" PDIFF = "PDIFF" TMVOL = "TMVOL" RCOEFF = "RCOEFF" NSC = "NSC" KLING_GUPTA = "KLING_GUPTA" + KGE_PRIME = "KGE_PRIME" DIAG_SPEARMAN = "DIAG_SPEARMAN" + FUZZY_NASH = "FUZZY_NASH" evaluation_metrics_multiplier = dict( From e71f888cc16a11b365f0caef353617016e5c805a Mon Sep 17 00:00:00 2001 From: Zeitsperre <10819524+Zeitsperre@users.noreply.github.com> Date: Thu, 6 Mar 2025 14:45:22 -0500 Subject: [PATCH 02/15] fix missing comma --- src/ravenpy/config/options.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/ravenpy/config/options.py b/src/ravenpy/config/options.py index ca9b63be..713e801c 100644 --- a/src/ravenpy/config/options.py +++ b/src/ravenpy/config/options.py @@ -219,7 +219,8 @@ "CHU_MATURITY", "VEG_DIAM", "VEG_MBETA", - "VEG_DENS" "PET_VEG_CORR", + "VEG_DENS", + "PET_VEG_CORR", "TFRAIN", "TFSNOW", "RELATIVE_HT", From c0bf5ce127fc00280aeed6d8d11c363e8568672f Mon Sep 17 00:00:00 2001 From: Zeitsperre <10819524+Zeitsperre@users.noreply.github.com> Date: Thu, 6 Mar 2025 14:53:48 -0500 Subject: [PATCH 03/15] update Blended model expected output --- tests/test_emulators.py | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/tests/test_emulators.py b/tests/test_emulators.py index 71f0e90e..9cd27bda 100644 --- a/tests/test_emulators.py +++ b/tests/test_emulators.py @@ -21,7 +21,8 @@ "CanadianShield": 0.4001, # <- This is the new value for CanadianShield with RavenHydroFramework v3.8 and v3.8.1 "HYPR": 0.685188, "SACSMA": -0.0382907, - "Blended": -0.913785, + # "Blended": -0.913785, <- This is the original value for Blended with RavenHydroFramework v3.8.1 + "Blended": -1.1507, # <- This is the new value for Blended with RavenHydroFramework v4.0.1 } @@ -38,12 +39,7 @@ def test_run(numeric_config, tmp_path): # assert conf.__config__.allow_mutation e = Emulator(config=conf, workdir=tmp_path) - # FIXME: The Blended model run returns error code -11. - if name == "Blended": - pytest.skip("The Blended model run returns error code -11.") - out = e.run() - d = out.diagnostics np.testing.assert_almost_equal(d["DIAG_NASH_SUTCLIFFE"], NSE[name], 4) From 65a73c6272e36445c31562a32c33a861b1a4e209 Mon Sep 17 00:00:00 2001 From: Zeitsperre <10819524+Zeitsperre@users.noreply.github.com> Date: Thu, 6 Mar 2025 17:19:14 -0500 Subject: [PATCH 04/15] add more eval methods to existing tests --- tests/test_commands.py | 26 ++++++++++++++++++++++++-- tests/test_emulators.py | 13 ++++++++----- 2 files changed, 32 insertions(+), 7 deletions(-) diff --git a/tests/test_commands.py b/tests/test_commands.py index 20971f2a..7cd18f2c 100644 --- a/tests/test_commands.py +++ b/tests/test_commands.py @@ -34,12 +34,34 @@ class Test(RV): def test_evaluation_metrics(): + + evaluation_metrics = [ + "ABSERR", + "ABSERR_RUN", + "ABSMAX", + "DIAG_SPEARMAN", + "FUZZY_NASH", + "KGE_PRIME", + "KLING_GUPTA", + "LOG_NASH", + "NASH_SUTCLIFFE", + "NSC", + "PCT_BIAS", + "PDIFF", + "RCOEFF", + "RMSE", + "TMVOL", + ] + class Test(RV): em: Sequence[o.EvaluationMetrics] = Field( - ["RMSE", "NASH_SUTCLIFFE"], alias="EvaluationMetrics" + evaluation_metrics, alias="EvaluationMetrics" ) - assert Test().to_rv().strip() == ":EvaluationMetrics RMSE NASH_SUTCLIFFE" + assert ( + Test().to_rv().strip() + == f":EvaluationMetrics {' '.join(evaluation_metrics)}" + ) def test_hrus(): diff --git a/tests/test_emulators.py b/tests/test_emulators.py index 9cd27bda..54682ead 100644 --- a/tests/test_emulators.py +++ b/tests/test_emulators.py @@ -149,8 +149,10 @@ def test_evaluation_periods(gr4jcn_config, tmp_path): """Test multiple evaluation periods are parsed correctly.""" gr4jcn, params = gr4jcn_config + evaluation_metrics = ["RMSE", "KLING_GUPTA", "KGE_PRIME"] + conf = gr4jcn.set_params(params) - conf.evaluation_metrics = ["RMSE", "KLING_GUPTA"] + conf.evaluation_metrics = evaluation_metrics conf.evaluation_period = [ rc.EvaluationPeriod(name="period1", start="2000-01-01", end="2000-01-07"), rc.EvaluationPeriod(name="period2", start="2001-01-01", end="2000-01-15"), @@ -158,9 +160,9 @@ def test_evaluation_periods(gr4jcn_config, tmp_path): out = Emulator(conf, workdir=tmp_path).run() d = out.diagnostics - assert "DIAG_RMSE" in d - assert "DIAG_KLING_GUPTA" in d - assert len(d["DIAG_RMSE"]) == 3 # ALL, period1, period2 + for name in evaluation_metrics: + assert f"DIAG_{name}" in d + assert len(d[f"DIAG_{name}"]) == 3 # ALL, period1, period2 @pytest.mark.slow @@ -407,7 +409,7 @@ def test_routing(get_local_testdata): GlobalParameter={"AVG_ANNUAL_RUNOFF": avg_annual_runoff}, WriteForcingFunctions=True, UniformInitialConditions=None, - EvaluationMetrics=("NASH_SUTCLIFFE",), + EvaluationMetrics=("NASH_SUTCLIFFE", "KGE_PRIME"), ) ############# @@ -458,6 +460,7 @@ def test_routing(get_local_testdata): # outlet of subbasin 2 d = out.diagnostics np.testing.assert_almost_equal(d["DIAG_NASH_SUTCLIFFE"], -0.0141168, 4) + np.testing.assert_almost_equal(d["DIAG_NASH_SUTCLIFFE"], -0.0141168, 4) assert len(list(out.path.glob("*ForcingFunctions.nc"))) == 1 From 89e143d00733840e79c6c05bef0294e55f3523dc Mon Sep 17 00:00:00 2001 From: Zeitsperre <10819524+Zeitsperre@users.noreply.github.com> Date: Thu, 6 Mar 2025 17:27:56 -0500 Subject: [PATCH 05/15] sort enums for legibility --- src/ravenpy/config/options.py | 464 +++++++++++++++++----------------- 1 file changed, 232 insertions(+), 232 deletions(-) diff --git a/src/ravenpy/config/options.py b/src/ravenpy/config/options.py index 713e801c..4322c362 100644 --- a/src/ravenpy/config/options.py +++ b/src/ravenpy/config/options.py @@ -2,231 +2,231 @@ from typing import Literal Forcings = Literal[ + "AIR_DENS", + "AIR_PRES", + "CLOUD_COVER", + "DAY_ANGLE", + "DAY_LENGTH", + "ET_RADIA", + "LW_INCOMING", + "LW_RADIA_NET", + "OW_PET", + "PET", + "PET_MONTH_AVE", + "POTENTIAL_MELT", "PRECIP", - "PRECIP_DAILY_AVE", "PRECIP_5DAY", - "SNOW_FRAC", - "SNOWFALL", + "PRECIP_DAILY_AVE", "RAINFALL", "RECHARGE", + "REL_HUMIDITY", + "SHORTWAVE", + "SNOWFALL", + "SNOW_FRAC", + "SUBDAILY_CORR", + "SW_RADIA", + "SW_RADIA_NET", "TEMP_AVE", + "TEMP_AVE_UNC", "TEMP_DAILY_AVE", - "TEMP_MIN", + "TEMP_DAILY_MAX", "TEMP_DAILY_MIN", "TEMP_MAX", - "TEMP_DAILY_MAX", - "TEMP_MONTH_MAX", - "TEMP_MONTH_MIN", - "TEMP_MONTH_AVE", - "TEMP_AVE_UNC", "TEMP_MAX_UNC", + "TEMP_MIN", "TEMP_MIN_UNC", - "AIR_DENS", - "AIR_PRES", - "REL_HUMIDITY", - "ET_RADIA", - "SHORTWAVE", - "SW_RADIA", - "SW_RADIA_NET", - "LW_RADIA_NET", - "LW_INCOMING", - "CLOUD_COVER", - "DAY_LENGTH", - "DAY_ANGLE", + "TEMP_MONTH_AVE", + "TEMP_MONTH_MAX", + "TEMP_MONTH_MIN", "WIND_VEL", - "PET", - "OW_PET", - "PET_MONTH_AVE", - "POTENTIAL_MELT", - "SUBDAILY_CORR", ] # Allowed soil parameters SoilParameters = Literal[ - "SAND_CON", - "CLAY_CON", - "SILT_CON", - "ORG_CON", - "POROSITY", - "STONE_FRAC", - "SAT_WILT", - "FIELD_CAPACITY", + "AIR_ENTRY_PRESSURE", + "ALBEDO_DRY", + "ALBEDO_WET", + "BASEFLOW_COEFF", + "BASEFLOW_COEFF2", + "BASEFLOW_N", + "BASEFLOW_THRESH", + "BF_LOSS_FRACTION", "BULK_DENSITY", - "HYDRAUL_COND", - "CLAPP_B", + "B_EXP", "CLAPP N,CLAPP M", - "SAT_RES", - "AIR_ENTRY_PRESSURE", - "WILTING_PRESSURE", - "HEAT_CAPACITY", - "THERMAL_COND", - "WETTING_FRONT_PSI", + "CLAPP_B", + "CLAY_CON", "EVAP_RES_FC", - "SHUTTLEWORTH_B", - "ALBEDO_WET", - "ALBEDO_DRY", - "VIZ_ZMIN", - "VIC_ZMAX", - "VIC ALPHA", - "VIC_EVAP_GAMMA", + "FIELD_CAPACITY", + "GR4J_X2", + "GR4J_X3", + "HBV_BETA", + "HEAT_CAPACITY", + "HYDRAUL_COND", + "INTERFLOW_COEF", + "MAX_BASEFLOW_RATE", + "MAX_CAP_RISE_RATE", + "MAX_INTERFLOW_RATE", "MAX_PERC_RATE", - "PERC_N", + "ORG_CON", "PERC_COEFF", + "PERC_N", + "PET_CORRECTION", # Not in Raven Docs Table A.3 + "POROSITY", "SAC_PERC_ALPHA", "SAC_PERC_EXPON", "SAC_PERC_PFREE", - "UNAVAIL_FRAC", - "HBV_BETA", - "MAX_BASEFLOW_RATE", - "BASEFLOW_N", - "BASEFLOW_COEFF", - "BASEFLOW_COEFF2", - "BASEFLOW_THRESH", - "BF_LOSS_FRACTION", + "SAND_CON", + "SAT_RES", + "SAT_WILT", + "SHUTTLEWORTH_B", + "SILT_CON", + "STONE_FRAC", "STORAGE_THRESHOLD", - "MAX_CAP_RISE_RATE", - "MAX_INTERFLOW_RATE", - "INTERFLOW_COEF", + "THERMAL_COND", "UBC_EVAL_SOIL_DEF", "UBC_INFIL_SOIL_DEF", - "GR4J_X2", - "GR4J_X3", - "B_EXP", - "PET_CORRECTION", # Not in Raven Docs Table A.3 + "UNAVAIL_FRAC", + "VIC ALPHA", + "VIC_EVAP_GAMMA", + "VIC_ZMAX", + "VIZ_ZMIN", + "WETTING_FRONT_PSI", + "WILTING_PRESSURE", ] StateVariables = Literal[ - "SURFACE_WATER", "ATMOSPHERE", "ATMOS_PRECIP", - "PONDED_WATER", - "SOIL", - "SOIL[0]", - "SOIL[1]", - "SOIL[2]", - "GROUNDWATER", "CANOPY", "CANOPY_SNOW", - "TRUNK", - "ROOT", - "DEPRESSION", - "WETLAND", - "LAKE_STORAGE", - "SNOW", - "SNOW_LIQ", - "TOTAL_SWE", - "GLACIER", - "GLACIER_ICE", + "CANOPY_TEMP", + "COLD_CONTENT", + "CONSTITUENT", + "CONSTITUENT_SINK", + "CONSTITUENT_SRC", + "CONSTITUENT_SW", "CONVOLUTION", "CONV_STOR", - "SURFACE_WATER_TEMP", - "SNOW_TEMP", - "COLD_CONTENT", - "GLACIER_CC", - "SOIL_TEMP", - "CANOPY_TEMP", - "SNOW_DEPTH", - "PERMAFROST_DEPTH", - "SNOW_COVER", - "SNOW_AGE", - "SNOW_ALBEDO", "CROP_HEAT_UNITS", "CUM_INFIL", "CUM_SNOWMELT", - "CONSTITUENT", - "CONSTITUENT_SRC", - "CONSTITUENT_SW", - "CONSTITUENT_SINK", + "DEPRESSION", + "GLACIER", + "GLACIER_CC", + "GLACIER_ICE", + "GROUNDWATER", + "LAKE_STORAGE", "MULTIPLE", + "PERMAFROST_DEPTH", + "PONDED_WATER", + "ROOT", + "SNOW", + "SNOW_AGE", + "SNOW_ALBEDO", + "SNOW_COVER", + "SNOW_DEPTH", + "SNOW_LIQ", + "SNOW_TEMP", + "SOIL", + "SOIL[0]", + "SOIL[1]", + "SOIL[2]", + "SOIL_TEMP", + "SURFACE_WATER", + "SURFACE_WATER_TEMP", + "TOTAL_SWE", + "TRUNK", + "WETLAND", ] LandUseParameters = Literal[ - "FOREST_COVERAGE", - "IMPERMEABLE_FRAC", - "ROUGHNESS", - "FOREST_SPARSENESS", - "DEP_MAX", - "MAX_DEP_AREA_FRAC", + "ABST_PERCENT", + "AET_COEFF", + "BF_LOSS_FRACTION", + "B_EXP", + "CC_DECAY_COEFF", + "DD_AGGRADATION", "DD_MELT_TEMP", - "MELT_FACTOR", "DD_REFREEZE_TEMP", - "MIN_MELT_FACTOR", - "MAX_MELT_FACTOR", - "REFREEZE_FACTOR", - "REFREEZE_EXP", - "DD_AGGRADATION", - "SNOW_PATCH_LIMIT", - "HBV_MELT_FOR_CORR", - "HBV_MELT_ASP_CORR", - "GLAC_STORAGE_COEFF", - "HBV_MELT_GLACIER_CORR", - "HBV_GLACIER_KMIN", - "HBV_GLACIER_AG", - "CC_DECAY_COEFF", - "SCS_CN", - "SCS_IA_FRACTION", - "PARTITION_COEFF", - "MAX_SAT_AREA_FRAC", - "B_EXP", - "ABST_PERCENT", + "DEP_K", + "DEP_MAX", "DEP_MAX_FLOW", "DEP_N", "DEP_SEEP_K", - "DEP_K", "DEP_THRESHOLD", - "PDMROF_B", - "PONDED_EXP", - "OW_PET_CORR", - "LAKE_PET_CORR", - "LAKE_REL_COEFF", + "FOREST_COVERAGE", "FOREST_PET_CORR", + "FOREST_SPARSENESS", "GAMMA_SCALE", - "GAMMA_SHAPE", "GAMMA_SCALE2", + "GAMMA_SHAPE", "GAMMA_SHAPE2", - "HMETS_RUNOFF_COEFF", - "AET_COEFF", + "GLAC_STORAGE_COEFF", "GR4J_X4", - "UBC_ICEPT_FACTOR", + "HBV_GLACIER_AG", + "HBV_GLACIER_KMIN", + "HBV_MELT_ASP_CORR", + "HBV_MELT_FOR_CORR", + "HBV_MELT_GLACIER_CORR", + "HMETS_RUNOFF_COEFF", + "IMPERMEABLE_FRAC", + "LAKE_PET_CORR", + "LAKE_REL_COEFF", + "MAX_DEP_AREA_FRAC", + "MAX_MELT_FACTOR", + "MAX_SAT_AREA_FRAC", + "MELT_FACTOR", + "MIN_MELT_FACTOR", + "OW_PET_CORR", + "PARTITION_COEFF", + "PDMROF_B", + "PONDED_EXP", + "REFREEZE_EXP", + "REFREEZE_FACTOR", + "ROUGHNESS", + "SCS_CN", + "SCS_IA_FRACTION", + "SNOW_PATCH_LIMIT", "STREAM_FRACTION", - "BF_LOSS_FRACTION", + "UBC_ICEPT_FACTOR", ] VegetationParameters = Literal[ - "MAX_HEIGHT", - "MAX_LEAF_COND", - "MAX_LAI", - "SVF_EXTINCTION", - "RAIN_ICEPT_PCT", - "SNOW_ICEPT_PCT", - "RAIN_ICEPT_FACT", - "SNOW_ICEPT_FACT", - "SAI_HT_RATIO", - "TRUNK_FRACTION", - "STEMFLOW_FRAC", "ALBEDO", "ALBEDO_WET", + "CAP_LAI_RATIO", + "CHU_MATURITY", + "DRIP_PROPORTION", "MAX_CAPACITY", - "MAX_SNOW_CAPACITY", - "ROOT_EXTINCT", + "MAX_HEIGHT", + "MAX_INTERCEPT_RATE", + "MAX_LAI", + "MAX_LEAF_COND", "MAX_ROOT_LENGTH", + "MAX_SNOW_CAPACITY", "MIN_RESISTIVITY", - "XYLEM_FRAC", - "ROOTRADIUS", - "PSI_CRITICAL", - "DRIP_PROPORTION", - "MAX_INTERCEPT_RATE", - "CHU_MATURITY", - "VEG_DIAM", - "VEG_MBETA", - "VEG_DENS", "PET_VEG_CORR", - "TFRAIN", - "TFSNOW", + "PSI_CRITICAL", + "RAIN_ICEPT_FACT", + "RAIN_ICEPT_PCT", "RELATIVE_HT", "RELATIVE_LAI", - "CAP_LAI_RATIO", + "ROOTRADIUS", + "ROOT_EXTINCT", + "SAI_HT_RATIO", "SNOCAP_LAI_RATIO", + "SNOW_ICEPT_FACT", + "SNOW_ICEPT_PCT", + "STEMFLOW_FRAC", + "SVF_EXTINCTION", + "TFRAIN", + "TFSNOW", + "TRUNK_FRACTION", + "VEG_DENS", + "VEG_DIAM", + "VEG_MBETA", + "XYLEM_FRAC", ] @@ -238,28 +238,28 @@ class AirPressureMethod(Enum): class Calendar(Enum): - PROLEPTIC_GREGORIAN = "PROLEPTIC_GREGORIAN" - JULIAN = "JULIAN" + ALL_LEAP = "ALL_LEAP" GREGORIAN = "GREGORIAN" - STANDARD = "STANDARD" + JULIAN = "JULIAN" NOLEAP = "NOLEAP" + PROLEPTIC_GREGORIAN = "PROLEPTIC_GREGORIAN" + STANDARD = "STANDARD" _365_DAY = "365_DAY" - ALL_LEAP = "ALL_LEAP" _366_DAY = "366_DAY" class CatchmentRoute(Enum): """:CatchmentRoute""" + DELAYED_FIRST_ORDER = "ROUTE_DELAYED_FIRST_ORDER" DUMP = "ROUTE_DUMP" + EXP = "ROUTE_EXPONENTIAL" + EXPONENTIAL_UH = "EXPONENTIAL_UH" GAMMA = "ROUTE_GAMMA_CONVOLUTION" + RESERVOIR = "ROUTE_RESERVOIR_SERIES" TRI = "ROUTE_TRI_CONVOLUTION" - TRI_CONVOLUTION = "TRI_CONVOLUTION" TRIANGULAR_UH = "TRIANGULAR_UH" - RESERVOIR = "ROUTE_RESERVOIR_SERIES" - EXP = "ROUTE_EXPONENTIAL" - DELAYED_FIRST_ORDER = "ROUTE_DELAYED_FIRST_ORDER" - EXPONENTIAL_UH = "EXPONENTIAL_UH" + TRI_CONVOLUTION = "TRI_CONVOLUTION" class CloudCoverMethod(Enum): @@ -269,70 +269,70 @@ class CloudCoverMethod(Enum): class EvaluationMetrics(Enum): - NASH_SUTCLIFFE = "NASH_SUTCLIFFE" - LOG_NASH = "LOG_NASH" - RMSE = "RMSE" - PCT_BIAS = "PCT_BIAS" ABSERR = "ABSERR" ABSERR_RUN = "ABSERR_RUN" ABSMAX = "ABSMAX" - PDIFF = "PDIFF" - TMVOL = "TMVOL" - RCOEFF = "RCOEFF" - NSC = "NSC" - KLING_GUPTA = "KLING_GUPTA" - KGE_PRIME = "KGE_PRIME" DIAG_SPEARMAN = "DIAG_SPEARMAN" FUZZY_NASH = "FUZZY_NASH" + KGE_PRIME = "KGE_PRIME" + KLING_GUPTA = "KLING_GUPTA" + LOG_NASH = "LOG_NASH" + NASH_SUTCLIFFE = "NASH_SUTCLIFFE" + NSC = "NSC" + PCT_BIAS = "PCT_BIAS" + PDIFF = "PDIFF" + RCOEFF = "RCOEFF" + RMSE = "RMSE" + TMVOL = "TMVOL" evaluation_metrics_multiplier = dict( - NASH_SUTCLIFFE=1, - LOG_NASH=1, - RMSE=-1, - PCT_BIAS="Not Supported", ABSERR=-1, ABSMAX=-1, + DIAG_SPEARMAN=1, + KLING_GUPTA=1, + LOG_NASH=1, + NASH_SUTCLIFFE=1, + NSC=1, + PCT_BIAS="Not Supported", PDIFF=-1, - TMVOL=-1, RCOEFF=-1, - NSC=1, - KLING_GUPTA=1, - DIAG_SPEARMAN=1, + RMSE=-1, + TMVOL=-1, ) class Evaporation(Enum): CONSTANT = "PET_CONSTANT" - PENMAN_MONTEITH = "PET_PENMAN_MONTEITH" - PENMAN_COMBINATION = "PET_PENMAN_COMBINATION" - PRIESTLEY_TAYLOR = "PET_PRIESTLEY_TAYLOR" - HARGREAVES = "PET_HARGREAVES" - HARGREAVES_1985 = "PET_HARGREAVES_1985" - FROMMONTHLY = "PET_FROMMONTHLY" DATA = "PET_DATA" + FROMMONTHLY = "PET_FROMMONTHLY" HAMON_1961 = "PET_HAMON_1961" - TURC_1961 = "PET_TURC_1961" + HARGREAVES = "PET_HARGREAVES" + HARGREAVES_1985 = "PET_HARGREAVES_1985" MAKKINK_1957 = "PET_MAKKINK_1957" - MONTHLY_FACTOR = "PET_MONTHLY_FACTOR" MOHYSE = "PET_MOHYSE" + MONTHLY_FACTOR = "PET_MONTHLY_FACTOR" OUDIN = "PET_OUDIN" + PENMAN_COMBINATION = "PET_PENMAN_COMBINATION" + PENMAN_MONTEITH = "PET_PENMAN_MONTEITH" + PRIESTLEY_TAYLOR = "PET_PRIESTLEY_TAYLOR" + TURC_1961 = "PET_TURC_1961" VAP_DEFICIT = "PET_VAPDEFICIT" class LWIncomingMethod(Enum): DATA = "LW_INC_DATA" DEFAULT = "LW_INC_DEFAULT" + DINGMAN = "LW_INC_DINGMAN" SICART = "LW_INC_SICART" SKYVIEW = "LW_INC_SKYVIEW" - DINGMAN = "LW_INC_DINGMAN" class Interpolation(Enum): - FROM_FILE = "INTERP_FROM_FILE" AVERAGE_ALL = "INTERP_AVERAGE_ALL" - NEAREST_NEIGHBOR = "INTERP_NEAREST_NEIGHBOR" + FROM_FILE = "INTERP_FROM_FILE" INVERSE_DISTANCE = "INTERP_INVERSE_DISTANCE" + NEAREST_NEIGHBOR = "INTERP_NEAREST_NEIGHBOR" class LWRadiationMethod(Enum): @@ -342,45 +342,45 @@ class LWRadiationMethod(Enum): class MonthlyInterpolationMethod(Enum): - UNIFORM = "MONTHINT_UNIFORM" - LINEAR_MID = "MONTHINT_LINEAR_MID" - LINEAR_FOM = "MONTHINT_LINEAR_FOM" LINEAR_21 = "MONTHINT_LINEAR_21" + LINEAR_FOM = "MONTHINT_LINEAR_FOM" + LINEAR_MID = "MONTHINT_LINEAR_MID" + UNIFORM = "MONTHINT_UNIFORM" class OroPETCorrect(Enum): + HBV = "OROCORR_HBV" NONE = "OROCORR_NONE" SIMPLELAPSE = "OROCORR_SIMPLELAPSE" - HBV = "OROCORR_HBV" class OroPrecipCorrect(Enum): - NONE = "OROCORR_NONE" - UBC = "OROCORR_UBC" HBV = "OROCORR_HBV" + NONE = "OROCORR_NONE" SIMPLELAPSE = "OROCORR_SIMPLELAPSE" + UBC = "OROCORR_UBC" class OroTempCorrect(Enum): - NONE = "OROCORR_NONE" HBV = "OROCORR_HBV" + NONE = "OROCORR_NONE" + SIMPLELAPSE = "OROCORR_SIMPLELAPSE" UBC = "OROCORR_UBC" UBC2 = "OROCORR_UBC_2" - SIMPLELAPSE = "OROCORR_SIMPLELAPSE" class PotentialMeltMethod(Enum): """:PotentialMelt algorithms""" - DEGREE_DAY = "POTMELT_DEGREE_DAY" - NONE = "POTMELT_NONE" - RESTRICTED = "POTMELT_RESTRICTED" DATA = "POTMELT_DATA" + DEGREE_DAY = "POTMELT_DEGREE_DAY" EB = "POTMELT_EB" - USACE = "POTMELT_USACE" - HMETS = "POTMELT_HMETS" HBV = "POTMELT_HBV" + HMETS = "POTMELT_HMETS" + NONE = "POTMELT_NONE" + RESTRICTED = "POTMELT_RESTRICTED" UBC = "POTMELT_UBC" + USACE = "POTMELT_USACE" class Precipitation(Enum): @@ -389,38 +389,38 @@ class Precipitation(Enum): class PrecipIceptFract(Enum): USER = "PRECIP_ICEPT_USER" # default - LAI = "PRECIP_ICEPT_LAI" EXPLAI = "PRECIP_ICEPT_EXPLAI" - NONE = "PRECIP_ICEPT_NONE" HEDSTROM = "PRECIP_ICEPT_HEDSTROM" + LAI = "PRECIP_ICEPT_LAI" + NONE = "PRECIP_ICEPT_NONE" class RainSnowFraction(Enum): DATA = "RAINSNOW_DATA" DINGMAN = "RAINSNOW_DINGMAN" - UBC = "RAINSNOW_UBC" - HBV = "RAINSNOW_HBV" HARDER = "RAINSNOW_HARDER" + HBV = "RAINSNOW_HBV" HSPF = "RAINSNOW_HSPF" - WANG = "RAINSNOW_WANG" SNTHERM89 = "RAINSNOW_SNTHERM89" + UBC = "RAINSNOW_UBC" + WANG = "RAINSNOW_WANG" class RelativeHumidityMethod(Enum): CONSTANT = "RELHUM_CONSTANT" + CORR = "RELHUM_CORR" DATA = "RELHUM_DATA" MINDEWPT = "RELHUM_MINDEWPT" - CORR = "RELHUM_CORR" WINDVEL = "WINDVEL_CORR" class Routing(Enum): DIFFUSIVE_WAVE = "ROUTE_DIFFUSIVE_WAVE" HYDROLOGIC = "ROUTE_HYDROLOGIC" + MUSKINGUM = "MUSKINGUM" NONE = "ROUTE_NONE" - STORAGE_COEFF = "ROUTE_STORAGE_COEFF" PLUG_FLOW = "ROUTE_PLUG_FLOW" - MUSKINGUM = "MUSKINGUM" + STORAGE_COEFF = "ROUTE_STORAGE_COEFF" class SoilModel(Enum): @@ -430,20 +430,20 @@ class SoilModel(Enum): SubBasinProperties = Literal[ - "TIME_TO_PEAK", - "TIME_CONC", - "TIME_LAG", - "NUM_RESERVOIRS", - "RES_CONSTANT", - "GAMMA_SHAPE", + "CELERITY", + "DIFFUSIVITY", "GAMMA_SCALE", - "Q_REFERENCE", + "GAMMA_SHAPE", "MANNINGS_N", - "SLOPE", - "DIFFUSIVITY", - "CELERITY", + "NUM_RESERVOIRS", + "Q_REFERENCE", "RAIN_CORR", + "RES_CONSTANT", + "SLOPE", "SNOW_CORR", + "TIME_CONC", + "TIME_LAG", + "TIME_TO_PEAK", ] @@ -455,16 +455,16 @@ class SubdailyMethod(Enum): class SWCanopyCorrect(Enum): NONE = "SW_CANOPY_CORR_NONE" # Default - STATIC = "SW_CANOPY_CORR_STATIC" DYNAMIC = "SW_CANOPY_CORR_DYNAMIC" + STATIC = "SW_CANOPY_CORR_STATIC" UBC = "SW_CANOPY_CORR_UBC" class SWCloudCorrect(Enum): NONE = "SW_CLOUD_CORR_NONE" # Default + ANNANDALE = "SW_CLOUD_CORR_ANNANDALE" DINGMAN = "SW_CLOUD_CORR_DINGMAN" UBC = "SW_CLOUD_CORR_UBCWM" - ANNANDALE = "SW_CLOUD_CORR_ANNANDALE" class SWRadiationMethod(Enum): @@ -480,8 +480,8 @@ class WindspeedMethod(Enum): class EnKFMode(Enum): - SPINUP = "ENKF_SPINUP" CLOSED_LOOP = "ENKF_CLOSED_LOOP" FORECAST = "ENKF_FORECAST" - OPEN_LOOP = "ENKF_OPEN_LOOP" OPEN_FORECAST = "ENKF_OPEN_FORECAST" + OPEN_LOOP = "ENKF_OPEN_LOOP" + SPINUP = "ENKF_SPINUP" From 5f47afeff1b29cbe816b6d90ed0344f99a4951eb Mon Sep 17 00:00:00 2001 From: Trevor James Smith <10819524+Zeitsperre@users.noreply.github.com> Date: Mon, 10 Mar 2025 11:48:36 -0400 Subject: [PATCH 06/15] fix typing --- src/ravenpy/config/commands.py | 8 ++++---- tests/emulators.py | 9 ++------- tests/test_emulators.py | 4 +--- 3 files changed, 7 insertions(+), 14 deletions(-) diff --git a/src/ravenpy/config/commands.py b/src/ravenpy/config/commands.py index 0b0d7299..79a22363 100644 --- a/src/ravenpy/config/commands.py +++ b/src/ravenpy/config/commands.py @@ -166,8 +166,8 @@ class EvaluationPeriod(LineCommand): """ name: str - start: dt.date - end: dt.date + start: Union[str, dt.date] + end: Union[str, dt.date] class CustomOutput(LineCommand): @@ -427,8 +427,8 @@ class ChannelProfile(FlatCommand): name: str = "chn_XXX" bed_slope: float = 0 - survey_points: tuple[tuple[float, float], ...] = () - roughness_zones: tuple[tuple[float, float], ...] = () + survey_points: list[tuple[Union[int, float, ...], Union[int, float, ...]]] = () + roughness_zones: list[tuple[Union[int, float, ...], Union[int, float, ...]]] = () def to_rv(self): template = """ diff --git a/tests/emulators.py b/tests/emulators.py index aa492ece..f637d681 100644 --- a/tests/emulators.py +++ b/tests/emulators.py @@ -289,19 +289,14 @@ def symbolic_config(get_local_testdata, salmon_hru, request): ) # Extra attributes for emulator - extras = {} - if name in ["CanadianShield", "HYPR", "SACSMA", "Blended"]: salmon_hru["slope"] = 0.01234 hrus = [salmon_hru["land"]] + extras = {} if name in ["GR4JCN"]: - extras.update( - dict( - GlobalParameter={"AVG_ANNUAL_RUNOFF": 208.480}, - ) - ) + extras.update({"GlobalParameter": {"AVG_ANNUAL_RUNOFF": 208.480}}) if name in ["HMETS"]: data_type.extend(["PET"]) diff --git a/tests/test_emulators.py b/tests/test_emulators.py index 54682ead..1e8cc710 100644 --- a/tests/test_emulators.py +++ b/tests/test_emulators.py @@ -1,5 +1,4 @@ import datetime as dt -import warnings import numpy as np import pytest @@ -62,7 +61,7 @@ def test_run(numeric_config, tmp_path): @pytest.mark.skip("Need to find a clean way to freeze emulator config instance.") def test_emulator_config_is_read_only(dummy_config, tmp_path): - cls, P = dummy_config + cls, _ = dummy_config e = Emulator(config=cls(), workdir=tmp_path) @@ -460,7 +459,6 @@ def test_routing(get_local_testdata): # outlet of subbasin 2 d = out.diagnostics np.testing.assert_almost_equal(d["DIAG_NASH_SUTCLIFFE"], -0.0141168, 4) - np.testing.assert_almost_equal(d["DIAG_NASH_SUTCLIFFE"], -0.0141168, 4) assert len(list(out.path.glob("*ForcingFunctions.nc"))) == 1 From d6b54d35fe8c40c638bbc6e864e1dea84180ba62 Mon Sep 17 00:00:00 2001 From: Trevor James Smith <10819524+Zeitsperre@users.noreply.github.com> Date: Mon, 10 Mar 2025 12:18:19 -0400 Subject: [PATCH 07/15] fix typing --- src/ravenpy/config/commands.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ravenpy/config/commands.py b/src/ravenpy/config/commands.py index 79a22363..08d08455 100644 --- a/src/ravenpy/config/commands.py +++ b/src/ravenpy/config/commands.py @@ -427,8 +427,8 @@ class ChannelProfile(FlatCommand): name: str = "chn_XXX" bed_slope: float = 0 - survey_points: list[tuple[Union[int, float, ...], Union[int, float, ...]]] = () - roughness_zones: list[tuple[Union[int, float, ...], Union[int, float, ...]]] = () + survey_points: list[tuple[Union[int, float], Union[int, float]]] = () + roughness_zones: list[tuple[Union[int, float], Union[int, float]]] = () def to_rv(self): template = """ From d71c9e94e2a7e5ad1c0c6d2aca15f702eb3adbe1 Mon Sep 17 00:00:00 2001 From: Zeitsperre <10819524+Zeitsperre@users.noreply.github.com> Date: Mon, 17 Mar 2025 16:46:26 -0400 Subject: [PATCH 08/15] address stability issues --- src/ravenpy/utilities/forecasting.py | 2 +- src/ravenpy/utilities/graphs.py | 4 +++- src/ravenpy/utilities/regionalization.py | 12 ++++++------ 3 files changed, 10 insertions(+), 8 deletions(-) diff --git a/src/ravenpy/utilities/forecasting.py b/src/ravenpy/utilities/forecasting.py index 84d4c10d..81643c8b 100644 --- a/src/ravenpy/utilities/forecasting.py +++ b/src/ravenpy/utilities/forecasting.py @@ -452,7 +452,7 @@ def compute_forecast_flood_risk( out = pct.to_dataset(name="exceedance_probability") out.attrs["source"] = f"PAVICS-Hydro flood risk forecasting tool, {domain}" out.attrs["history"] = ( - f"File created on {dt.datetime.utcnow().strftime('%Y-%m-%d %H:%M:%S')} " + f"File created on {dt.datetime.now(dt.UTC).strftime('%Y-%m-%d %H:%M:%S')} " f"UTC on the PAVICS-Hydro service available at {domain}." ) out.attrs["title"] = ( diff --git a/src/ravenpy/utilities/graphs.py b/src/ravenpy/utilities/graphs.py index 339b2be6..79997be3 100644 --- a/src/ravenpy/utilities/graphs.py +++ b/src/ravenpy/utilities/graphs.py @@ -91,6 +91,7 @@ def mean_annual_hydrograph(file_list: Sequence[Union[str, Path]]): fig, ax = plt.subplots() # initialize figure # Plot the observed streamflows if available + mah_obs = None if hasattr(ds[0], "q_obs"): q_obs = ds[0].q_obs mah_obs = q_obs.groupby("time.dayofyear").mean() @@ -122,7 +123,8 @@ def mean_annual_hydrograph(file_list: Sequence[Union[str, Path]]): ), ) - plt.xlim(0, mah_obs.shape[0]) + if mah_obs is not None: + plt.xlim(0, mah_obs.shape[0]) plt.ylim(bottom=0, top=None) ax.set_xlabel("Time") ax.set_ylabel(r"$Streamflow [m^3s^{{-1}}]$") diff --git a/src/ravenpy/utilities/regionalization.py b/src/ravenpy/utilities/regionalization.py index 4ac4a402..d4ab7dcb 100644 --- a/src/ravenpy/utilities/regionalization.py +++ b/src/ravenpy/utilities/regionalization.py @@ -284,7 +284,7 @@ def regionalization_params( ungauged_properties: pd.DataFrame, filtered_params: pd.DataFrame, filtered_prop: pd.DataFrame, -) -> list[float]: +) -> Union[list[list[float]], list[np.ndarray]]: """ Return the model parameters to use for the regionalization. @@ -306,7 +306,7 @@ def regionalization_params( Returns ------- list - List of model parameters to be used for the regionalization. + A list of model parameters to be used for the regionalization. """ if method == "MLR" or "RA" in method: mlr_params, r2 = multiple_linear_regression( @@ -318,7 +318,7 @@ def regionalization_params( mlr_params, ] - elif "RA" in method: + else: gp = gauged_params.copy() for p, r, col in zip(mlr_params, r2, gauged_params): @@ -385,12 +385,12 @@ def multiple_linear_regression( Returns ------- list of Any, list of int - A named tuple of the estimated model parameters and the R2 of the linear regression. + A named tuple of the estimated model parameters and the R2 of the linear regression. """ # Add constants to the gauged predictors x = sm.add_constant(source) - # Add the constant 1 for the ungauged catchment predictors + # Add the constant '1' for the ungauged catchment predictors predictors = sm.add_constant(target, prepend=True, has_constant="add") # Perform regression for each parameter @@ -400,6 +400,6 @@ def multiple_linear_regression( mlr_parameters = [r.predict(exog=predictors)[0] for r in regression] # Extract the adjusted r_squared value for each parameter - r2 = [r.rsquared_adj for r in regression] + r2 = [r.rsquared_adj() for r in regression] return mlr_parameters, r2 From 237577402ea2e7cc654a3e5e8bdc4add864c7d72 Mon Sep 17 00:00:00 2001 From: Trevor James Smith <10819524+Zeitsperre@users.noreply.github.com> Date: Tue, 18 Mar 2025 13:13:37 -0400 Subject: [PATCH 09/15] fix call signatures, docstrings, type inferences --- AUTHORS.rst | 2 +- README.rst | 2 +- docs/conf.py | 2 +- src/ravenpy/__init__.py | 2 +- src/ravenpy/cli/aggregate_forcings_to_hrus.py | 6 ++-- src/ravenpy/config/base.py | 4 +-- src/ravenpy/config/defaults.py | 4 +-- src/ravenpy/config/emulators/gr4jcn.py | 2 +- src/ravenpy/config/parsers.py | 6 ++-- src/ravenpy/config/utils.py | 2 -- src/ravenpy/extractors/routing_product.py | 33 +++++++++---------- src/ravenpy/utilities/analysis.py | 6 ++-- src/ravenpy/utilities/calibration.py | 3 +- src/ravenpy/utilities/checks.py | 4 +-- src/ravenpy/utilities/coords.py | 5 +-- src/ravenpy/utilities/geo.py | 6 ++-- src/ravenpy/utilities/geoserver.py | 25 +++++++++----- src/ravenpy/utilities/io.py | 16 ++++----- src/ravenpy/utilities/mk_test.py | 19 +++++++---- src/ravenpy/utilities/regionalization.py | 22 ++++++------- 20 files changed, 91 insertions(+), 80 deletions(-) diff --git a/AUTHORS.rst b/AUTHORS.rst index ab6ecc00..b54b795f 100644 --- a/AUTHORS.rst +++ b/AUTHORS.rst @@ -19,4 +19,4 @@ Contributors ------------ * Louise Arnal `@lou-a `_ -* Pascal Bourgault `@aulemahal `_ +* Pascal Bourgault `@aulemahal `_ diff --git a/README.rst b/README.rst index 70f99dea..074274d4 100644 --- a/README.rst +++ b/README.rst @@ -15,7 +15,7 @@ RavenPy |logo| +----------------------------+-----------------------------------------------------+ -A Python wrapper to setup and run the hydrologic modelling framework Raven_. +A Python wrapper for running the hydrologic modelling framework Raven_. * Free software: MIT license * Documentation: https://ravenpy.readthedocs.io diff --git a/docs/conf.py b/docs/conf.py index 42033493..9e146692 100755 --- a/docs/conf.py +++ b/docs/conf.py @@ -170,7 +170,7 @@ def rebuild_readme(): # the built documents. # # The short X.Y version. -version = ravenpy.__version__ +version = ravenpy.__version__.split("-")[0] # The full version, including alpha/beta/rc tags. release = ravenpy.__version__ diff --git a/src/ravenpy/__init__.py b/src/ravenpy/__init__.py index 0d6d3202..c5d9b8ea 100644 --- a/src/ravenpy/__init__.py +++ b/src/ravenpy/__init__.py @@ -1,4 +1,4 @@ -"""A Python wrapper to setup and run the hydrologic modelling framework Raven.""" +"""A Python wrapper for running the hydrologic modelling framework Raven.""" ################################################################################### # MIT License diff --git a/src/ravenpy/cli/aggregate_forcings_to_hrus.py b/src/ravenpy/cli/aggregate_forcings_to_hrus.py index c53b5b06..a41b8f98 100644 --- a/src/ravenpy/cli/aggregate_forcings_to_hrus.py +++ b/src/ravenpy/cli/aggregate_forcings_to_hrus.py @@ -66,7 +66,7 @@ def aggregate_forcings_to_hrus( command will require. """ # NOTE: This is in order to make sphinx-click happy. Magic. Do not touch. - import netCDF4 as nc4 # noqa: N813 + import netCDF4 import numpy as np gws = GridWeights.parse(Path(input_weight_file).read_text()) @@ -76,7 +76,7 @@ def aggregate_forcings_to_hrus( weights_data = gws.data # read NetCDF - nc_in = nc4.Dataset(input_nc_file, "r") + nc_in = netCDF4.Dataset(input_nc_file, "r") # length of dimensions nlon = nc_in.dimensions[dim_names[0]].size @@ -107,7 +107,7 @@ def aggregate_forcings_to_hrus( else: output_nc_file_path = Path(output_nc_file) - nc_out = nc4.Dataset(output_nc_file_path, "w") + nc_out = netCDF4.Dataset(output_nc_file_path, "w") _ = nc_out.createDimension("time", ntime) _ = nc_out.createDimension("nHRU", nHRU) diff --git a/src/ravenpy/config/base.py b/src/ravenpy/config/base.py index e39f8691..3bc46716 100644 --- a/src/ravenpy/config/base.py +++ b/src/ravenpy/config/base.py @@ -320,7 +320,7 @@ def parse_symbolic(value, **kwds): Note that parsing the output of `model_dump` can cause problems because there is not always enough information in the dictionary to recreate the correct model. """ - from pymbolic.mapper.evaluator import EvaluationMapper as EM # noqa: N817 + from pymbolic.mapper.evaluator import EvaluationMapper from pymbolic.primitives import ExpressionNode, Variable if isinstance(value, dict): @@ -337,7 +337,7 @@ def parse_symbolic(value, **kwds): elif isinstance(value, (Variable, ExpressionNode)): # Inject numerical values numerical value - return EM(context=kwds)(value) + return EvaluationMapper(context=kwds)(value) else: return value diff --git a/src/ravenpy/config/defaults.py b/src/ravenpy/config/defaults.py index 7b765548..3b5eee6e 100644 --- a/src/ravenpy/config/defaults.py +++ b/src/ravenpy/config/defaults.py @@ -47,8 +47,8 @@ def nc_attrs(cls, val): """Ensure default netCDF attributes are present.""" - # FIXME: assertions should not be found outside of testing code. Replace with conditional logic. - assert "model_id" in val # noqa: S101 + if "model_id" not in val: + raise ValueError("The key 'model_id' must be present in the input dictionary.") out = default_nc_attrs() out.update(val) diff --git a/src/ravenpy/config/emulators/gr4jcn.py b/src/ravenpy/config/emulators/gr4jcn.py index 1bb17141..7117c5d0 100644 --- a/src/ravenpy/config/emulators/gr4jcn.py +++ b/src/ravenpy/config/emulators/gr4jcn.py @@ -131,7 +131,7 @@ class GR4JCN(Config): p.Split( algo="RAVEN_DEFAULT", source="SOIL[2]", - to=["CONVOLUTION[0]", "CONVOLUTION[1]"], + to=("CONVOLUTION[0]", "CONVOLUTION[1]"), p=0.9, ), p.Convolve(algo="CONVOL_GR4J_1", source="CONVOLUTION[0]", to="SOIL[1]"), diff --git a/src/ravenpy/config/parsers.py b/src/ravenpy/config/parsers.py index 5d47ba3d..e4c900cb 100644 --- a/src/ravenpy/config/parsers.py +++ b/src/ravenpy/config/parsers.py @@ -75,7 +75,7 @@ def parse_raven_messages(path): # The error message for an unknown command is exceptionally on two lines # (the second starts with a triple space) - for m in re.findall("^([A-Z ]+) :(.+)(?:\n (.+))?", path.read_text(), re.M): + for m in re.findall("^([A-Z ]+) :(.+)(?:\n {3}(.+))?", path.read_text(), re.M): if m[0] == "SIMULATION COMPLETE": messages["SIMULATION COMPLETE"] = True continue @@ -142,7 +142,9 @@ def parse_outputs(run_name: str, outputdir: Optional[Union[str, Path]] = None): return out -def _time_stamp_from_solution(solution: str, calendar: str) -> cftime.datetime: +def _time_stamp_from_solution( + solution: str, calendar: str +) -> Optional[cftime.datetime]: """Return datetime from solution TimeStamp.""" match = re.search(r":TimeStamp (\S+ \S+)", solution) if match: diff --git a/src/ravenpy/config/utils.py b/src/ravenpy/config/utils.py index 1296e47c..20aadd75 100644 --- a/src/ravenpy/config/utils.py +++ b/src/ravenpy/config/utils.py @@ -19,8 +19,6 @@ def nc_specs( alt_names: Optional[Union[str, Sequence[str]]] = None, mon_ave: bool = False, engine: str = "h5netcdf", - # FIXME: Is this call signature still relevant? - linear_transform=None, ): """Extract specifications from netCDF file. diff --git a/src/ravenpy/extractors/routing_product.py b/src/ravenpy/extractors/routing_product.py index 6e36a744..a163b055 100644 --- a/src/ravenpy/extractors/routing_product.py +++ b/src/ravenpy/extractors/routing_product.py @@ -2,7 +2,7 @@ import warnings from collections import defaultdict from pathlib import Path -from typing import Union +from typing import Optional, Union import pandas @@ -19,7 +19,7 @@ msg = gis_import_error_message.format(Path(__file__).stem) raise ImportError(msg) from e -import netCDF4 as nc4 # noqa: N813 +import netCDF4 import numpy as np @@ -226,7 +226,8 @@ def _extract_channel_profile(row) -> dict: # river bottom width W_btm botwd = channel_width - 2 * sidwd - # if derived bottom width is negative, set bottom width to 0.5*bankfull width and recalculate zch + # if derived bottom width is negative, set bottom width to 0.5 * bankfull width and recalculate zch + # FIXME: zch recalculation is recalculated but not used. Should this be higher up? if botwd < 0: botwd = 0.5 * channel_width sidwd = 0.5 * 0.5 * channel_width @@ -282,8 +283,7 @@ def _extract_hru_from_sb(self, row) -> dict: elif self.hru_aspect_convention == "ArcGIS": aspect = 360 - aspect else: - # FIXME: assertions should not be found outside of testing code. Replace with conditional logic. - assert False # noqa: S101 + raise ValueError(f"Unknown aspect convention: {self.hru_aspect_convention}") return dict( hru_id=int(row["SubId"]), @@ -307,8 +307,7 @@ def _extract_hru(self, row) -> dict: elif self.hru_aspect_convention == "ArcGIS": aspect = 360 - aspect else: - # FIXME: assertions should not be found outside of testing code. Replace with conditional logic. - assert False # noqa: S101 + raise ValueError(f"Unknown aspect convention: {self.hru_aspect_convention}") return dict( hru_id=int(row["HRU_ID"]), @@ -372,16 +371,14 @@ def __init__( self._sub_ids = sub_ids or [] self._area_error_threshold = area_error_threshold - # FIXME: assertions should not be found outside of testing code. Replace with conditional logic. - assert not ( # noqa: S101 - self._gauge_ids and self._sub_ids - ), "Only one of gauge_ids or sub_ids can be specified" + if self._gauge_ids and self._sub_ids: + raise ValueError("Only one of gauge_ids or sub_ids can be specified") input_file_path = Path(input_file_path) if input_file_path.suffix == ".nc": # Note that we cannot use xarray because it complains about variables and dimensions # having the same name. - self._input_data = nc4.Dataset(input_file_path) + self._input_data = netCDF4.Dataset(input_file_path) self._input_is_netcdf = True elif input_file_path.suffix in [".zip", ".shp"]: self._input_data = open_shapefile(input_file_path) @@ -628,7 +625,7 @@ def _prepare_input_data(self): def _compute_grid_cell_polygons(self): grid_cell_geom_gpd_wkt: list[list[list[ogr.Geometry]]] = [ - [[] for ilon in range(self._nlon)] for ilat in range(self._nlat) + [[] for _ilon in range(self._nlon)] for _ilat in range(self._nlat) ] if self._input_is_netcdf: @@ -713,7 +710,8 @@ def _compute_grid_cell_polygons(self): return grid_cell_geom_gpd_wkt - def _create_gridcells_from_centers(self, lat, lon): + @staticmethod + def _create_gridcells_from_centers(lat, lon): # create array of edges where (x,y) are always center cells nlon = np.shape(lon)[1] nlat = np.shape(lat)[0] @@ -756,7 +754,8 @@ def _create_gridcells_from_centers(self, lat, lon): return [lath, lonh] - def _shape_to_geometry(self, shape_from_jsonfile, epsg=None): + @staticmethod + def _shape_to_geometry(shape_from_jsonfile, epsg=None): # converts shape read from shapefile to geometry # epsg :: integer EPSG code @@ -834,13 +833,13 @@ def _check_gridcell_in_proximity_of_shape(gridcell_edges, shape_from_jsonfile): def upstream_from_id( - fid: int, df: Union[pandas.DataFrame, geopandas.GeoDataFrame] + fid: Union[str, int, float], df: Union[pandas.DataFrame, geopandas.GeoDataFrame] ) -> Union[pandas.DataFrame, geopandas.GeoDataFrame]: """Return upstream sub-basins by evaluating the downstream networks. Parameters ---------- - fid : int + fid : str or int or float feature ID of the downstream feature of interest. df : pandas.DataFrame or geopandas.GeoDataFrame A GeoDataframe comprising the watershed attributes. diff --git a/src/ravenpy/utilities/analysis.py b/src/ravenpy/utilities/analysis.py index 58c44965..1c0271ad 100644 --- a/src/ravenpy/utilities/analysis.py +++ b/src/ravenpy/utilities/analysis.py @@ -73,7 +73,7 @@ def dem_prop( ---------- dem : str or Path DEM raster in reprojected coordinates. - geom : Polygon or MultiPolygon or List[Polygon or MultiPolygon] + geom : Polygon or MultiPolygon or list of Polygon or MultiPolygon Geometry over which aggregate properties will be computed. If None compute properties over entire raster. directory : str or Path, optional Folder to save the GDAL terrain analysis outputs. @@ -127,7 +127,7 @@ def gdal_slope_analysis( Parameters ---------- dem : str or Path - Path to file storing DEM. + A path to file storing DEM. set_output : str or Path, optional If set to a valid filepath, will write to this path, otherwise will use an in-memory gdal.Dataset. units : str @@ -185,7 +185,7 @@ def gdal_aspect_analysis( Parameters ---------- dem : str or Path - Path to file storing DEM. + A path to file storing DEM. set_output : str or Path or bool If set to a valid filepath, will write to this path, otherwise will use an in-memory gdal.Dataset. flat_values_are_zero : bool diff --git a/src/ravenpy/utilities/calibration.py b/src/ravenpy/utilities/calibration.py index 2098576f..ce9c7775 100644 --- a/src/ravenpy/utilities/calibration.py +++ b/src/ravenpy/utilities/calibration.py @@ -85,7 +85,8 @@ def parameters(self): """Return a random parameter combination.""" return generate(self.pdist) - def evaluation(self): + @staticmethod + def evaluation(): """Return the observation. Since Raven computes the objective function itself, we simply return a placeholder. diff --git a/src/ravenpy/utilities/checks.py b/src/ravenpy/utilities/checks.py index 5da140b0..eddb4134 100644 --- a/src/ravenpy/utilities/checks.py +++ b/src/ravenpy/utilities/checks.py @@ -15,9 +15,9 @@ from pyproj import CRS from pyproj.exceptions import CRSError from shapely.geometry import GeometryCollection, MultiPolygon, Point, shape -except (ImportError, ModuleNotFoundError) as e: +except (ImportError, ModuleNotFoundError) as err: msg = gis_import_error_message.format(Path(__file__).stem) - raise ImportError(msg) from e + raise ImportError(msg) from err import ravenpy.utilities.io as io diff --git a/src/ravenpy/utilities/coords.py b/src/ravenpy/utilities/coords.py index 450ac269..8055d7bd 100644 --- a/src/ravenpy/utilities/coords.py +++ b/src/ravenpy/utilities/coords.py @@ -1,6 +1,7 @@ from dataclasses import fields import numpy as np +import pint import xarray as xr from ravenpy.config.emulators import get_model @@ -106,7 +107,7 @@ def infer_scale_and_offset( return scale, offset -def units_transform(source, target, context="hydro"): +def units_transform(source: Union[str, pint.Unit], target: str, context: str = "hydro"): """Return linear transform parameters to convert one unit to another. If the target unit is given by `y = ax + b`, where `x` is the value of the source unit, then this function @@ -118,7 +119,7 @@ def units_transform(source, target, context="hydro"): Source unit string, pint-recognized. target : str Target unit string, pint-recognized. - context : str, optional + context : str Context of unit conversion. Default: "hydro". """ from xclim.core.units import convert_units_to diff --git a/src/ravenpy/utilities/geo.py b/src/ravenpy/utilities/geo.py index f50ee24f..cc6df289 100644 --- a/src/ravenpy/utilities/geo.py +++ b/src/ravenpy/utilities/geo.py @@ -251,7 +251,7 @@ def generic_vector_reproject( def determine_upstream_ids( - fid: str, + fid: Union[str, int, float], df: Union[pd.DataFrame, geopandas.GeoDataFrame], *, basin_field: str, @@ -263,8 +263,8 @@ def determine_upstream_ids( Parameters ---------- - fid : str - feature ID of the downstream feature of interest. + fid : str or int or float + The feature ID of the downstream feature of interest. df : pd.DataFrame A Dataframe comprising the watershed attributes. basin_field : str diff --git a/src/ravenpy/utilities/geoserver.py b/src/ravenpy/utilities/geoserver.py index 5dd9558a..a490d198 100644 --- a/src/ravenpy/utilities/geoserver.py +++ b/src/ravenpy/utilities/geoserver.py @@ -167,7 +167,8 @@ def _get_feature_attributes_wfs( layer : str Name of geographic layer queried. geoserver : str - The address of the geoserver housing the layer to be queried. Default: https://pavics.ouranos.ca/geoserver/. + The address of the geoserver housing the layer to be queried. + Default: https://pavics.ouranos.ca/geoserver/. Returns ------- @@ -213,7 +214,8 @@ def _filter_feature_attributes_wfs( layer : str Name of geographic layer queried. geoserver : str - The address of the geoserver housing the layer to be queried. Default: https://pavics.ouranos.ca/geoserver/. + The address of the geoserver housing the layer to be queried. + Default: https://pavics.ouranos.ca/geoserver/. Returns ------- @@ -269,7 +271,8 @@ def get_raster_wcs( layer : str Layer name of raster exposed on GeoServer instance, e.g. 'public:CEC_NALCMS_LandUse_2010'. geoserver : str - The address of the geoserver housing the layer to be queried. Default: https://pavics.ouranos.ca/geoserver/. + The address of the geoserver housing the layer to be queried. + Default: https://pavics.ouranos.ca/geoserver/. Returns ------- @@ -524,7 +527,8 @@ def hydro_routing_upstream( lakes : {"1km", "all"} Query the version of dataset with lakes under 1km in width removed ("1km") or return all lakes ("all"). geoserver : str - The address of the geoserver housing the layer to be queried. Default: https://pavics.ouranos.ca/geoserver/. + The address of the geoserver housing the layer to be queried. + Default: https://pavics.ouranos.ca/geoserver/. Returns ------- @@ -571,8 +575,8 @@ def get_hydro_routing_attributes_wfs( """ Return a URL that formats and returns a remote GetFeatures request from hydro routing dataset. - For geographic rasters, subsetting is based on WGS84 (Long, Lat) boundaries. If not geographic, subsetting based - on projected coordinate system (Easting, Northing) boundaries. + For geographic rasters, subsetting is based on WGS84 (Long, Lat) boundaries. + If not geographic, subsetting based on projected coordinate system (Easting, Northing) boundaries. Parameters ---------- @@ -583,7 +587,8 @@ def get_hydro_routing_attributes_wfs( lakes : {"1km", "all"} Query the version of dataset with lakes under 1km in width removed ("1km") or return all lakes ("all"). geoserver : str - The address of the geoserver housing the layer to be queried. Default: https://pavics.ouranos.ca/geoserver/. + The address of the geoserver housing the layer to be queried. + Default: https://pavics.ouranos.ca/geoserver/. Returns ------- @@ -622,7 +627,8 @@ def filter_hydro_routing_attributes_wfs( lakes : {"1km", "all"} Query the version of dataset with lakes under 1km in width removed ("1km") or return all lakes ("all"). geoserver : str - The address of the geoserver housing the layer to be queried. Default: https://pavics.ouranos.ca/geoserver/. + The address of the geoserver housing the layer to be queried. + Default: https://pavics.ouranos.ca/geoserver/. Returns ------- @@ -661,7 +667,8 @@ def get_hydro_routing_location_wfs( level : int Level of granularity requested for the lakes vector (range(7,13)). Default: 12. geoserver : str - The address of the geoserver housing the layer to be queried. Default: https://pavics.ouranos.ca/geoserver/. + The address of the geoserver housing the layer to be queried. + Default: https://pavics.ouranos.ca/geoserver/. Returns ------- diff --git a/src/ravenpy/utilities/io.py b/src/ravenpy/utilities/io.py index 29203e9d..97a67b48 100644 --- a/src/ravenpy/utilities/io.py +++ b/src/ravenpy/utilities/io.py @@ -18,9 +18,9 @@ import rasterio from pyproj import CRS from shapely.geometry import shape -except (ImportError, ModuleNotFoundError) as e: +except (ImportError, ModuleNotFoundError) as err: msg = gis_import_error_message.format(Path(__file__).stem) - raise ImportError(msg) from e + raise ImportError(msg) from err LOGGER = logging.getLogger("RavenPy") WGS84 = 4326 @@ -97,8 +97,8 @@ def generic_extract_archive( Returns ------- - list - List of original or of extracted files. + list of str + A list of original or of extracted files. """ archive_types = [".tar", ".zip", ".7z"] output_dir = output_dir or tempfile.gettempdir() @@ -167,7 +167,7 @@ def archive_sniffer( Returns ------- list of str or Path - List of files with matching accepted extensions. + A list of files with matching accepted extensions. """ potential_files = list() @@ -189,12 +189,12 @@ def crs_sniffer( Parameters ---------- - \*args : Union[str, Path, Sequence[Union[str, Path]]] + \*args : str or Path or sequence of str or Path Path(s) to the file(s) to examine. Returns ------- - Union[List[str], str] + str or list of str Returns either a list of CRSes or a single CRS definition, depending on the number of instances found. """ crs_list = list() @@ -255,7 +255,7 @@ def raster_datatype_sniffer(file: Union[str, Path]) -> str: Parameters ---------- - file : Union[str, Path] + file : str or Path Path to file. Returns diff --git a/src/ravenpy/utilities/mk_test.py b/src/ravenpy/utilities/mk_test.py index 0a0917d9..3bf7c67d 100644 --- a/src/ravenpy/utilities/mk_test.py +++ b/src/ravenpy/utilities/mk_test.py @@ -44,7 +44,7 @@ def mk_test_calc( Notes ----- - http://vsp.pnnl.gov/help/Vsample/Design_Trend_Mann_Kendall.htm + https://vsp.pnnl.gov/help/Vsample/Design_Trend_Mann_Kendall.htm trend: tells the trend (increasing, decreasing or no trend) h: True (if trend is present) or False (if trend is absence) @@ -108,13 +108,13 @@ def check_num_samples( tol: float = 1e-6, num_cycles: int = 10000, m: int = 5, -) -> Union[int, float]: +) -> Optional[Union[int]]: """Check number of samples. This function is an implementation of the "Calculation of Number of Samples Required to Detect a Trend" section written by Sat Kumar Tomer (satkumartomer@gmail.com) which can be found at: - http://vsp.pnnl.gov/help/Vsample/Design_Trend_Mann_Kendall.htm + https://vsp.pnnl.gov/help/Vsample/Design_Trend_Mann_Kendall.htm As stated on the webpage in the URL above the method uses a Monte-Carlo simulation to determine the required number of points in time, n, to take a measurement in order to detect a linear trend for specified small @@ -128,7 +128,7 @@ def check_num_samples( Parameters ---------- beta : float - Probability of falsely accepting the null hypothesis. + Probability of falsely accepting the null hypothesis. delta : float Change per sample period, i.e., the change that occurs between two adjacent sampling times. std_dev : float @@ -145,9 +145,14 @@ def check_num_samples( Total number of cycles of the simulation. This is to ensure that the simulation does finish regardless of convergence or not (10000 default). m : int - If the tolerance is too small then the simulation could continue to cycle through the same sample numbers over and over. - This parameter determines how many cycles to look back. - If the same number of samples has been determined m cycles ago then the simulation will stop. + If the tolerance is too small then the simulation could continue to cycle through the same sample numbers over and over. + This parameter determines how many cycles to look back. + If the same number of samples has been determined m cycles ago then the simulation will stop. + + Returns + ------- + int, optional + The number of samples required to detect a trend. Examples -------- diff --git a/src/ravenpy/utilities/regionalization.py b/src/ravenpy/utilities/regionalization.py index d4ab7dcb..633546e1 100644 --- a/src/ravenpy/utilities/regionalization.py +++ b/src/ravenpy/utilities/regionalization.py @@ -35,7 +35,6 @@ def regionalize( min_NSE: float = 0.6, # noqa: N803 workdir: Optional[Union[str, Path]] = None, overwrite: bool = False, - **kwds, ) -> tuple[xr.DataArray, xr.Dataset]: r"""Perform regionalization for catchment whose outlet is defined by coordinates. @@ -50,7 +49,8 @@ def regionalize( params : pd.DataFrame Model parameters of gauged catchments. Needed for all but MRL method. props : pd.DataFrame - Properties of gauged catchments to be analyzed for the regionalization. Needed for MLR and RA methods. + Properties of gauged catchments to be analyzed for the regionalization. + Needed for MLR and RA methods. target_props : pd.Series or dict Properties of ungauged catchment. Needed for MLR and RA methods. size : int @@ -61,15 +61,13 @@ def regionalize( Work directory. If None, a temporary directory will be created. overwrite : bool If True, existing files will be overwritten. - \*\*kwds : dict - Model configuration parameters, including the forcing files (ts). Returns ------- qsim : DataArray (time, ) Multi-donor averaged predicted streamflow. ensemble : Dataset - Dataset containing the ensemble of simulations and parameters used: + A Dataset containing the ensemble of simulations and parameters used: - q_sim : DataArray (realization, time) Ensemble of members based on number of donors. @@ -291,22 +289,22 @@ def regionalization_params( Parameters ---------- method : {'MLR', 'SP', 'PS', 'SP_IDW', 'PS_IDW', 'SP_IDW_RA', 'PS_IDW_RA'} - Name of the regionalization method to use. + Name of the regionalization method to use. gauged_params : pd.DataFrame - A DataFrame of parameters for donor catchments (size = number of donors). + A DataFrame of parameters for donor catchments (size = number of donors). gauged_properties : pd.DataFrame - A DataFrame of properties of the donor catchments (size = number of donors). + A DataFrame of properties of the donor catchments (size = number of donors). ungauged_properties : pd.DataFrame - A DataFrame of properties of the ungauged catchment (size = 1). + A DataFrame of properties of the ungauged catchment (size = 1). filtered_params : pd.DataFrame - A DataFrame of parameters of all filtered catchments (size = all catchments with NSE > min_NSE). + A DataFrame of parameters of all filtered catchments (size = all catchments with NSE > min_NSE). filtered_prop : pd.DataFrame - A DataFrame of properties of all filtered catchments (size = all catchments with NSE > min_NSE). + A DataFrame of properties of all filtered catchments (size = all catchments with NSE > min_NSE). Returns ------- list - A list of model parameters to be used for the regionalization. + A list of model parameters to be used for the regionalization. """ if method == "MLR" or "RA" in method: mlr_params, r2 = multiple_linear_regression( From 1920a6f82ff35cbd01c7a52fb576cfdc65d5f528 Mon Sep 17 00:00:00 2001 From: Trevor James Smith <10819524+Zeitsperre@users.noreply.github.com> Date: Tue, 18 Mar 2025 13:23:16 -0400 Subject: [PATCH 10/15] add missing union --- src/ravenpy/utilities/coords.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/ravenpy/utilities/coords.py b/src/ravenpy/utilities/coords.py index 8055d7bd..b21bd91e 100644 --- a/src/ravenpy/utilities/coords.py +++ b/src/ravenpy/utilities/coords.py @@ -1,4 +1,5 @@ from dataclasses import fields +from typing import Union import numpy as np import pint From f90faeef2523c96ebb3fdd8dd5ba566d0687de6d Mon Sep 17 00:00:00 2001 From: Trevor James Smith <10819524+Zeitsperre@users.noreply.github.com> Date: Wed, 19 Mar 2025 11:45:19 -0400 Subject: [PATCH 11/15] update description, mark note about Python3.13 limitations --- README.rst | 2 +- environment-dev.yml | 2 +- pyproject.toml | 2 +- src/ravenpy/__init__.py | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/README.rst b/README.rst index 074274d4..069959c6 100644 --- a/README.rst +++ b/README.rst @@ -15,7 +15,7 @@ RavenPy |logo| +----------------------------+-----------------------------------------------------+ -A Python wrapper for running the hydrologic modelling framework Raven_. +A Python wrapper for configuring and running the hydrologic modelling framework Raven_. * Free software: MIT license * Documentation: https://ravenpy.readthedocs.io diff --git a/environment-dev.yml b/environment-dev.yml index 17e19161..d5f56dc6 100644 --- a/environment-dev.yml +++ b/environment-dev.yml @@ -24,7 +24,7 @@ dependencies: - pint >=0.24.4 - platformdirs >=4.3.6 - pydantic >=2.0 - - pydap >=3.4.0 + - pydap >=3.4.0 # Note: As of 2025-03-18 (v3.5.4) does not support Python 3.13 - pymetalink >=6.5.2 - pymbolic >=2024.2 - pyproj >=3.3.0 diff --git a/pyproject.toml b/pyproject.toml index 22ddf183..fc6a3f4c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -50,7 +50,7 @@ dependencies = [ "pint >=0.24.4", "platformdirs >=4.3.6", "pydantic >=2.0", - "pydap >=3.4.0", + "pydap >=3.4.0", # Note: As of 2025-03-18 (v3.5.4) does not support Python 3.13 "pymbolic >=2024.2", "raven-hydro >=0.4.0,<1.0", "scipy >=1.11.0", diff --git a/src/ravenpy/__init__.py b/src/ravenpy/__init__.py index c5d9b8ea..764b468c 100644 --- a/src/ravenpy/__init__.py +++ b/src/ravenpy/__init__.py @@ -1,4 +1,4 @@ -"""A Python wrapper for running the hydrologic modelling framework Raven.""" +"""A Python wrapper for configuring and running the hydrologic modelling framework Raven.""" ################################################################################### # MIT License From ea4c5ce19f5f0e154347e6b2f2be2e53cee07ed1 Mon Sep 17 00:00:00 2001 From: Trevor James Smith <10819524+Zeitsperre@users.noreply.github.com> Date: Wed, 19 Mar 2025 17:02:08 -0400 Subject: [PATCH 12/15] revert pydantic behaviour-affecting changes --- src/ravenpy/config/commands.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/ravenpy/config/commands.py b/src/ravenpy/config/commands.py index 11f9e229..aba675d7 100644 --- a/src/ravenpy/config/commands.py +++ b/src/ravenpy/config/commands.py @@ -166,8 +166,8 @@ class EvaluationPeriod(LineCommand): """ name: str - start: Union[str, dt.date] - end: Union[str, dt.date] + start: dt.date + end: dt.date class CustomOutput(LineCommand): @@ -427,8 +427,8 @@ class ChannelProfile(FlatCommand): name: str = "chn_XXX" bed_slope: float = 0 - survey_points: list[tuple[Union[int, float], Union[int, float]]] = () - roughness_zones: list[tuple[Union[int, float], Union[int, float]]] = () + survey_points: tuple[tuple[float, float], ...] = () + roughness_zones: tuple[tuple[float, float], ...] = () def to_rv(self): template = """ From 4831275b06388746f6e19d122dec9468474b779c Mon Sep 17 00:00:00 2001 From: Trevor James Smith <10819524+Zeitsperre@users.noreply.github.com> Date: Wed, 19 Mar 2025 17:04:16 -0400 Subject: [PATCH 13/15] update copyright year to 2025 in __init__.py and LICENSE --- LICENSE | 2 +- src/ravenpy/__init__.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/LICENSE b/LICENSE index deb78974..c735c557 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2020-2024 David Huard, Trevor James Smith, Christian Jauvin, Julie Mai, Ming Han +Copyright (c) 2020-2025 David Huard, Trevor James Smith, Christian Jauvin, Julie Mai, Ming Han Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/ravenpy/__init__.py b/src/ravenpy/__init__.py index 764b468c..fde88dc6 100644 --- a/src/ravenpy/__init__.py +++ b/src/ravenpy/__init__.py @@ -3,7 +3,7 @@ ################################################################################### # MIT License # -# Copyright (c) 2020-2024 David Huard, Trevor James Smith, Christian Jauvin, Julie Mai, Ming Han +# Copyright (c) 2020-2025 David Huard, Trevor James Smith, Christian Jauvin, Julie Mai, Ming Han # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal From ade90a47ddcf0c49e4044367fe2affcd5c2d49fc Mon Sep 17 00:00:00 2001 From: Trevor James Smith <10819524+Zeitsperre@users.noreply.github.com> Date: Fri, 21 Mar 2025 13:42:52 -0400 Subject: [PATCH 14/15] update CHANGELOG.rst --- CHANGELOG.rst | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index bd558ac0..5ca7a127 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -5,10 +5,13 @@ Changelog v0.18.0 (unreleased) -------------------- +New features +^^^^^^^^^^^^ * `ravenpy` now supports Python3.13. (PR #459) * Updated `raven-hydro` to v0.4.0 (`RavenHydroFramework` v4.0.1). (PR #459) * Updated `xclim` to v0.54.0, `pint` to v0.24.4, and `numpy` to v1.24.0 (no longer pinned below v2.0). (PR #459) * `ravenpy` is now registered with the Open Source Security Foundation (OSSF) Best Practices initiative (`RavenPy OpenSSF-BP Status `_). (PR #464) +* `ravenpy` now enables new EvaluationMetrics commands in the model configuration. Other features from `RavenHydroFramework` will be included in newer releases. (PR #476) Bug fixes ^^^^^^^^^ @@ -25,6 +28,9 @@ Internal changes * Spelling errors in documentation have been addressed. * GitHub Workflows now test `ravenpy` using macOS as well as Python3.13. (PR #459) * Several small deprecation and usage warnings as well as a few variable typing issues have been addressed. (PR #464) +* Updated the license to reflect current year. (PR #476) +* Documentation version now supports showing hyphens in the version number. (PR #476) +* Call signatures and docstrings of functions have been modified to be more precise for the expected variable type. (PR #476) v0.17.0 (2025-01-27) -------------------- From 9852720c55ea26c3a9287833c4467595c694a661 Mon Sep 17 00:00:00 2001 From: Zeitsperre <10819524+Zeitsperre@users.noreply.github.com> Date: Thu, 27 Mar 2025 14:45:34 -0400 Subject: [PATCH 15/15] use term "coverage", set allowed endpoints for Finish step --- .github/workflows/main.yml | 11 ++++++++--- tox.ini | 10 +++++----- 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index a51a1f15..6814f716 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -65,7 +65,7 @@ jobs: include: - os: 'ubuntu-latest' python-version: '3.10' - tox-env: 'py3.10-coveralls-upstream' + tox-env: 'py3.10-coverage-upstream' steps: - name: Harden Runner uses: step-security/harden-runner@4d991eb9b905ef189e4c376166672c3f2f230481 # v2.11.0 @@ -122,7 +122,7 @@ jobs: if [ "${{ matrix.tox-env }}" != "false" ]; then python3 -m tox -e ${{ matrix.tox-env }} elif [ "${{ matrix.python-version }}" != "3.13" ]; then - python3 -m tox -e py${{ matrix.python-version }}-coveralls + python3 -m tox -e py${{ matrix.python-version }}-coverage else python3 -m tox -e py${{ matrix.python-version }} fi @@ -197,6 +197,7 @@ jobs: parallel: true finish: + name: Finish needs: - pip - conda @@ -206,7 +207,11 @@ jobs: uses: step-security/harden-runner@4d991eb9b905ef189e4c376166672c3f2f230481 # v2.11.0 with: disable-sudo: true - egress-policy: audit + egress-policy: block + allowed-endpoints: > + coveralls.io:443 + github.com:443 + objects.githubusercontent.com:443 - name: Coveralls Finished uses: coverallsapp/github-action@648a8eb78e6d50909eff900e4ec85cab4524a45b # v2.3.6 with: diff --git a/tox.ini b/tox.ini index 6a6c92eb..39b66dff 100644 --- a/tox.ini +++ b/tox.ini @@ -13,9 +13,9 @@ opts = [gh] python = - 3.10 = py3.10-coveralls-upstream - 3.11 = py3.11-coveralls - 3.12 = py3.12-coveralls + 3.10 = py3.10-coverage-upstream + 3.11 = py3.11-coverage + 3.12 = py3.12-coverage 3.13 = py3.13 # coveralls not supported on 3.13 [testenv:lint] @@ -62,7 +62,7 @@ download = true install_command = python -m pip install --no-user {opts} {packages} deps = - coveralls: coveralls>=4.0.1 + coverage: coveralls>=4.0.1 ; numpy must be present in python env before GDAL is installed numpy >=1.24.0 gdal == {env:GDAL_VERSION} @@ -76,7 +76,7 @@ commands = upstream: python -m pip install --upgrade --force-reinstall --no-deps --no-cache-dir git+https://github.com/Ouranosinc/raven-hydro.git@{env:UPSTREAM_BRANCH} ; Run tests pytest {posargs} - coveralls: - coveralls + coverage: - coveralls allowlist_externals = make env