From 06bb9abb2be669191b0b5dd2f7607d8323eb4a40 Mon Sep 17 00:00:00 2001 From: "Matthew.Deshotel" Date: Tue, 14 Jul 2026 06:32:39 -0500 Subject: [PATCH] add type hints downscaling --- .../NextGen_Forcings_Engine/core/downscale.py | 846 ++++++++++-------- 1 file changed, 453 insertions(+), 393 deletions(-) diff --git a/NextGen_Forcings_Engine_BMI/NextGen_Forcings_Engine/core/downscale.py b/NextGen_Forcings_Engine_BMI/NextGen_Forcings_Engine/core/downscale.py index 4b521568..2989a86a 100755 --- a/NextGen_Forcings_Engine_BMI/NextGen_Forcings_Engine/core/downscale.py +++ b/NextGen_Forcings_Engine_BMI/NextGen_Forcings_Engine/core/downscale.py @@ -4,20 +4,42 @@ downscaling is needed, based off options specified by the user. """ +from __future__ import annotations + import math import os import time +from typing import TYPE_CHECKING, Any import numpy as np from netCDF4 import Dataset from . import err_handler +if TYPE_CHECKING: + from NextGen_Forcings_Engine_BMI.NextGen_Forcings_Engine.core.config import ( + ConfigOptions, + ) + from NextGen_Forcings_Engine_BMI.NextGen_Forcings_Engine.core.forcingInputMod import ( + InputForcings, + ) + from NextGen_Forcings_Engine_BMI.NextGen_Forcings_Engine.core.geoMod import ( + GeoMeta, + ) + from NextGen_Forcings_Engine_BMI.NextGen_Forcings_Engine.core.parallel import ( + MpiConfig, + ) + -def run_downscaling(input_forcings, config_options, geo_meta_wrf_hydro, mpi_config): +def run_downscaling( + input_forcings: InputForcings, + config_options: ConfigOptions, + geo_meta: GeoMeta, + mpi_config: MpiConfig, +): """Top level module function that will downscale forcing variables for this particular input forcing product. - :param geo_meta_wrf_hydro: + :param geo_meta: :param mpi_config: :param input_forcings: :param config_options: @@ -26,28 +48,28 @@ def run_downscaling(input_forcings, config_options, geo_meta_wrf_hydro, mpi_conf # Dictionary mapping to temperature downscaling. downscale_temperature = {0: no_downscale, 1: simple_lapse, 2: param_lapse} downscale_temperature[input_forcings.t2dDownscaleOpt]( - input_forcings, config_options, geo_meta_wrf_hydro, mpi_config + input_forcings, config_options, geo_meta, mpi_config ) err_handler.check_program_status(config_options, mpi_config) # Dictionary mapping to pressure downscaling. downscale_pressure = {0: no_downscale, 1: pressure_down_classic} downscale_pressure[input_forcings.psfcDownscaleOpt]( - input_forcings, config_options, geo_meta_wrf_hydro, mpi_config + input_forcings, config_options, geo_meta, mpi_config ) err_handler.check_program_status(config_options, mpi_config) # Dictionary mapping to shortwave radiation downscaling downscale_sw = {0: no_downscale, 1: ncar_topo_adj} downscale_sw[input_forcings.swDownscaleOpt]( - input_forcings, config_options, geo_meta_wrf_hydro, mpi_config + input_forcings, config_options, geo_meta, mpi_config ) err_handler.check_program_status(config_options, mpi_config) # Dictionary mapping to specific humidity downscaling downscale_q2 = {0: no_downscale, 1: q2_down_classic} downscale_q2[input_forcings.q2dDownscaleOpt]( - input_forcings, config_options, geo_meta_wrf_hydro, mpi_config + input_forcings, config_options, geo_meta, mpi_config ) err_handler.check_program_status(config_options, mpi_config) @@ -58,166 +80,181 @@ def run_downscaling(input_forcings, config_options, geo_meta_wrf_hydro, mpi_conf # 1: precip_mtn_mapper } downscale_precip[input_forcings.precipDownscaleOpt]( - input_forcings, config_options, geo_meta_wrf_hydro, mpi_config + input_forcings, config_options, geo_meta, mpi_config ) err_handler.check_program_status(config_options, mpi_config) -def no_downscale(input_forcings, ConfigOptions, GeoMetaWrfHydro, MpiConfig): +def no_downscale( + input_forcings: InputForcings, + config_options: ConfigOptions, + geo_meta: GeoMeta, + mpi_config: MpiConfig, +): """Pass states through without any downscaling. Generic function for passing states through without any downscaling. :param input_forcings: - :param ConfigOptions: + :param config_options: :return: """ - if ConfigOptions.grid_type == "gridded": + if config_options.grid_type == "gridded": input_forcings.final_forcings = input_forcings.final_forcings - elif ConfigOptions.grid_type == "unstructured": + elif config_options.grid_type == "unstructured": input_forcings.final_forcings = input_forcings.final_forcings input_forcings.final_forcings_elem = input_forcings.final_forcings_elem - elif ConfigOptions.grid_type == "hydrofabric": + elif config_options.grid_type == "hydrofabric": input_forcings.final_forcings = input_forcings.final_forcings -def simple_lapse(input_forcings, ConfigOptions, GeoMetaWrfHydro, MpiConfig): +def simple_lapse( + input_forcings: InputForcings, + config_options: ConfigOptions, + geo_meta: GeoMeta, + mpi_config: MpiConfig, +): """Apply a single lapse rate adjustment to modeled 2-meter temperature. Function that applies a single lapse rate adjustment to modeled 2-meter temperature by taking the difference of the native input elevation and the WRF-hydro elevation. :param inpute_forcings: - :param ConfigOptions: - :param GeoMetaWrfHydro: + :param config_options: + :param geo_meta: :return: """ - if MpiConfig.rank == 0: - ConfigOptions.statusMsg = ( + if mpi_config.rank == 0: + config_options.statusMsg = ( "Applying simple lapse rate to temperature downscaling" ) - err_handler.log_msg(ConfigOptions, MpiConfig) + err_handler.log_msg(config_options, mpi_config) # Calculate the elevation difference. if input_forcings.height is None: - ConfigOptions.errMsg = ( + config_options.errMsg = ( "Unable to perform downscaling without terrain height input" ) - err_handler.log_critical(ConfigOptions, MpiConfig) + err_handler.log_critical(config_options, mpi_config) return # Initalize missing data vars indNdv = None indNdv_elem = None - if ConfigOptions.grid_type == "gridded": - elevDiff = input_forcings.height - GeoMetaWrfHydro.height - elif ConfigOptions.grid_type == "unstructured": - elevDiff = input_forcings.height - GeoMetaWrfHydro.height - elevDiff_elem = input_forcings.height_elem - GeoMetaWrfHydro.height_elem - elif ConfigOptions.grid_type == "hydrofabric": - elevDiff = input_forcings.height - GeoMetaWrfHydro.height + if config_options.grid_type == "gridded": + elevDiff = input_forcings.height - geo_meta.height + elif config_options.grid_type == "unstructured": + elevDiff = input_forcings.height - geo_meta.height + elevDiff_elem = input_forcings.height_elem - geo_meta.height_elem + elif config_options.grid_type == "hydrofabric": + elevDiff = input_forcings.height - geo_meta.height # Assign existing, un-downscaled temperatures to a temporary placeholder, which # will be used for specific humidity downscaling. if input_forcings.q2dDownscaleOpt > 0: - if ConfigOptions.grid_type == "gridded": + if config_options.grid_type == "gridded": input_forcings.t2dTmp[:, :] = input_forcings.final_forcings[4, :, :] - elif ConfigOptions.grid_type == "unstructured": + elif config_options.grid_type == "unstructured": input_forcings.t2dTmp[:] = input_forcings.final_forcings[4, :] input_forcings.t2dTmp_elem[:] = input_forcings.final_forcings_elem[4, :] - elif ConfigOptions.grid_type == "hydrofabric": + elif config_options.grid_type == "hydrofabric": input_forcings.t2dTmp[:] = input_forcings.final_forcings[4, :] # Apply single lapse rate value to the input 2-meter # temperature values. - if ConfigOptions.grid_type == "gridded": + if config_options.grid_type == "gridded": try: - indNdv = np.where(input_forcings.final_forcings == ConfigOptions.globalNdv) + indNdv = np.where(input_forcings.final_forcings == config_options.globalNdv) except: - ConfigOptions.errMsg = "Unable to perform NDV search on input forcings" - err_handler.log_critical(ConfigOptions, MpiConfig) + config_options.errMsg = "Unable to perform NDV search on input forcings" + err_handler.log_critical(config_options, mpi_config) return try: input_forcings.final_forcings[4, :, :] = ( input_forcings.final_forcings[4, :, :] + (6.49 / 1000.0) * elevDiff ) except: - ConfigOptions.errMsg = ( + config_options.errMsg = ( "Unable to apply lapse rate to input 2-meter temperatures." ) - err_handler.log_critical(ConfigOptions, MpiConfig) + err_handler.log_critical(config_options, mpi_config) return - input_forcings.final_forcings[indNdv] = ConfigOptions.globalNdv + input_forcings.final_forcings[indNdv] = config_options.globalNdv - elif ConfigOptions.grid_type == "unstructured": + elif config_options.grid_type == "unstructured": try: - indNdv = np.where(input_forcings.final_forcings == ConfigOptions.globalNdv) + indNdv = np.where(input_forcings.final_forcings == config_options.globalNdv) except: - ConfigOptions.errMsg = "Unable to perform NDV search on input forcings" - err_handler.log_critical(ConfigOptions, MpiConfig) + config_options.errMsg = "Unable to perform NDV search on input forcings" + err_handler.log_critical(config_options, mpi_config) return try: indNdv_elem = np.where( - input_forcings.final_forcings_elem == ConfigOptions.globalNdv + input_forcings.final_forcings_elem == config_options.globalNdv ) except: - ConfigOptions.errMsg = "Unable to perform NDV search on input forcings" - err_handler.log_critical(ConfigOptions, MpiConfig) + config_options.errMsg = "Unable to perform NDV search on input forcings" + err_handler.log_critical(config_options, mpi_config) return try: input_forcings.final_forcings[4, :] = ( input_forcings.final_forcings[4, :] + (6.49 / 1000.0) * elevDiff ) except: - ConfigOptions.errMsg = ( + config_options.errMsg = ( "Unable to apply lapse rate to input 2-meter temperatures." ) - err_handler.log_critical(ConfigOptions, MpiConfig) + err_handler.log_critical(config_options, mpi_config) return - input_forcings.final_forcings[indNdv] = ConfigOptions.globalNdv + input_forcings.final_forcings[indNdv] = config_options.globalNdv try: input_forcings.final_forcings_elem[4, :] = ( input_forcings.final_forcings_elem[4, :] + (6.49 / 1000.0) * elevDiff_elem ) except: - ConfigOptions.errMsg = ( + config_options.errMsg = ( "Unable to apply lapse rate to input 2-meter temperatures." ) - err_handler.log_critical(ConfigOptions, MpiConfig) + err_handler.log_critical(config_options, mpi_config) return - input_forcings.final_forcings_elem[indNdv_elem] = ConfigOptions.globalNdv + input_forcings.final_forcings_elem[indNdv_elem] = config_options.globalNdv - elif ConfigOptions.grid_type == "hydrofabric": + elif config_options.grid_type == "hydrofabric": try: - indNdv = np.where(input_forcings.final_forcings == ConfigOptions.globalNdv) + indNdv = np.where(input_forcings.final_forcings == config_options.globalNdv) except: - ConfigOptions.errMsg = "Unable to perform NDV search on input forcings" - err_handler.log_critical(ConfigOptions, MpiConfig) + config_options.errMsg = "Unable to perform NDV search on input forcings" + err_handler.log_critical(config_options, mpi_config) return try: input_forcings.final_forcings[4, :] = ( input_forcings.final_forcings[4, :] + (6.49 / 1000.0) * elevDiff ) except: - ConfigOptions.errMsg = ( + config_options.errMsg = ( "Unable to apply lapse rate to input 2-meter temperatures." ) - err_handler.log_critical(ConfigOptions, MpiConfig) + err_handler.log_critical(config_options, mpi_config) return - input_forcings.final_forcings[indNdv] = ConfigOptions.globalNdv + input_forcings.final_forcings[indNdv] = config_options.globalNdv # Reset for memory efficiency indNdv = None indNdv_elem = None -def param_lapse(input_forcings, ConfigOptions, GeoMetaWrfHydro, MpiConfig): +def param_lapse( + input_forcings: InputForcings, + config_options: ConfigOptions, + geo_meta: GeoMeta, + mpi_config: MpiConfig, +): """Apply a single lapse rate adjustment to modeled 2-meter temperature. Function that applies a apriori lapse rate adjustment to modeled @@ -226,52 +263,52 @@ def param_lapse(input_forcings, ConfigOptions, GeoMetaWrfHydro, MpiConfig): rate grid has already been regridded to the final output WRF-Hydro grid. :param inpute_forcings: - :param ConfigOptions: - :param GeoMetaWrfHydro: + :param config_options: + :param geo_meta: :return: """ ###################### WRF-Hydro domain only functionality ###################### - if MpiConfig.rank == 0: - ConfigOptions.statusMsg = ( + if mpi_config.rank == 0: + config_options.statusMsg = ( "Applying apriori lapse rate grid to temperature downscaling" ) - err_handler.log_msg(ConfigOptions, MpiConfig) + err_handler.log_msg(config_options, mpi_config) # Calculate the elevation difference. if input_forcings.height is None: - ConfigOptions.errMsg = ( + config_options.errMsg = ( "Unable to perform downscaling without terrain height input" ) - err_handler.log_critical(ConfigOptions, MpiConfig) + err_handler.log_critical(config_options, mpi_config) return - elevDiff = input_forcings.height - GeoMetaWrfHydro.height + elevDiff = input_forcings.height - geo_meta.height if input_forcings.lapseGrid is None: # if not np.any(input_forcings.lapseGrid): # We have not read in our lapse rate file. Read it in, do extensive checks, # scatter the lapse rate grid out to individual processors, then apply the # lapse rate to the 2-meter temperature grid. - if MpiConfig.rank == 0: + if mpi_config.rank == 0: while True: # First ensure we have a parameter directory if input_forcings.paramDir == "NONE": - ConfigOptions.errMsg = ( + config_options.errMsg = ( "User has specified spatial temperature lapse rate " "downscaling while no downscaling parameter directory " "exists." ) - err_handler.log_critical(ConfigOptions, MpiConfig) + err_handler.log_critical(config_options, mpi_config) break # Compose the path to the lapse rate grid file. lapsePath = input_forcings.paramDir + "/lapse_param.nc" if not os.path.isfile(lapsePath): - ConfigOptions.errMsg = ( + config_options.errMsg = ( "Expected lapse rate parameter file: " + lapsePath + " does not exist." ) - err_handler.log_critical(ConfigOptions, MpiConfig) + err_handler.log_critical(config_options, mpi_config) break # Open the lapse rate file. Check for the expected variable, along with @@ -279,118 +316,120 @@ def param_lapse(input_forcings, ConfigOptions, GeoMetaWrfHydro, MpiConfig): try: idTmp = Dataset(lapsePath, "r") except: - ConfigOptions.errMsg = "Unable to open parameter file: " + lapsePath - err_handler.log_critical(ConfigOptions, MpiConfig) + config_options.errMsg = ( + "Unable to open parameter file: " + lapsePath + ) + err_handler.log_critical(config_options, mpi_config) break if not "lapse" in idTmp.variables.keys(): - ConfigOptions.errMsg = ( + config_options.errMsg = ( "Expected 'lapse' variable not located in parameter " "file: " + lapsePath ) - err_handler.log_critical(ConfigOptions, MpiConfig) + err_handler.log_critical(config_options, mpi_config) break try: lapseTmp = idTmp.variables["lapse"][:, :] except: - ConfigOptions.errMsg = ( + config_options.errMsg = ( "Unable to extracte 'lapse' variable from parameter: " "file: " + lapsePath ) - err_handler.log_critical(ConfigOptions, MpiConfig) + err_handler.log_critical(config_options, mpi_config) break # Check dimensions to ensure they match up to the output grid. - if lapseTmp.shape[1] != GeoMetaWrfHydro.nx_global: - ConfigOptions.errMsg = ( + if lapseTmp.shape[1] != geo_meta.nx_global: + config_options.errMsg = ( "X-Dimension size mismatch between output grid and lapse " "rate from parameter file: " + lapsePath ) - err_handler.log_critical(ConfigOptions, MpiConfig) + err_handler.log_critical(config_options, mpi_config) break - if lapseTmp.shape[0] != GeoMetaWrfHydro.ny_global: - ConfigOptions.errMsg = ( + if lapseTmp.shape[0] != geo_meta.ny_global: + config_options.errMsg = ( "Y-Dimension size mismatch between output grid and lapse " "rate from parameter file: " + lapsePath ) - err_handler.log_critical(ConfigOptions, MpiConfig) + err_handler.log_critical(config_options, mpi_config) break # Perform a quick search to ensure we don't have radical values. indTmp = np.where(lapseTmp < -10.0) if len(indTmp[0]) > 0: - ConfigOptions.errMsg = ( + config_options.errMsg = ( "Found anomolous negative values in the lapse rate grid from " "parameter file: " + lapsePath ) - err_handler.log_critical(ConfigOptions, MpiConfig) + err_handler.log_critical(config_options, mpi_config) break indTmp = np.where(lapseTmp > 100.0) if len(indTmp[0]) > 0: - ConfigOptions.errMsg = ( + config_options.errMsg = ( "Found excessively high values in the lapse rate grid from " "parameter file: " + lapsePath ) - err_handler.log_critical(ConfigOptions, MpiConfig) + err_handler.log_critical(config_options, mpi_config) break # Close the parameter lapse rate file. try: idTmp.close() except: - ConfigOptions.errMsg = ( + config_options.errMsg = ( "Unable to close parameter file: " + lapsePath ) - err_handler.log_critical(ConfigOptions, MpiConfig) + err_handler.log_critical(config_options, mpi_config) break break else: lapseTmp = None - err_handler.check_program_status(ConfigOptions, MpiConfig) + err_handler.check_program_status(config_options, mpi_config) # Scatter the lapse rate grid to the other processors. - input_forcings.lapseGrid = MpiConfig.scatter_array( - GeoMetaWrfHydro, lapseTmp, ConfigOptions + input_forcings.lapseGrid = mpi_config.scatter_array( + geo_meta, lapseTmp, config_options ) - err_handler.check_program_status(ConfigOptions, MpiConfig) + err_handler.check_program_status(config_options, mpi_config) # Apply the local lapse rate grid to our local slab of 2-meter temperature data. temperature_grid_tmp = input_forcings.final_forcings[4, :, :] try: - indNdv = np.where(input_forcings.final_forcings == ConfigOptions.globalNdv) + indNdv = np.where(input_forcings.final_forcings == config_options.globalNdv) except: - ConfigOptions.errMsg = ( + config_options.errMsg = ( "Unable to perform NDV search on input " + input_forcings.product_name + " regridded forcings." ) - err_handler.log_critical(ConfigOptions, MpiConfig) + err_handler.log_critical(config_options, mpi_config) return try: - indValid = np.where(temperature_grid_tmp != ConfigOptions.globalNdv) + indValid = np.where(temperature_grid_tmp != config_options.globalNdv) except: - ConfigOptions.errMsg = ( + config_options.errMsg = ( "Unable to perform search for valid values on input " + input_forcings.product_name + " regridded temperature forcings." ) - err_handler.log_critical(ConfigOptions, MpiConfig) + err_handler.log_critical(config_options, mpi_config) return try: temperature_grid_tmp[indValid] = temperature_grid_tmp[indValid] + ( (input_forcings.lapseGrid[indValid] / 1000.0) * elevDiff[indValid] ) except: - ConfigOptions.errMsg = ( + config_options.errMsg = ( "Unable to apply spatial lapse rate values to input " + input_forcings.product_name + " regridded temperature forcings." ) - err_handler.log_critical(ConfigOptions, MpiConfig) + err_handler.log_critical(config_options, mpi_config) return input_forcings.final_forcings[4, :, :] = temperature_grid_tmp - input_forcings.final_forcings[indNdv] = ConfigOptions.globalNdv + input_forcings.final_forcings[indNdv] = config_options.globalNdv # Reset for memory efficiency indTmp = None @@ -400,58 +439,63 @@ def param_lapse(input_forcings, ConfigOptions, GeoMetaWrfHydro, MpiConfig): temperature_grid_tmp = None -def pressure_down_classic(input_forcings, ConfigOptions, GeoMetaWrfHydro, MpiConfig): +def pressure_down_classic( + input_forcings: InputForcings, + config_options: ConfigOptions, + geo_meta: GeoMeta, + mpi_config: MpiConfig, +): """Apply a single lapse rate adjustment to modeled surface pressure. Generic function to downscale surface pressure to the WRF-Hydro domain. :param input_forcings: - :param ConfigOptions: - :param GeoMetaWrfHydro: + :param config_options: + :param geo_meta: :return: """ - if MpiConfig.rank == 0: - ConfigOptions.statusMsg = ( + if mpi_config.rank == 0: + config_options.statusMsg = ( "Performing topographic adjustment to surface pressure." ) - err_handler.log_msg(ConfigOptions, MpiConfig) + err_handler.log_msg(config_options, mpi_config) # Calculate the elevation difference. if input_forcings.height is None: - ConfigOptions.errMsg = ( + config_options.errMsg = ( "Unable to perform downscaling without terrain height input" ) - err_handler.log_critical(ConfigOptions, MpiConfig) + err_handler.log_critical(config_options, mpi_config) return # Initalize missing data vars indNdv = None indNdv_elem = None - if ConfigOptions.grid_type == "gridded": - elevDiff = input_forcings.height - GeoMetaWrfHydro.height - elif ConfigOptions.grid_type == "unstructured": - elevDiff = input_forcings.height - GeoMetaWrfHydro.height - elevDiff_elem = input_forcings.height_elem - GeoMetaWrfHydro.height_elem - elif ConfigOptions.grid_type == "hydrofabric": - elevDiff = input_forcings.height - GeoMetaWrfHydro.height + if config_options.grid_type == "gridded": + elevDiff = input_forcings.height - geo_meta.height + elif config_options.grid_type == "unstructured": + elevDiff = input_forcings.height - geo_meta.height + elevDiff_elem = input_forcings.height_elem - geo_meta.height_elem + elif config_options.grid_type == "hydrofabric": + elevDiff = input_forcings.height - geo_meta.height # Assign existing, un-downscaled pressure values to a temporary placeholder, which # will be used for specific humidity downscaling. if input_forcings.q2dDownscaleOpt > 0: - if ConfigOptions.grid_type == "gridded": + if config_options.grid_type == "gridded": input_forcings.psfcTmp[:, :] = input_forcings.final_forcings[6, :, :] - elif ConfigOptions.grid_type == "unstructured": + elif config_options.grid_type == "unstructured": input_forcings.psfcTmp[:] = input_forcings.final_forcings[6, :] input_forcings.psfcTmp_elem[:] = input_forcings.final_forcings_elem[6, :] - elif ConfigOptions.grid_type == "hydrofabric": + elif config_options.grid_type == "hydrofabric": input_forcings.psfcTmp[:] = input_forcings.final_forcings[6, :] - if ConfigOptions.grid_type == "gridded": + if config_options.grid_type == "gridded": try: - indNdv = np.where(input_forcings.final_forcings == ConfigOptions.globalNdv) + indNdv = np.where(input_forcings.final_forcings == config_options.globalNdv) except: - ConfigOptions.errMsg = "Unable to perform NDV search on input forcings" - err_handler.log_critical(ConfigOptions, MpiConfig) + config_options.errMsg = "Unable to perform NDV search on input forcings" + err_handler.log_critical(config_options, mpi_config) return try: input_forcings.final_forcings[6, :, :] = input_forcings.final_forcings[ @@ -460,28 +504,28 @@ def pressure_down_classic(input_forcings, ConfigOptions, GeoMetaWrfHydro, MpiCon input_forcings.final_forcings[4, :, :] * 287.05 ) except: - ConfigOptions.errMsg = ( + config_options.errMsg = ( "Unable to downscale surface pressure to input forcings." ) - err_handler.log_critical(ConfigOptions, MpiConfig) + err_handler.log_critical(config_options, mpi_config) return - input_forcings.final_forcings[indNdv] = ConfigOptions.globalNdv + input_forcings.final_forcings[indNdv] = config_options.globalNdv - elif ConfigOptions.grid_type == "unstructured": + elif config_options.grid_type == "unstructured": try: - indNdv = np.where(input_forcings.final_forcings == ConfigOptions.globalNdv) + indNdv = np.where(input_forcings.final_forcings == config_options.globalNdv) except: - ConfigOptions.errMsg = "Unable to perform NDV search on input forcings" - err_handler.log_critical(ConfigOptions, MpiConfig) + config_options.errMsg = "Unable to perform NDV search on input forcings" + err_handler.log_critical(config_options, mpi_config) return try: indNdv_elem = np.where( - input_forcings.final_forcings_elem == ConfigOptions.globalNdv + input_forcings.final_forcings_elem == config_options.globalNdv ) except: - ConfigOptions.errMsg = "Unable to perform NDV search on input forcings" - err_handler.log_critical(ConfigOptions, MpiConfig) + config_options.errMsg = "Unable to perform NDV search on input forcings" + err_handler.log_critical(config_options, mpi_config) return try: input_forcings.final_forcings[6, :] = input_forcings.final_forcings[ @@ -490,13 +534,13 @@ def pressure_down_classic(input_forcings, ConfigOptions, GeoMetaWrfHydro, MpiCon input_forcings.final_forcings[4, :] * 287.05 ) except: - ConfigOptions.errMsg = ( + config_options.errMsg = ( "Unable to downscale surface pressure to input forcings." ) - err_handler.log_critical(ConfigOptions, MpiConfig) + err_handler.log_critical(config_options, mpi_config) return - input_forcings.final_forcings[indNdv] = ConfigOptions.globalNdv + input_forcings.final_forcings[indNdv] = config_options.globalNdv try: input_forcings.final_forcings_elem[6, :] = ( @@ -505,19 +549,19 @@ def pressure_down_classic(input_forcings, ConfigOptions, GeoMetaWrfHydro, MpiCon / (input_forcings.final_forcings_elem[4, :] * 287.05) ) except: - ConfigOptions.errMsg = ( + config_options.errMsg = ( "Unable to downscale surface pressure to input forcings." ) - err_handler.log_critical(ConfigOptions, MpiConfig) + err_handler.log_critical(config_options, mpi_config) return - input_forcings.final_forcings_elem[indNdv_elem] = ConfigOptions.globalNdv - elif ConfigOptions.grid_type == "hydrofabric": + input_forcings.final_forcings_elem[indNdv_elem] = config_options.globalNdv + elif config_options.grid_type == "hydrofabric": try: - indNdv = np.where(input_forcings.final_forcings == ConfigOptions.globalNdv) + indNdv = np.where(input_forcings.final_forcings == config_options.globalNdv) except: - ConfigOptions.errMsg = "Unable to perform NDV search on input forcings" - err_handler.log_critical(ConfigOptions, MpiConfig) + config_options.errMsg = "Unable to perform NDV search on input forcings" + err_handler.log_critical(config_options, mpi_config) return try: input_forcings.final_forcings[6, :] = input_forcings.final_forcings[ @@ -526,119 +570,124 @@ def pressure_down_classic(input_forcings, ConfigOptions, GeoMetaWrfHydro, MpiCon input_forcings.final_forcings[4, :] * 287.05 ) except: - ConfigOptions.errMsg = ( + config_options.errMsg = ( "Unable to downscale surface pressure to input forcings." ) - err_handler.log_critical(ConfigOptions, MpiConfig) + err_handler.log_critical(config_options, mpi_config) return - input_forcings.final_forcings[indNdv] = ConfigOptions.globalNdv + input_forcings.final_forcings[indNdv] = config_options.globalNdv # Reset for memory efficiency indNdv = None indNdv_elem = None -def q2_down_classic(input_forcings, ConfigOptions, GeoMetaWrfHydro, MpiConfig): +def q2_down_classic( + input_forcings: InputForcings, + config_options: ConfigOptions, + geo_meta: GeoMeta, + mpi_config: MpiConfig, +): """Apply a single lapse rate adjustment to modeled 2-meter specific humidity. NCAR function for downscaling 2-meter specific humidity using already downscaled 2-meter temperature, unadjusted surface pressure, and downscaled surface pressure. :param input_forcings: - :param ConfigOptions: - :param GeoMetaWrfHydro: + :param config_options: + :param geo_meta: :return: """ - if MpiConfig.rank == 0: - ConfigOptions.statusMsg = ( + if mpi_config.rank == 0: + config_options.statusMsg = ( "Performing topographic adjustment to specific humidity." ) - err_handler.log_msg(ConfigOptions, MpiConfig) + err_handler.log_msg(config_options, mpi_config) - if ConfigOptions.grid_type != "unstructured": + if config_options.grid_type != "unstructured": # Establish where we have missing values. try: - indNdv = np.where(input_forcings.final_forcings == ConfigOptions.globalNdv) + indNdv = np.where(input_forcings.final_forcings == config_options.globalNdv) except: - ConfigOptions.errMsg = "Unable to perform NDV search on input forcings" - err_handler.log_critical(ConfigOptions, MpiConfig) + config_options.errMsg = "Unable to perform NDV search on input forcings" + err_handler.log_critical(config_options, mpi_config) return # First calculate relative humidity given original surface pressure and 2-meter # temperature try: - relHum = rel_hum(input_forcings, ConfigOptions) + relHum = rel_hum(input_forcings, config_options) except: - ConfigOptions.errMsg = ( + config_options.errMsg = ( "Unable to perform topographic downscaling of incoming " "specific humidity to relative humidity" ) - err_handler.log_critical(ConfigOptions, MpiConfig) + err_handler.log_critical(config_options, mpi_config) return # Downscale 2-meter specific humidity try: - q2Tmp = mixhum_ptrh(input_forcings, relHum, 2, ConfigOptions) + q2Tmp = mixhum_ptrh(input_forcings, relHum, 2, config_options) except: - ConfigOptions.errMsg = ( + config_options.errMsg = ( "Unable to perform topographic downscaling of " "incoming specific humidity" ) - err_handler.log_critical(ConfigOptions, MpiConfig) + err_handler.log_critical(config_options, mpi_config) return - if ConfigOptions.grid_type == "gridded": + if config_options.grid_type == "gridded": input_forcings.final_forcings[5, :, :] = q2Tmp else: input_forcings.final_forcings[5, :] = q2Tmp - input_forcings.final_forcings[indNdv] = ConfigOptions.globalNdv + input_forcings.final_forcings[indNdv] = config_options.globalNdv q2Tmp = None - elif ConfigOptions.grid_type == "unstructured": + elif config_options.grid_type == "unstructured": # Establish where we have missing values. try: - indNdv = np.where(input_forcings.final_forcings == ConfigOptions.globalNdv) + indNdv = np.where(input_forcings.final_forcings == config_options.globalNdv) except: - ConfigOptions.errMsg = "Unable to perform NDV search on input forcings" - err_handler.log_critical(ConfigOptions, MpiConfig) + config_options.errMsg = "Unable to perform NDV search on input forcings" + err_handler.log_critical(config_options, mpi_config) return # Establish where we have missing values. try: indNdv_elem = np.where( - input_forcings.final_forcings_elem == ConfigOptions.globalNdv + input_forcings.final_forcings_elem == config_options.globalNdv ) except: - ConfigOptions.errMsg = "Unable to perform NDV search on input forcings" - err_handler.log_critical(ConfigOptions, MpiConfig) + config_options.errMsg = "Unable to perform NDV search on input forcings" + err_handler.log_critical(config_options, mpi_config) return # First calculate relative humidity given original surface pressure and 2-meter # temperature try: - relHum, relHum_elem = rel_hum(input_forcings, ConfigOptions) + relHum, relHum_elem = rel_hum(input_forcings, config_options) except: - ConfigOptions.errMsg = ( + config_options.errMsg = ( "Unable to perform topographic downscaling of incoming " "specific humidity to relative humidity" ) - err_handler.log_critical(ConfigOptions, MpiConfig) + err_handler.log_critical(config_options, mpi_config) return # Downscale 2-meter specific humidity try: q2Tmp, q2Tmp_elem = mixhum_ptrh_unstructured( - input_forcings, relHum, relHum_elem, 2, ConfigOptions + input_forcings, relHum, relHum_elem, 2, config_options ) except: - ConfigOptions.errMsg = ( + config_options.errMsg = ( "Unable to perform topographic downscaling of " "incoming specific humidity" ) - err_handler.log_critical(ConfigOptions, MpiConfig) + err_handler.log_critical(config_options, mpi_config) return input_forcings.final_forcings[5, :] = q2Tmp input_forcings.final_forcings_elem[5, :] = q2Tmp_elem - input_forcings.final_forcings[indNdv] = ConfigOptions.globalNdv - input_forcings.final_forcings_elem[indNdv_elem] = ConfigOptions.globalNdv + input_forcings.final_forcings[indNdv] = config_options.globalNdv + input_forcings.final_forcings_elem[indNdv_elem] = config_options.globalNdv q2Tmp = None indNdv = None q2Tmp_elem = None @@ -646,24 +695,27 @@ def q2_down_classic(input_forcings, ConfigOptions, GeoMetaWrfHydro, MpiConfig): def nwm_monthly_PRISM_downscale( - input_forcings, ConfigOptions, GeoMetaWrfHydro, MpiConfig + input_forcings: InputForcings, + config_options: ConfigOptions, + geo_meta: GeoMeta, + mpi_config: MpiConfig, ): """Apply a single lapse rate adjustment to modeled precipitation. NCAR/OWP function for downscaling precipitation using monthly PRISM climatology in a mountain-mapper like fashion. :param input_forcings: - :param ConfigOptions: - :param GeoMetaWrfHydro: + :param config_options: + :param geo_meta: :return: """ ############################### WRF-Hydro domain only method ################################ - if MpiConfig.rank == 0: - ConfigOptions.statusMsg = ( + if mpi_config.rank == 0: + config_options.statusMsg = ( "Performing NWM Monthly PRISM Mountain Mapper Downscaling of Precipitation" ) - err_handler.log_msg(ConfigOptions, MpiConfig) + err_handler.log_msg(config_options, mpi_config) # Establish whether or not we need to read in new PRISM monthly climatology: # 1.) This is the first output timestep, and no grids have been initialized. @@ -675,8 +727,8 @@ def nwm_monthly_PRISM_downscale( if input_forcings.keyValue == 3: keyValueStr = "GFS" if mmVersion == None: - ConfigOptions.errMsg = "Invalid Mountain Mapper Precip Downscaling option\n" - err_handler.log_critical(ConfigOptions, MpiConfig) + config_options.errMsg = "Invalid Mountain Mapper Precip Downscaling option\n" + err_handler.log_critical(config_options, mpi_config) if ( input_forcings.nwmPRISM_denGrid is None @@ -685,7 +737,10 @@ def nwm_monthly_PRISM_downscale( # We are on situation 1 - This is the first output step. initialize_flag = True # LOG.debug('WE NEED TO READ IN PRISM GRIDS') - if ConfigOptions.current_output_date.month != ConfigOptions.prev_output_date.month: + if ( + config_options.current_output_date.month + != config_options.prev_output_date.month + ): # We are on situation #2 - The month has changed so we need to reinitialize the # PRISM grids. initialize_flag = True @@ -701,13 +756,13 @@ def nwm_monthly_PRISM_downscale( numeratorPath = ( input_forcings.paramDir + "/PRISM_Precip_Clim_" - + ConfigOptions.current_output_date.strftime("%b") + + config_options.current_output_date.strftime("%b") + "_NWM_Grid.nc" ) denominatorPath = ( input_forcings.paramDir + "/PRISM_Precip_Clim_" - + ConfigOptions.current_output_date.strftime("%b") + + config_options.current_output_date.strftime("%b") + "_NWM_to_" + str(keyValueStr) + "_Grid.nc" @@ -718,13 +773,13 @@ def nwm_monthly_PRISM_downscale( numeratorPath = ( input_forcings.paramDir + "/PRISM_Precip_Clim_" - + ConfigOptions.current_output_date.strftime("%b") + + config_options.current_output_date.strftime("%b") + "_NWM_Grid.nc" ) denominatorPath = ( input_forcings.paramDir + "/PRISM_Precip_Clim_" - + ConfigOptions.current_output_date.strftime("%b") + + config_options.current_output_date.strftime("%b") + "_" + str(keyValueStr) + "_to_NWM_Grid.nc" @@ -732,184 +787,182 @@ def nwm_monthly_PRISM_downscale( # Make sure files exist. if not os.path.isfile(numeratorPath): - ConfigOptions.errMsg = ( + config_options.errMsg = ( "Expected parameter file: " + numeratorPath + " for mountain mapper downscaling of precipitation not found." ) - err_handler.log_critical(ConfigOptions, MpiConfig) + err_handler.log_critical(config_options, mpi_config) break if not os.path.isfile(denominatorPath): - ConfigOptions.errMsg = ( + config_options.errMsg = ( "Expected parameter file: " + denominatorPath + " for mountain mapper downscaling of precipitation not found." ) - err_handler.log_critical(ConfigOptions, MpiConfig) + err_handler.log_critical(config_options, mpi_config) break - if MpiConfig.rank == 0: + if mpi_config.rank == 0: # Open the NetCDF parameter files. Check to make sure expected dimension # sizes are in place, along with variable names, etc. try: idNum = Dataset(numeratorPath, "r") except: - ConfigOptions.errMsg = ( + config_options.errMsg = ( "Unable to open parameter file: " + numeratorPath ) - err_handler.log_critical(ConfigOptions, MpiConfig) + err_handler.log_critical(config_options, mpi_config) break try: idDenom = Dataset(denominatorPath, "r") except: - ConfigOptions.errMsg = ( + config_options.errMsg = ( "Unable to open parameter file: " + denominatorPath ) - err_handler.log_critical(ConfigOptions, MpiConfig) + err_handler.log_critical(config_options, mpi_config) break # Check to make sure expected names, dimension sizes are present. if "x" not in idNum.variables.keys(): - ConfigOptions.errMsg = ( + config_options.errMsg = ( "Expected 'x' variable not found in parameter file: " + numeratorPath ) - err_handler.log_critical(ConfigOptions, MpiConfig) + err_handler.log_critical(config_options, mpi_config) break if "x" not in idDenom.variables.keys(): - ConfigOptions.errMsg = ( + config_options.errMsg = ( "Expected 'x' variable not found in parameter file: " + denominatorPath ) - err_handler.log_critical(ConfigOptions, MpiConfig) + err_handler.log_critical(config_options, mpi_config) break if "y" not in idNum.variables.keys(): - ConfigOptions.errMsg = ( + config_options.errMsg = ( "Expected 'y' variable not found in parameter file: " + numeratorPath ) - err_handler.log_critical(ConfigOptions, MpiConfig) + err_handler.log_critical(config_options, mpi_config) break if "y" not in idDenom.variables.keys(): - ConfigOptions.errMsg = ( + config_options.errMsg = ( "Expected 'y' variable not found in parameter file: " + denominatorPath ) - err_handler.log_critical(ConfigOptions, MpiConfig) + err_handler.log_critical(config_options, mpi_config) break if "Data" not in idNum.variables.keys(): - ConfigOptions.errMsg = ( + config_options.errMsg = ( "Expected 'Data' variable not found in parameter file: " + numeratorPath ) - err_handler.log_critical(ConfigOptions, MpiConfig) + err_handler.log_critical(config_options, mpi_config) break if "Data" not in idDenom.variables.keys(): - ConfigOptions.errMsg = ( + config_options.errMsg = ( "Expected 'Data' variable not found in parameter file: " + denominatorPath ) - err_handler.log_critical(ConfigOptions, MpiConfig) + err_handler.log_critical(config_options, mpi_config) break - if idNum.variables["Data"].shape[0] != GeoMetaWrfHydro.ny_global: - ConfigOptions.errMsg = ( + if idNum.variables["Data"].shape[0] != geo_meta.ny_global: + config_options.errMsg = ( "Input Y dimension for: " + numeratorPath + " does not match the output WRF-Hydro Y dimension size." ) - err_handler.log_critical(ConfigOptions, MpiConfig) + err_handler.log_critical(config_options, mpi_config) break - if idDenom.variables["Data"].shape[0] != GeoMetaWrfHydro.ny_global: - ConfigOptions.errMsg = ( + if idDenom.variables["Data"].shape[0] != geo_meta.ny_global: + config_options.errMsg = ( "Input Y dimension for: " + denominatorPath + " does not match the output WRF-Hydro Y dimension size." ) - err_handler.log_critical(ConfigOptions, MpiConfig) + err_handler.log_critical(config_options, mpi_config) break - if idNum.variables["Data"].shape[1] != GeoMetaWrfHydro.nx_global: - ConfigOptions.errMsg = ( + if idNum.variables["Data"].shape[1] != geo_meta.nx_global: + config_options.errMsg = ( "Input X dimension for: " + numeratorPath + " does not match the output WRF-Hydro X dimension size." ) - err_handler.log_critical(ConfigOptions, MpiConfig) + err_handler.log_critical(config_options, mpi_config) break - if idDenom.variables["Data"].shape[1] != GeoMetaWrfHydro.nx_global: - ConfigOptions.errMsg = ( + if idDenom.variables["Data"].shape[1] != geo_meta.nx_global: + config_options.errMsg = ( "Input X dimension for: " + denominatorPath + " does not match the output WRF-Hydro X dimension size." ) - err_handler.log_critical(ConfigOptions, MpiConfig) + err_handler.log_critical(config_options, mpi_config) break # Read in the PRISM grid on the output grid. Then scatter the array out to the processors. try: numDataTmp = idNum.variables["Data"][:, :] except: - ConfigOptions.errMsg = ( + config_options.errMsg = ( "Unable to extract 'Data' from parameter file: " + numeratorPath ) - err_handler.log_critical(ConfigOptions, MpiConfig) + err_handler.log_critical(config_options, mpi_config) break try: denDataTmp = idDenom.variables["Data"][:, :] except: - ConfigOptions.errMsg = ( + config_options.errMsg = ( "Unable to extract 'Data' from parameter file: " + denominatorPath ) - err_handler.log_critical(ConfigOptions, MpiConfig) + err_handler.log_critical(config_options, mpi_config) break # Close the parameter files. try: idNum.close() except: - ConfigOptions.errMsg = ( + config_options.errMsg = ( "Unable to close parameter file: " + numeratorPath ) - err_handler.log_critical(ConfigOptions, MpiConfig) + err_handler.log_critical(config_options, mpi_config) break try: idDenom.close() except: - ConfigOptions.errMsg = ( + config_options.errMsg = ( "Unable to close parameter file: " + denominatorPath ) - err_handler.log_critical(ConfigOptions, MpiConfig) + err_handler.log_critical(config_options, mpi_config) break else: numDataTmp = None denDataTmp = None break - err_handler.check_program_status(ConfigOptions, MpiConfig) + err_handler.check_program_status(config_options, mpi_config) # Scatter the array out to the local processors - input_forcings.nwmPRISM_numGrid = MpiConfig.scatter_array( - GeoMetaWrfHydro, numDataTmp, ConfigOptions + input_forcings.nwmPRISM_numGrid = mpi_config.scatter_array( + geo_meta, numDataTmp, config_options ) - err_handler.check_program_status(ConfigOptions, MpiConfig) + err_handler.check_program_status(config_options, mpi_config) - input_forcings.nwmPRISM_denGrid = MpiConfig.scatter_array( - GeoMetaWrfHydro, denDataTmp, ConfigOptions + input_forcings.nwmPRISM_denGrid = mpi_config.scatter_array( + geo_meta, denDataTmp, config_options ) - err_handler.check_program_status(ConfigOptions, MpiConfig) + err_handler.check_program_status(config_options, mpi_config) # Create temporary grids from the local slabs of params/precip forcings. hourlyGrid = input_forcings.final_forcings[3, :, :] - tmpGrid = np.full( - [GeoMetaWrfHydro.ny_local, GeoMetaWrfHydro.nx_local], -9999.0, dtype=float - ) + tmpGrid = np.full([geo_meta.ny_local, geo_meta.nx_local], -9999.0, dtype=float) ratioRainGrid = np.full( - [GeoMetaWrfHydro.ny_local, GeoMetaWrfHydro.nx_local], -9999.0, dtype=float + [geo_meta.ny_local, geo_meta.nx_local], -9999.0, dtype=float ) localRainRate = input_forcings.final_forcings[3, :, :] @@ -922,40 +975,40 @@ def nwm_monthly_PRISM_downscale( (localRainRate != -9999.0) & (denLocal != -9999.0) & (denLocal > 1.0) ) except: - ConfigOptions.errMsg = ( + config_options.errMsg = ( "Unable to run numpy search for valid values on precip and " "param grid in mountain mapper downscaling" ) - err_handler.log_critical(ConfigOptions, MpiConfig) - err_handler.check_program_status(ConfigOptions, MpiConfig) + err_handler.log_critical(config_options, mpi_config) + err_handler.check_program_status(config_options, mpi_config) try: tmpGrid[indValid] = localRainRate[indValid] / denLocal[indValid] except: - ConfigOptions.errMsg = ( + config_options.errMsg = ( "Unable to divide precip by denominator in mountain mapper downscaling" ) - err_handler.log_critical(ConfigOptions, MpiConfig) - err_handler.check_program_status(ConfigOptions, MpiConfig) + err_handler.log_critical(config_options, mpi_config) + err_handler.check_program_status(config_options, mpi_config) # Establish index of where we have valid data. try: indValid = np.where((tmpGrid != -9999.0) & (numLocal != -9999.0)) except: - ConfigOptions.errMsg = ( + config_options.errMsg = ( "Unable to run numpy search for valid values on precip and " "param grid in mountain mapper downscaling" ) - err_handler.log_critical(ConfigOptions, MpiConfig) - err_handler.check_program_status(ConfigOptions, MpiConfig) + err_handler.log_critical(config_options, mpi_config) + err_handler.check_program_status(config_options, mpi_config) try: ratioRainGrid[indValid] = tmpGrid[indValid] * numLocal[indValid] except: - ConfigOptions.errMsg = ( + config_options.errMsg = ( "Unable to multiply precip by numerator in mountain mapper downscaling" ) - err_handler.log_critical(ConfigOptions, MpiConfig) - err_handler.check_program_status(ConfigOptions, MpiConfig) + err_handler.log_critical(config_options, mpi_config) + err_handler.check_program_status(config_options, mpi_config) count = 0 @@ -964,12 +1017,12 @@ def nwm_monthly_PRISM_downscale( (ratioRainGrid == -9999.0) & (numLocal != -9999.0) & (hourlyGrid != -9999.0) ) except: - ConfigOptions.errMsg = ( + config_options.errMsg = ( "Unable to run numpy search for valid values on precip and " "param grid in mountain mapper downscaling" ) - err_handler.log_critical(ConfigOptions, MpiConfig) - err_handler.check_program_status(ConfigOptions, MpiConfig) + err_handler.log_critical(config_options, mpi_config) + err_handler.check_program_status(config_options, mpi_config) count = len(indValid[0]) if count > 0: @@ -979,23 +1032,23 @@ def nwm_monthly_PRISM_downscale( indValid = np.where(ratioRainGrid != -9999.0) except: - ConfigOptions.errMsg = ( + config_options.errMsg = ( "Unable to run numpy search for valid values on precip and " "param grid in mountain mapper downscaling" ) - err_handler.log_critical(ConfigOptions, MpiConfig) - err_handler.check_program_status(ConfigOptions, MpiConfig) + err_handler.log_critical(config_options, mpi_config) + err_handler.check_program_status(config_options, mpi_config) ## Convert local precip back to a rate (mm/s) try: ratioRainGrid[indValid] = ratioRainGrid[indValid] / 3600 except: - ConfigOptions.errMsg = ( + config_options.errMsg = ( "Unable to convert temporary precip rate from mm to mm/s." ) - err_handler.log_critical(ConfigOptions, MpiConfig) - err_handler.check_program_status(ConfigOptions, MpiConfig) + err_handler.log_critical(config_options, mpi_config) + err_handler.check_program_status(config_options, mpi_config) input_forcings.final_forcings[3, :, :] = ratioRainGrid # Reset variables for memory efficiency @@ -1006,27 +1059,32 @@ def nwm_monthly_PRISM_downscale( denLocal = None -def ncar_topo_adj(input_forcings, ConfigOptions, GeoMetaWrfHydro, MpiConfig): +def ncar_topo_adj( + input_forcings: InputForcings, + config_options: ConfigOptions, + geo_meta: GeoMeta, + mpi_config: MpiConfig, +): """Topographic adjustment of incoming shortwave radiation fluxes, given input parameters. :param input_forcings: - :param ConfigOptions: + :param config_options: :return: """ - if MpiConfig.rank == 0: - ConfigOptions.statusMsg = ( + if mpi_config.rank == 0: + config_options.statusMsg = ( "Performing topographic adjustment to incoming shortwave radiation flux." ) - err_handler.log_msg(ConfigOptions, MpiConfig) + err_handler.log_msg(config_options, mpi_config) - if ConfigOptions.grid_type == "gridded": + if config_options.grid_type == "gridded": # Establish where we have missing values. try: - indNdv = np.where(input_forcings.final_forcings == ConfigOptions.globalNdv) + indNdv = np.where(input_forcings.final_forcings == config_options.globalNdv) except: - ConfigOptions.errMsg = "Unable to perform NDV search on input forcings" - err_handler.log_critical(ConfigOptions, MpiConfig) + config_options.errMsg = "Unable to perform NDV search on input forcings" + err_handler.log_critical(config_options, mpi_config) return # By the time this function has been called, necessary input static grids (height, slope, etc), @@ -1034,28 +1092,28 @@ def ncar_topo_adj(input_forcings, ConfigOptions, GeoMetaWrfHydro, MpiConfig): DEGRAD = math.pi / 180.0 DPD = 360.0 / 365.0 try: - DECLIN, SOLCON = radconst(ConfigOptions) + DECLIN, SOLCON = radconst(config_options) except: - ConfigOptions.errMsg = ( + config_options.errMsg = ( "Unable to calculate solar constants based on datetime information." ) - err_handler.log_critical(ConfigOptions, MpiConfig) + err_handler.log_critical(config_options, mpi_config) return try: - coszen_loc, hrang_loc = calc_coszen(ConfigOptions, DECLIN, GeoMetaWrfHydro) + coszen_loc, hrang_loc = calc_coszen(config_options, DECLIN, geo_meta) except: - ConfigOptions.errMsg = ( + config_options.errMsg = ( "Unable to calculate COSZEN or HRANG variables for topographic adjustment " "of incoming shortwave radiation" ) - err_handler.log_critical(ConfigOptions, MpiConfig) + err_handler.log_critical(config_options, mpi_config) return try: TOPO_RAD_ADJ_DRVR( - GeoMetaWrfHydro, - ConfigOptions, + geo_meta, + config_options, input_forcings, coszen_loc, DECLIN, @@ -1063,15 +1121,15 @@ def ncar_topo_adj(input_forcings, ConfigOptions, GeoMetaWrfHydro, MpiConfig): hrang_loc, ) except: - ConfigOptions.errMsg = ( + config_options.errMsg = ( "Unable to perform final topographic adjustment of incoming " "shortwave radiation fluxes." ) - err_handler.log_critical(ConfigOptions, MpiConfig) + err_handler.log_critical(config_options, mpi_config) return # Assign missing values based on our mask. - input_forcings.final_forcings[indNdv] = ConfigOptions.globalNdv + input_forcings.final_forcings[indNdv] = config_options.globalNdv # Reset variables to free up memory DECLIN = None @@ -1080,16 +1138,16 @@ def ncar_topo_adj(input_forcings, ConfigOptions, GeoMetaWrfHydro, MpiConfig): hrang_loc = None indNdv = None - elif ConfigOptions.grid_type == "unstructured": + elif config_options.grid_type == "unstructured": # Establish where we have missing values. try: - indNdv = np.where(input_forcings.final_forcings == ConfigOptions.globalNdv) + indNdv = np.where(input_forcings.final_forcings == config_options.globalNdv) indNdv_elem = np.where( - input_forcings.final_forcings_elem == ConfigOptions.globalNdv + input_forcings.final_forcings_elem == config_options.globalNdv ) except: - ConfigOptions.errMsg = "Unable to perform NDV search on input forcings" - err_handler.log_critical(ConfigOptions, MpiConfig) + config_options.errMsg = "Unable to perform NDV search on input forcings" + err_handler.log_critical(config_options, mpi_config) return # By the time this function has been called, necessary input static grids (height, slope, etc), @@ -1097,29 +1155,29 @@ def ncar_topo_adj(input_forcings, ConfigOptions, GeoMetaWrfHydro, MpiConfig): DEGRAD = math.pi / 180.0 DPD = 360.0 / 365.0 try: - DECLIN, SOLCON = radconst(ConfigOptions) + DECLIN, SOLCON = radconst(config_options) except: - ConfigOptions.errMsg = ( + config_options.errMsg = ( "Unable to calculate solar constants based on datetime information." ) - err_handler.log_critical(ConfigOptions, MpiConfig) + err_handler.log_critical(config_options, mpi_config) return try: coszen_loc, coszen_loc_elem, hrang_loc, hrang_loc_elem = ( - calc_coszen_unstructured(ConfigOptions, DECLIN, GeoMetaWrfHydro) + calc_coszen_unstructured(config_options, DECLIN, geo_meta) ) except: - ConfigOptions.errMsg = ( + config_options.errMsg = ( "Unable to calculate COSZEN or HRANG variables for topographic adjustment " "of incoming shortwave radiation" ) - err_handler.log_critical(ConfigOptions, MpiConfig) + err_handler.log_critical(config_options, mpi_config) return try: TOPO_RAD_ADJ_DRVR_unstructured( - GeoMetaWrfHydro, + geo_meta, input_forcings, coszen_loc, coszen_loc_elem, @@ -1129,16 +1187,16 @@ def ncar_topo_adj(input_forcings, ConfigOptions, GeoMetaWrfHydro, MpiConfig): hrang_loc_elem, ) except: - ConfigOptions.errMsg = ( + config_options.errMsg = ( "Unable to perform final topographic adjustment of incoming " "shortwave radiation fluxes." ) - err_handler.log_critical(ConfigOptions, MpiConfig) + err_handler.log_critical(config_options, mpi_config) return # Assign missing values based on our mask. - input_forcings.final_forcings[indNdv] = ConfigOptions.globalNdv - input_forcings.final_forcings_elem[indNdv_elem] = ConfigOptions.globalNdv + input_forcings.final_forcings[indNdv] = config_options.globalNdv + input_forcings.final_forcings_elem[indNdv_elem] = config_options.globalNdv # Reset variables to free up memory DECLIN = None @@ -1149,13 +1207,13 @@ def ncar_topo_adj(input_forcings, ConfigOptions, GeoMetaWrfHydro, MpiConfig): coszen_loc_elem = None hrang_loc_elem = None indNdv_elem = None - elif ConfigOptions.grid_type == "hydrofabric": + elif config_options.grid_type == "hydrofabric": # Establish where we have missing values. try: - indNdv = np.where(input_forcings.final_forcings == ConfigOptions.globalNdv) + indNdv = np.where(input_forcings.final_forcings == config_options.globalNdv) except: - ConfigOptions.errMsg = "Unable to perform NDV search on input forcings" - err_handler.log_critical(ConfigOptions, MpiConfig) + config_options.errMsg = "Unable to perform NDV search on input forcings" + err_handler.log_critical(config_options, mpi_config) return # By the time this function has been called, necessary input static grids (height, slope, etc), @@ -1163,28 +1221,28 @@ def ncar_topo_adj(input_forcings, ConfigOptions, GeoMetaWrfHydro, MpiConfig): DEGRAD = math.pi / 180.0 DPD = 360.0 / 365.0 try: - DECLIN, SOLCON = radconst(ConfigOptions) + DECLIN, SOLCON = radconst(config_options) except: - ConfigOptions.errMsg = ( + config_options.errMsg = ( "Unable to calculate solar constants based on datetime information." ) - err_handler.log_critical(ConfigOptions, MpiConfig) + err_handler.log_critical(config_options, mpi_config) return try: - coszen_loc, hrang_loc = calc_coszen(ConfigOptions, DECLIN, GeoMetaWrfHydro) + coszen_loc, hrang_loc = calc_coszen(config_options, DECLIN, geo_meta) except: - ConfigOptions.errMsg = ( + config_options.errMsg = ( "Unable to calculate COSZEN or HRANG variables for topographic adjustment " "of incoming shortwave radiation" ) - err_handler.log_critical(ConfigOptions, MpiConfig) + err_handler.log_critical(config_options, mpi_config) return try: TOPO_RAD_ADJ_DRVR( - GeoMetaWrfHydro, - ConfigOptions, + geo_meta, + config_options, input_forcings, coszen_loc, DECLIN, @@ -1192,15 +1250,15 @@ def ncar_topo_adj(input_forcings, ConfigOptions, GeoMetaWrfHydro, MpiConfig): hrang_loc, ) except: - ConfigOptions.errMsg = ( + config_options.errMsg = ( "Unable to perform final topographic adjustment of incoming " "shortwave radiation fluxes." ) - err_handler.log_critical(ConfigOptions, MpiConfig) + err_handler.log_critical(config_options, mpi_config) return # Assign missing values based on our mask. - input_forcings.final_forcings[indNdv] = ConfigOptions.globalNdv + input_forcings.final_forcings[indNdv] = config_options.globalNdv # Reset variables to free up memory DECLIN = None @@ -1210,14 +1268,14 @@ def ncar_topo_adj(input_forcings, ConfigOptions, GeoMetaWrfHydro, MpiConfig): indNdv = None -def radconst(ConfigOptions): +def radconst(config_options: ConfigOptions): """Calculate the current incoming solar constant. Function to calculate the current incoming solar constant. - :param ConfigOptions: + :param config_options: :return: """ - dCurrent = ConfigOptions.current_output_date + dCurrent = config_options.current_output_date DEGRAD = math.pi / 180.0 DPD = 360.0 / 365.0 @@ -1257,12 +1315,12 @@ def radconst(ConfigOptions): return DECLIN, SOLCON -def calc_coszen(ConfigOptions, declin, GeoMetaWrfHydro): +def calc_coszen(config_options: ConfigOptions, declin, geo_meta: GeoMeta): """Calculate the cosine of the solar zenith angle and the hour angle. Downscaling function to compute radiation terms based on current datetime information and lat/lon grids. - :param ConfigOptions: + :param config_options: :param input_forcings: :param declin: :return: @@ -1271,7 +1329,7 @@ def calc_coszen(ConfigOptions, declin, GeoMetaWrfHydro): gmt = 0 # Calculate the current julian day. - dCurrent = ConfigOptions.current_output_date + dCurrent = config_options.current_output_date julian = time.strptime(dCurrent.strftime("%Y.%m.%d"), "%Y.%m.%d").tm_yday da = 6.2831853071795862 * ((julian - 1) / 365.0) @@ -1283,9 +1341,9 @@ def calc_coszen(ConfigOptions, declin, GeoMetaWrfHydro): ) * 229.18 xtime = dCurrent.hour * 60.0 # Minutes of day xt24 = int(xtime) % 1440 + eot - tloctm = GeoMetaWrfHydro.longitude_grid / 15.0 + gmt + xt24 / 60.0 + tloctm = geo_meta.longitude_grid / 15.0 + gmt + xt24 / 60.0 hrang = ((tloctm - 12.0) * degrad) * 15.0 - xxlat = GeoMetaWrfHydro.latitude_grid * degrad + xxlat = geo_meta.latitude_grid * degrad coszen = np.sin(xxlat) * math.sin(declin) + np.cos(xxlat) * math.cos( declin ) * np.cos(hrang) @@ -1297,12 +1355,12 @@ def calc_coszen(ConfigOptions, declin, GeoMetaWrfHydro): return coszen, hrang -def calc_coszen_unstructured(ConfigOptions, declin, GeoMetaWrfHydro): +def calc_coszen_unstructured(config_options: ConfigOptions, declin, geo_meta: GeoMeta): """Calculate the cosine of the solar zenith angle and the hour angle for unstructured grids. Downscaling function to compute radiation terms based on current datetime information and lat/lon grids. - :param ConfigOptions: + :param config_options: :param input_forcings: :param declin: :return: @@ -1311,7 +1369,7 @@ def calc_coszen_unstructured(ConfigOptions, declin, GeoMetaWrfHydro): gmt = 0 # Calculate the current julian day. - dCurrent = ConfigOptions.current_output_date + dCurrent = config_options.current_output_date julian = time.strptime(dCurrent.strftime("%Y.%m.%d"), "%Y.%m.%d").tm_yday da = 6.2831853071795862 * ((julian - 1) / 365.0) @@ -1323,12 +1381,12 @@ def calc_coszen_unstructured(ConfigOptions, declin, GeoMetaWrfHydro): ) * 229.18 xtime = dCurrent.hour * 60.0 # Minutes of day xt24 = int(xtime) % 1440 + eot - tloctm = GeoMetaWrfHydro.longitude_grid / 15.0 + gmt + xt24 / 60.0 - tloctm_elem = GeoMetaWrfHydro.longitude_grid_elem / 15.0 + gmt + xt24 / 60.0 + tloctm = geo_meta.longitude_grid / 15.0 + gmt + xt24 / 60.0 + tloctm_elem = geo_meta.longitude_grid_elem / 15.0 + gmt + xt24 / 60.0 hrang = ((tloctm - 12.0) * degrad) * 15.0 hrang_elem = ((tloctm_elem - 12.0) * degrad) * 15.0 - xxlat = GeoMetaWrfHydro.latitude_grid * degrad - xxlat_elem = GeoMetaWrfHydro.latitude_grid_elem * degrad + xxlat = geo_meta.latitude_grid * degrad + xxlat_elem = geo_meta.latitude_grid_elem * degrad coszen = np.sin(xxlat) * math.sin(declin) + np.cos(xxlat) * math.cos( declin ) * np.cos(hrang) @@ -1345,13 +1403,19 @@ def calc_coszen_unstructured(ConfigOptions, declin, GeoMetaWrfHydro): def TOPO_RAD_ADJ_DRVR( - GeoMetaWrfHydro, ConfigOptions, input_forcings, COSZEN, declin, solcon, hrang2d + geo_meta: GeoMeta, + config_options: ConfigOptions, + input_forcings: InputForcings, + COSZEN, + declin, + solcon, + hrang2d, ): """Topographic adjustment of incoming shortwave radiation fluxes, given input parameters. Downscaling driver for correcting incoming shortwave radiation fluxes from a low resolution to a a higher resolution. - :param GeoMetaWrfHydro: + :param geo_meta: :param input_forcings: :param COSZEN: :param declin: @@ -1361,13 +1425,13 @@ def TOPO_RAD_ADJ_DRVR( """ degrad = math.pi / 180.0 - ny = GeoMetaWrfHydro.ny_local - nx = GeoMetaWrfHydro.nx_local + ny = geo_meta.ny_local + nx = geo_meta.nx_local - xxlat = GeoMetaWrfHydro.latitude_grid * degrad + xxlat = geo_meta.latitude_grid * degrad # Sanity checking on incoming shortwave grid. - if ConfigOptions.grid_type == "gridded": + if config_options.grid_type == "gridded": SWDOWN = input_forcings.final_forcings[7, :, :] else: SWDOWN = input_forcings.final_forcings[7, :] @@ -1376,14 +1440,14 @@ def TOPO_RAD_ADJ_DRVR( COSZEN[np.where(COSZEN < 1e-4)] = 1e-4 - if ConfigOptions.grid_type == "gridded": + if config_options.grid_type == "gridded": corr_frac = np.empty([ny, nx], int) # shadow_mask = np.empty([ny,nx],int) diffuse_frac = np.empty([ny, nx], int) corr_frac[:, :] = 0 diffuse_frac[:, :] = 0 # shadow_mask[:,:] = 0 - indTmp = np.where((GeoMetaWrfHydro.slope[:, :] == 0.0) & (SWDOWN <= 10.0)) + indTmp = np.where((geo_meta.slope[:, :] == 0.0) & (SWDOWN <= 10.0)) else: corr_frac = np.empty([ny], int) # shadow_mask = np.empty([ny],int) @@ -1391,20 +1455,16 @@ def TOPO_RAD_ADJ_DRVR( corr_frac[:] = 0 diffuse_frac[:] = 0 # shadow_mask[:] = 0 - indTmp = np.where((GeoMetaWrfHydro.slope[:] == 0.0) & (SWDOWN <= 10.0)) + indTmp = np.where((geo_meta.slope[:] == 0.0) & (SWDOWN <= 10.0)) corr_frac[indTmp] = 1 term1 = np.sin(xxlat) * np.cos(hrang2d) - term2 = (0 - np.cos(GeoMetaWrfHydro.slp_azi)) * np.sin(GeoMetaWrfHydro.slope) - term3 = np.sin(hrang2d) * ( - np.sin(GeoMetaWrfHydro.slp_azi) * np.sin(GeoMetaWrfHydro.slope) - ) - term4 = (np.cos(xxlat) * np.cos(hrang2d)) * np.cos(GeoMetaWrfHydro.slope) - term5 = np.cos(xxlat) * ( - np.cos(GeoMetaWrfHydro.slp_azi) * np.sin(GeoMetaWrfHydro.slope) - ) - term6 = np.sin(xxlat) * np.cos(GeoMetaWrfHydro.slope) + term2 = (0 - np.cos(geo_meta.slp_azi)) * np.sin(geo_meta.slope) + term3 = np.sin(hrang2d) * (np.sin(geo_meta.slp_azi) * np.sin(geo_meta.slope)) + term4 = (np.cos(xxlat) * np.cos(hrang2d)) * np.cos(geo_meta.slope) + term5 = np.cos(xxlat) * (np.cos(geo_meta.slp_azi) * np.sin(geo_meta.slope)) + term6 = np.sin(xxlat) * np.cos(geo_meta.slope) csza_slp = (term1 * term2 - term3 + term4) * math.cos(declin) + ( term5 + term6 @@ -1432,7 +1492,7 @@ def TOPO_RAD_ADJ_DRVR( term5 = None term6 = None - if ConfigOptions.grid_type == "gridded": + if config_options.grid_type == "gridded": input_forcings.final_forcings[7, :, :] = SWDOWN_OUT else: input_forcings.final_forcings[7, :] = SWDOWN_OUT @@ -1443,8 +1503,8 @@ def TOPO_RAD_ADJ_DRVR( def TOPO_RAD_ADJ_DRVR_unstructured( - GeoMetaWrfHydro, - input_forcings, + geo_meta: GeoMeta, + input_forcings: InputForcings, COSZEN, COSZEN_elem, declin, @@ -1456,7 +1516,7 @@ def TOPO_RAD_ADJ_DRVR_unstructured( Downscaling driver for correcting incoming shortwave radiation fluxes from a low resolution to a a higher resolution. - :param GeoMetaWrfHydro: + :param geo_meta: :param input_forcings: :param COSZEN: :param declin: @@ -1466,14 +1526,14 @@ def TOPO_RAD_ADJ_DRVR_unstructured( """ degrad = math.pi / 180.0 - ny = GeoMetaWrfHydro.ny_local - nx = GeoMetaWrfHydro.nx_local + ny = geo_meta.ny_local + nx = geo_meta.nx_local - ny_elem = GeoMetaWrfHydro.ny_local_elem - nx_elem = GeoMetaWrfHydro.nx_local_elem + ny_elem = geo_meta.ny_local_elem + nx_elem = geo_meta.nx_local_elem - xxlat = GeoMetaWrfHydro.latitude_grid * degrad - xxlat_elem = GeoMetaWrfHydro.latitude_grid_elem * degrad + xxlat = geo_meta.latitude_grid * degrad + xxlat_elem = geo_meta.latitude_grid_elem * degrad # Sanity checking on incoming shortwave grid. SWDOWN = input_forcings.final_forcings[7, :] @@ -1494,38 +1554,30 @@ def TOPO_RAD_ADJ_DRVR_unstructured( diffuse_frac[:] = 0 diffuse_frac_elem[:] = 0 - indTmp = np.where((GeoMetaWrfHydro.slope[:] == 0.0) & (SWDOWN <= 10.0)) - indTmp_elem = np.where( - (GeoMetaWrfHydro.slope_elem[:] == 0.0) & (SWDOWN_elem <= 10.0) - ) + indTmp = np.where((geo_meta.slope[:] == 0.0) & (SWDOWN <= 10.0)) + indTmp_elem = np.where((geo_meta.slope_elem[:] == 0.0) & (SWDOWN_elem <= 10.0)) corr_frac[indTmp] = 1 corr_frac_elem[indTmp_elem] = 1 term1 = np.sin(xxlat) * np.cos(hrang2d) term1_elem = np.sin(xxlat_elem) * np.cos(hrang2d_elem) - term2 = (0 - np.cos(GeoMetaWrfHydro.slp_azi)) * np.sin(GeoMetaWrfHydro.slope) - term2_elem = (0 - np.cos(GeoMetaWrfHydro.slp_azi_elem)) * np.sin( - GeoMetaWrfHydro.slope_elem - ) - term3 = np.sin(hrang2d) * ( - np.sin(GeoMetaWrfHydro.slp_azi) * np.sin(GeoMetaWrfHydro.slope) - ) + term2 = (0 - np.cos(geo_meta.slp_azi)) * np.sin(geo_meta.slope) + term2_elem = (0 - np.cos(geo_meta.slp_azi_elem)) * np.sin(geo_meta.slope_elem) + term3 = np.sin(hrang2d) * (np.sin(geo_meta.slp_azi) * np.sin(geo_meta.slope)) term3_elem = np.sin(hrang2d_elem) * ( - np.sin(GeoMetaWrfHydro.slp_azi_elem) * np.sin(GeoMetaWrfHydro.slope_elem) + np.sin(geo_meta.slp_azi_elem) * np.sin(geo_meta.slope_elem) ) - term4 = (np.cos(xxlat) * np.cos(hrang2d)) * np.cos(GeoMetaWrfHydro.slope) + term4 = (np.cos(xxlat) * np.cos(hrang2d)) * np.cos(geo_meta.slope) term4_elem = (np.cos(xxlat_elem) * np.cos(hrang2d_elem)) * np.cos( - GeoMetaWrfHydro.slope_elem - ) - term5 = np.cos(xxlat) * ( - np.cos(GeoMetaWrfHydro.slp_azi) * np.sin(GeoMetaWrfHydro.slope) + geo_meta.slope_elem ) + term5 = np.cos(xxlat) * (np.cos(geo_meta.slp_azi) * np.sin(geo_meta.slope)) term5_elem = np.cos(xxlat_elem) * ( - np.cos(GeoMetaWrfHydro.slp_azi_elem) * np.sin(GeoMetaWrfHydro.slope_elem) + np.cos(geo_meta.slp_azi_elem) * np.sin(geo_meta.slope_elem) ) - term6 = np.sin(xxlat) * np.cos(GeoMetaWrfHydro.slope) - term6_elem = np.sin(xxlat_elem) * np.cos(GeoMetaWrfHydro.slope_elem) + term6 = np.sin(xxlat) * np.cos(geo_meta.slope) + term6_elem = np.sin(xxlat_elem) * np.cos(geo_meta.slope_elem) csza_slp = (term1 * term2 - term3 + term4) * math.cos(declin) + ( term5 + term6 @@ -1579,28 +1631,28 @@ def TOPO_RAD_ADJ_DRVR_unstructured( SWDOWN_OUT_elem = None -def rel_hum(input_forcings, ConfigOptions): +def rel_hum(input_forcings: InputForcings, config_options: ConfigOptions): """Calculate relative humidity given original, undownscaled surface pressure and 2-meter temperature. Function to calculate relative humidity given original, undownscaled surface pressure and 2-meter temperature. :param input_forcings: - :param ConfigOptions: + :param config_options: :return: """ - if ConfigOptions.grid_type == "gridded": + if config_options.grid_type == "gridded": tmpHumidity = input_forcings.final_forcings[5, :, :] / ( 1 - input_forcings.final_forcings[5, :, :] ) - elif ConfigOptions.grid_type == "unstructured": + elif config_options.grid_type == "unstructured": tmpHumidity = input_forcings.final_forcings[5, :] / ( 1 - input_forcings.final_forcings[5, :] ) tmpHumidity_elem = input_forcings.final_forcings_elem[5, :] / ( 1 - input_forcings.final_forcings_elem[5, :] ) - elif ConfigOptions.grid_type == "hydrofabric": + elif config_options.grid_type == "hydrofabric": tmpHumidity = input_forcings.final_forcings[5, :] / ( 1 - input_forcings.final_forcings[5, :] ) @@ -1612,7 +1664,7 @@ def rel_hum(input_forcings, ConfigOptions): A = 17.269 B = 35.86 - if ConfigOptions.grid_type == "gridded": + if config_options.grid_type == "gridded": EST = ES0 * np.exp( (A * (input_forcings.t2dTmp - T0)) / (input_forcings.t2dTmp - B) ) @@ -1624,7 +1676,7 @@ def rel_hum(input_forcings, ConfigOptions): return RH - elif ConfigOptions.grid_type == "unstructured": + elif config_options.grid_type == "unstructured": EST = ES0 * np.exp( (A * (input_forcings.t2dTmp - T0)) / (input_forcings.t2dTmp - B) ) @@ -1643,7 +1695,7 @@ def rel_hum(input_forcings, ConfigOptions): return RH, RH_elem - elif ConfigOptions.grid_type == "hydrofabric": + elif config_options.grid_type == "hydrofabric": EST = ES0 * np.exp( (A * (input_forcings.t2dTmp - T0)) / (input_forcings.t2dTmp - B) ) @@ -1656,13 +1708,15 @@ def rel_hum(input_forcings, ConfigOptions): return RH -def mixhum_ptrh(input_forcings, relHum, iswit, ConfigOptions): +def mixhum_ptrh( + input_forcings: InputForcings, relHum, iswit, config_options: ConfigOptions +): """Convert relative humidity back to a downscaled 2-meter specific humidity. Functionto convert relative humidity back to a downscaled 2-meter specific humidity :param input_forcings: - :param ConfigOptions: + :param config_options: :return: """ T0 = 273.15 @@ -1672,7 +1726,7 @@ def mixhum_ptrh(input_forcings, relHum, iswit, ConfigOptions): A = 17.269 B = 35.86 - if ConfigOptions.grid_type == "gridded": + if config_options.grid_type == "gridded": term1 = A * (input_forcings.final_forcings[4, :, :] - T0) term2 = input_forcings.final_forcings[4, :, :] - B EST = np.exp(term1 / term2) * ES0 @@ -1703,13 +1757,19 @@ def mixhum_ptrh(input_forcings, relHum, iswit, ConfigOptions): return QW -def mixhum_ptrh_unstructured(input_forcings, relHum, relHum_elem, iswit, ConfigOptions): +def mixhum_ptrh_unstructured( + input_forcings: InputForcings, + relHum, + relHum_elem, + iswit, + config_options: ConfigOptions, +): """Convert relative humidity back to a downscaled 2-meter specific humidity. Functionto convert relative humidity back to a downscaled 2-meter specific humidity :param input_forcings: - :param ConfigOptions: + :param config_options: :return: """ T0 = 273.15