NeuralGCM / model /legacy /orographies.py
yzt15806542928's picture
Upload folder using huggingface_hub
f4a39ee verified
Raw
History Blame Contribute Delete
5.01 kB
# Copyright 2024 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Modules responsible for orography processing and initialization."""
from typing import Any, Callable, Mapping, Optional, Sequence
from dinosaur import coordinate_systems
from dinosaur import primitive_equations
from dinosaur import scales
from dinosaur import typing
from dinosaur import xarray_utils
import gin
import haiku as hk
import jax.numpy as jnp
import numpy as np
units = scales.units
OrographyModule = Callable[..., typing.Array]
FilterModule = Callable[..., typing.PyTreeFilterFn]
@gin.register
class ClippedOrography(hk.Module):
"""Module that initializes orography by converting to modal and clipping."""
def __init__(
self,
coords: coordinate_systems.CoordinateSystem,
dt: float,
physics_specs: Any,
aux_features: typing.AuxFeatures,
wavenumbers_to_clip: int = 1,
name: Optional[str] = None,
):
del dt, physics_specs
super().__init__(name=name)
self.coords = coords
self.wavenumbers_to_clip = wavenumbers_to_clip
self.nodal_orography = aux_features.get(
xarray_utils.OROGRAPHY, np.zeros(coords.horizontal.nodal_shape))
def __call__(self) -> typing.Array:
"""Returns orography converted to modal representation with clipping."""
return primitive_equations.truncated_modal_orography(
self.nodal_orography, self.coords, self.wavenumbers_to_clip)
@gin.register
class FilteredCustomOrography(hk.Module):
"""Module that initializes orography from external data."""
def __init__(
self,
coords: coordinate_systems.CoordinateSystem,
dt: float,
physics_specs: Any,
aux_features: typing.AuxFeatures,
orography_data_path: str,
filter_modules: Sequence[FilterModule] = tuple(),
renaming_dict: Optional[Mapping[str, str]] = None,
name: Optional[str] = None,
):
super().__init__(name=name)
ds = xarray_utils.ds_from_path_or_aux(orography_data_path, aux_features)
if renaming_dict is not None:
ds = ds.rename(renaming_dict)
nodal_orography = xarray_utils.nodal_orography_from_ds(ds)
# TODO(dkochkov) Insist on having units specified in variable attrs.
self.nodal_orography = physics_specs.nondimensionalize(
nodal_orography * units.meter) # pyrefly: ignore[unsupported-operation]
self.coords = coords
# Note: here we explicitly use linear truncation to preserve full signal.
# Smoothing is then achieved by interpolation to self.coords and filtering.
self.input_coords = xarray_utils.coordinate_system_from_dataset(
ds, truncation=xarray_utils.LINEAR, spmd_mesh=coords.spmd_mesh,
spherical_harmonics_impl=self.coords.horizontal.spherical_harmonics_impl
)
self.filter_fns = [
module(coords, dt, physics_specs, aux_features)
for module in filter_modules]
def __call__(self) -> typing.Array:
"""Returns orography converted to modal representation with filtering."""
return primitive_equations.filtered_modal_orography(
self.nodal_orography, self.coords, self.input_coords, self.filter_fns)
@gin.register
class LearnedOrography(hk.Module):
"""Module that uses learned parameters to correct orography."""
def __init__(
self,
coords: coordinate_systems.CoordinateSystem,
dt: float,
physics_specs: Any,
aux_features: typing.AuxFeatures,
base_orography_module: OrographyModule,
correction_scale: float,
name: Optional[str] = None,
):
super().__init__(name=name)
self.coords = coords
self.base_orography_fn = base_orography_module(
coords, dt, physics_specs, aux_features)
self.scale = correction_scale
# coords.horizontal.modal_shape can change based upon the required amount of
# padding for a particular implementation of spherical harmonics, but the
# mask should always have the same number of non-zero elements in the same
# order.
self.correction = hk.get_parameter(
'orography', (coords.horizontal.mask.sum(),), jnp.float32,
init=hk.initializers.Constant(0.0))
def __call__(self) -> typing.Array:
"""Returns orography in modal representation."""
mask = self.coords.horizontal.mask
correction_2d = jnp.zeros(self.coords.horizontal.modal_shape)
correction_2d = correction_2d.at[mask].set(self.correction)
return self.base_orography_fn() + correction_2d * self.scale # pytype: disable=not-callable # jax-ndarray