Skip to content

Commit

Permalink
New Fontend (#71)
Browse files Browse the repository at this point in the history
  • Loading branch information
sehnem authored Dec 9, 2024
1 parent 1f9205c commit 8cda228
Show file tree
Hide file tree
Showing 24 changed files with 2,348 additions and 1,247 deletions.
4 changes: 1 addition & 3 deletions .github/workflows/conda.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ jobs:
fail-fast: false
matrix:
platform: [ubuntu-latest, macos-latest]
python-version: ["3.11"]
python-version: ["3.12"]

runs-on: ${{ matrix.platform }}

Expand All @@ -33,8 +33,6 @@ jobs:

steps:
- uses: actions/checkout@v4
with:
submodules: recursive

- name: Get conda
uses: conda-incubator/[email protected]
Expand Down
18 changes: 17 additions & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ endif()
ExternalProject_Add(
rte-rrtmgp
GIT_REPOSITORY https://github.com/earth-system-radiation/rte-rrtmgp.git
GIT_TAG origin/develop
GIT_TAG v1.8
GIT_SHALLOW TRUE
SOURCE_DIR ${CMAKE_CURRENT_BINARY_DIR}/rte-rrtmgp
CONFIGURE_COMMAND ""
Expand Down Expand Up @@ -81,8 +81,24 @@ target_compile_definitions(${TARGET_NAME} PRIVATE
DCMAKE_LIBRARY_OUTPUT_DIRECTORY=pyrte_rrtmgp
)

# Add these checks after the initial Linux check
set(APPLE_ARM FALSE)
if(APPLE)
if(CMAKE_SYSTEM_PROCESSOR STREQUAL "arm64")
set(APPLE_ARM TRUE)
endif()
endif()

if (${LINUX})
target_link_libraries(${TARGET_NAME} PUBLIC gfortran)
elseif(APPLE)
# On macOS, explicitly link against gfortran runtime
if(APPLE_ARM)
target_link_directories(${TARGET_NAME} PUBLIC /opt/homebrew/lib/gcc/current)
else()
target_link_directories(${TARGET_NAME} PUBLIC /usr/local/lib/gcc/current)
endif()
target_link_libraries(${TARGET_NAME} PUBLIC gfortran)
endif()

# The install directory is the output (wheel) directory
Expand Down
56 changes: 27 additions & 29 deletions examples/lw_example.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -6,47 +6,45 @@
"metadata": {},
"outputs": [],
"source": [
"import os\n",
"\n",
"import numpy as np\n",
"import xarray as xr\n",
"\n",
"from pyrte_rrtmgp import rrtmgp_gas_optics\n",
"from pyrte_rrtmgp.kernels.rte import lw_solver_noscat\n",
"\n",
"from pyrte_rrtmgp.rrtmgp_gas_optics import GasOpticsFiles, load_gas_optics\n",
"from pyrte_rrtmgp.rrtmgp_data import download_rrtmgp_data\n",
"from pyrte_rrtmgp.rte_solver import RTESolver\n",
"\n",
"ERROR_TOLERANCE = 1e-4\n",
"ERROR_TOLERANCE = 1e-7\n",
"\n",
"rte_rrtmgp_dir = \"../rrtmgp-data\"\n",
"clear_sky_example_files = f\"{rte_rrtmgp_dir}/examples/rfmip-clear-sky/inputs\"\n",
"rte_rrtmgp_dir = download_rrtmgp_data()\n",
"rfmip_dir = os.path.join(rte_rrtmgp_dir, \"examples\", \"rfmip-clear-sky\")\n",
"input_dir = os.path.join(rfmip_dir, \"inputs\")\n",
"ref_dir = os.path.join(rfmip_dir, \"reference\")\n",
"\n",
"rfmip = xr.load_dataset(\n",
" f\"{clear_sky_example_files}/multiple_input4MIPs_radiation_RFMIP_UColorado-RFMIP-1-2_none.nc\"\n",
")\n",
"rfmip = rfmip.sel(expt=0) # only one experiment\n",
"gas_optics_lw = load_gas_optics(gas_optics_file=GasOpticsFiles.LW_G256)\n",
"\n",
"kdist = xr.load_dataset(f\"{rte_rrtmgp_dir}/rrtmgp-gas-lw-g256.nc\")\n",
"rrtmgp_gas_optics = kdist.gas_optics.load_atmosferic_conditions(rfmip)\n",
"atmosphere_file = \"multiple_input4MIPs_radiation_RFMIP_UColorado-RFMIP-1-2_none.nc\"\n",
"atmosphere_path = os.path.join(input_dir, atmosphere_file)\n",
"atmosphere = xr.load_dataset(atmosphere_path)\n",
"\n",
"_, solver_flux_up, solver_flux_down, _, _ = lw_solver_noscat(\n",
" tau=rrtmgp_gas_optics.tau,\n",
" lay_source=rrtmgp_gas_optics.lay_src,\n",
" lev_source=rrtmgp_gas_optics.lev_src,\n",
" sfc_emis=rfmip[\"surface_emissivity\"].data,\n",
" sfc_src=rrtmgp_gas_optics.sfc_src,\n",
" sfc_src_jac=rrtmgp_gas_optics.sfc_src_jac,\n",
")\n",
"gas_optics_lw.gas_optics.compute(atmosphere, problem_type=\"absorption\")\n",
"\n",
"rlu = xr.load_dataset(\n",
" \"../tests/test_python_frontend/rlu_Efx_RTE-RRTMGP-181204_rad-irf_r1i1p1f1_gn.nc\"\n",
")\n",
"ref_flux_up = rlu.isel(expt=0)[\"rlu\"].values\n",
"solver = RTESolver()\n",
"fluxes = solver.solve(atmosphere, add_to_input=False)\n",
"\n",
"rld = xr.load_dataset(\n",
" \"../tests/test_python_frontend/rld_Efx_RTE-RRTMGP-181204_rad-irf_r1i1p1f1_gn.nc\"\n",
")\n",
"ref_flux_down = rld.isel(expt=0)[\"rld\"].values\n",
"rlu_reference = f\"{ref_dir}/rlu_Efx_RTE-RRTMGP-181204_rad-irf_r1i1p1f1_gn.nc\"\n",
"rld_reference = f\"{ref_dir}/rld_Efx_RTE-RRTMGP-181204_rad-irf_r1i1p1f1_gn.nc\"\n",
"rlu = xr.load_dataset(rlu_reference, decode_cf=False)\n",
"rld = xr.load_dataset(rld_reference, decode_cf=False)\n",
"\n",
"assert np.isclose(solver_flux_up, ref_flux_up, atol=ERROR_TOLERANCE).all()\n",
"assert np.isclose(solver_flux_down, ref_flux_down, atol=ERROR_TOLERANCE).all()"
"assert np.isclose(\n",
" fluxes[\"lw_flux_up_broadband\"], rlu[\"rlu\"], atol=ERROR_TOLERANCE\n",
").all()\n",
"assert np.isclose(\n",
" fluxes[\"lw_flux_down_broadband\"], rld[\"rld\"], atol=ERROR_TOLERANCE\n",
").all()"
]
}
],
Expand Down
75 changes: 24 additions & 51 deletions examples/sw_example.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -2,72 +2,45 @@
"cells": [
{
"cell_type": "code",
"execution_count": 1,
"execution_count": 2,
"metadata": {},
"outputs": [],
"source": [
"import os\n",
"\n",
"import numpy as np\n",
"import xarray as xr\n",
"\n",
"from pyrte_rrtmgp import rrtmgp_gas_optics\n",
"from pyrte_rrtmgp.kernels.rte import sw_solver_2stream\n",
"from pyrte_rrtmgp.utils import compute_mu0, get_usecols, compute_toa_flux\n",
"\n",
"ERROR_TOLERANCE = 1e-4\n",
"\n",
"rte_rrtmgp_dir = \"../rrtmgp-data\"\n",
"clear_sky_example_files = f\"{rte_rrtmgp_dir}/examples/rfmip-clear-sky/inputs\"\n",
"\n",
"rfmip = xr.load_dataset(\n",
" f\"{clear_sky_example_files}/multiple_input4MIPs_radiation_RFMIP_UColorado-RFMIP-1-2_none.nc\"\n",
")\n",
"rfmip = rfmip.sel(expt=0) # only one experiment\n",
"\n",
"kdist = xr.load_dataset(f\"{rte_rrtmgp_dir}/rrtmgp-gas-sw-g224.nc\")\n",
"gas_optics = kdist.gas_optics.load_atmosferic_conditions(rfmip)\n",
"from pyrte_rrtmgp.rrtmgp_gas_optics import GasOpticsFiles, load_gas_optics\n",
"from pyrte_rrtmgp.rrtmgp_data import download_rrtmgp_data\n",
"from pyrte_rrtmgp.rte_solver import RTESolver\n",
"\n",
"surface_albedo = rfmip[\"surface_albedo\"].data\n",
"total_solar_irradiance = rfmip[\"total_solar_irradiance\"].data\n",
"ERROR_TOLERANCE = 1e-7\n",
"\n",
"nlayer = len(rfmip[\"layer\"])\n",
"mu0 = compute_mu0(rfmip[\"solar_zenith_angle\"].values, nlayer=nlayer)\n",
"rte_rrtmgp_dir = download_rrtmgp_data()\n",
"rfmip_dir = os.path.join(rte_rrtmgp_dir, \"examples\", \"rfmip-clear-sky\")\n",
"input_dir = os.path.join(rfmip_dir, \"inputs\")\n",
"ref_dir = os.path.join(rfmip_dir, \"reference\")\n",
"\n",
"toa_flux = compute_toa_flux(total_solar_irradiance, gas_optics.solar_source)\n",
"gas_optics_sw = load_gas_optics(gas_optics_file=GasOpticsFiles.SW_G224)\n",
"\n",
"_, _, _, solver_flux_up, solver_flux_down, _ = sw_solver_2stream(\n",
" kdist.gas_optics.top_at_1,\n",
" gas_optics.tau,\n",
" gas_optics.ssa,\n",
" gas_optics.g,\n",
" mu0,\n",
" sfc_alb_dir=surface_albedo,\n",
" sfc_alb_dif=surface_albedo,\n",
" inc_flux_dir=toa_flux,\n",
" inc_flux_dif=None,\n",
" has_dif_bc=False,\n",
" do_broadband=True,\n",
")\n",
"atmosphere_file = \"multiple_input4MIPs_radiation_RFMIP_UColorado-RFMIP-1-2_none.nc\"\n",
"atmosphere_path = os.path.join(input_dir, atmosphere_file)\n",
"atmosphere = xr.load_dataset(atmosphere_path)\n",
"\n",
"# RTE will fail if passed solar zenith angles greater than 90 degree. We replace any with\n",
"# nighttime columns with a default solar zenith angle. We'll mask these out later, of\n",
"# course, but this gives us more work and so a better measure of timing.\n",
"usecol = get_usecols(rfmip[\"solar_zenith_angle\"].values)\n",
"solver_flux_up = solver_flux_up * usecol[:, np.newaxis]\n",
"solver_flux_down = solver_flux_down * usecol[:, np.newaxis]\n",
"gas_optics_sw.gas_optics.compute(atmosphere, problem_type=\"two-stream\")\n",
"\n",
"# Compare the results with the reference data\n",
"rsu = xr.load_dataset(\n",
" \"../tests/test_python_frontend/rsu_Efx_RTE-RRTMGP-181204_rad-irf_r1i1p1f1_gn.nc\"\n",
")\n",
"ref_flux_up = rsu.isel(expt=0)[\"rsu\"].values\n",
"solver = RTESolver()\n",
"fluxes = solver.solve(atmosphere, add_to_input=False)\n",
"\n",
"rsd = xr.load_dataset(\n",
" \"../tests/test_python_frontend/rsd_Efx_RTE-RRTMGP-181204_rad-irf_r1i1p1f1_gn.nc\"\n",
")\n",
"ref_flux_down = rsd.isel(expt=0)[\"rsd\"].values\n",
"rsu_reference = f\"{ref_dir}/rsu_Efx_RTE-RRTMGP-181204_rad-irf_r1i1p1f1_gn.nc\"\n",
"rsd_reference = f\"{ref_dir}/rsd_Efx_RTE-RRTMGP-181204_rad-irf_r1i1p1f1_gn.nc\"\n",
"rsu = xr.load_dataset(rsu_reference, decode_cf=False)\n",
"rsd = xr.load_dataset(rsd_reference, decode_cf=False)\n",
"\n",
"assert np.isclose(solver_flux_up, ref_flux_up, atol=ERROR_TOLERANCE).all()\n",
"assert np.isclose(solver_flux_down, ref_flux_down, atol=ERROR_TOLERANCE).all()"
"assert np.isclose(fluxes[\"sw_flux_up\"], rsu[\"rsu\"], atol=ERROR_TOLERANCE).all()\n",
"assert np.isclose(fluxes[\"sw_flux_down\"], rsd[\"rsd\"], atol=ERROR_TOLERANCE).all()"
]
}
],
Expand Down
5 changes: 3 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ version = "0.0.6"
description = "A Python interface to the RTE+RRTMGP Fortran software package."
readme = "README.md"
requires-python = ">=3.7"
dependencies = ["numpy>=1.21.0", "xarray>=2023.5.0", "netcdf4>=1.5.7"]
dependencies = ["numpy>=2.0.0", "xarray>=2023.5.0", "netcdf4>=1.5.7", "requests>=2.4.0"]
classifiers = [
"Development Status :: 4 - Beta",
"License :: OSI Approved :: MIT License",
Expand All @@ -15,6 +15,7 @@ classifiers = [
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"Programming Language :: Python :: 3.13",
]


Expand All @@ -24,7 +25,7 @@ build-backend = "scikit_build_core.build"


[project.optional-dependencies]
test = ["pytest", "numpy>=1.21.0", "xarray>=2023.5.0", "netcdf4>=1.5.7", "requests>=2.4.0"]
test = ["pytest", "numpy>=2.0.0", "xarray>=2023.5.0", "netcdf4>=1.5.7", "requests>=2.4.0"]


[tool.scikit-build]
Expand Down
52 changes: 52 additions & 0 deletions pyrte_rrtmgp/config.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
"""Default mappings for gas names, dimensions and variables used in RRTMGP.
This module contains dictionaries that map standard names to dataset-specific names
for gases, dimensions and variables used in radiative transfer calculations.
"""

from typing import Dict, Final

# Mapping of standard gas names to RRTMGP-specific names
DEFAULT_GAS_MAPPING: Final[Dict[str, str]] = {
"h2o": "water_vapor",
"co2": "carbon_dioxide_GM",
"o3": "ozone",
"n2o": "nitrous_oxide_GM",
"co": "carbon_monoxide_GM",
"ch4": "methane_GM",
"o2": "oxygen_GM",
"n2": "nitrogen_GM",
"ccl4": "carbon_tetrachloride_GM",
"cfc11": "cfc11_GM",
"cfc12": "cfc12_GM",
"cfc22": "hcfc22_GM",
"hfc143a": "hfc143a_GM",
"hfc125": "hfc125_GM",
"hfc23": "hfc23_GM",
"hfc32": "hfc32_GM",
"hfc134a": "hfc134a_GM",
"cf4": "cf4_GM",
"no2": "no2",
}

# Mapping of standard dimension names to dataset-specific names
DEFAULT_DIM_MAPPING: Final[Dict[str, str]] = {
"site": "site",
"layer": "layer",
"level": "level",
}

# Mapping of standard variable names to dataset-specific names
DEFAULT_VAR_MAPPING: Final[Dict[str, str]] = {
"pres_layer": "pres_layer",
"pres_level": "pres_level",
"temp_layer": "temp_layer",
"temp_level": "temp_level",
"surface_temperature": "surface_temperature",
"solar_zenith_angle": "solar_zenith_angle",
"surface_albedo": "surface_albedo",
"surface_albedo_dir": "surface_albedo_dir",
"surface_albedo_dif": "surface_albedo_dif",
"surface_emissivity": "surface_emissivity",
"surface_emissivity_jacobian": "surface_emissivity_jacobian",
}
54 changes: 49 additions & 5 deletions pyrte_rrtmgp/constants.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,49 @@
HELMERT1 = 9.80665
HELMERT2 = 0.02586
M_DRY = 0.028964
M_H2O = 0.018016
AVOGAD = 6.02214076e23
"""Physical and mathematical constants used in radiative transfer calculations.
This module contains various physical and mathematical constants needed for
radiative transfer calculations, including gravitational parameters, molecular
masses, and Gaussian quadrature weights and points.
"""

from typing import Dict, Final

import numpy as np
from numpy.typing import NDArray

# Gravitational parameters from Helmert's equation (m/s^2)
HELMERT1: Final[float] = 9.80665 # Standard gravity at sea level
HELMERT2: Final[float] = 0.02586 # Gravity variation with latitude

# Molecular masses (kg/mol)
M_DRY: Final[float] = 0.028964 # Dry air
M_H2O: Final[float] = 0.018016 # Water vapor

# Avogadro's number (molecules/mol)
AVOGAD: Final[float] = 6.02214076e23

# Solar constants for orbit calculations
SOLAR_CONSTANTS: Final[Dict[str, float]] = {
"A_OFFSET": 0.1495954, # Semi-major axis offset (AU)
"B_OFFSET": 0.00066696, # Orbital eccentricity factor
}

# Gaussian quadrature constants for radiative transfer
GAUSS_DS: NDArray[np.float64] = np.reciprocal(
np.array(
[
[0.6096748751, np.inf, np.inf, np.inf],
[0.2509907356, 0.7908473988, np.inf, np.inf],
[0.1024922169, 0.4417960320, 0.8633751621, np.inf],
[0.0454586727, 0.2322334416, 0.5740198775, 0.9030775973],
]
)
)

GAUSS_WTS: NDArray[np.float64] = np.array(
[
[1.0, 0.0, 0.0, 0.0],
[0.2300253764, 0.7699746236, 0.0, 0.0],
[0.0437820218, 0.3875796738, 0.5686383044, 0.0],
[0.0092068785, 0.1285704278, 0.4323381850, 0.4298845087],
]
)
40 changes: 40 additions & 0 deletions pyrte_rrtmgp/data_types.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
from enum import Enum, StrEnum


class GasOpticsFiles(StrEnum):
"""Enumeration of default RRTMGP gas optics data files.
This enum defines the available pre-configured gas optics data files that can be used
with RRTMGP. The files contain absorption coefficients and other optical properties
needed for radiative transfer calculations.
Attributes:
LW_G128: Longwave gas optics file with 128 g-points
LW_G256: Longwave gas optics file with 256 g-points
SW_G112: Shortwave gas optics file with 112 g-points
SW_G224: Shortwave gas optics file with 224 g-points
"""

LW_G128 = "rrtmgp-gas-lw-g128.nc"
LW_G256 = "rrtmgp-gas-lw-g256.nc"
SW_G112 = "rrtmgp-gas-sw-g112.nc"
SW_G224 = "rrtmgp-gas-sw-g224.nc"


class ProblemTypes(StrEnum):
"""Enumeration of available radiation calculation types.
This enum defines the different types of radiation calculations that can be performed,
including both longwave and shortwave calculations with different solution methods.
Attributes:
LW_ABSORPTION: Longwave absorption-only calculation
LW_2STREAM: Longwave two-stream approximation calculation
SW_DIRECT: Shortwave direct beam calculation
SW_2STREAM: Shortwave two-stream approximation calculation
"""

LW_ABSORPTION = "Longwave absorption"
LW_2STREAM = "Longwave 2-stream"
SW_DIRECT = "Shortwave direct"
SW_2STREAM = "Shortwave 2-stream"
Loading

0 comments on commit 8cda228

Please sign in to comment.