NeuralGCM / model /legacy /parameterizations.py
yzt15806542928's picture
Upload folder using huggingface_hub
f4a39ee verified
Raw
History Blame Contribute Delete
6.82 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.
"""Physics parameterization modules that compute non-dynamical tendencies."""
from typing import Any, Callable, Optional
from dinosaur import coordinate_systems
from dinosaur import pytree_utils
from dinosaur import typing
import gin
import haiku as hk
import jax
from model.legacy import features
from model.legacy import mappings
from model.legacy import transforms
FeaturesModule = features.FeaturesModule
Forcing = typing.Forcing
MappingModule = mappings.MappingModule
StepFilterModule = Callable[..., typing.PyTreeStepFilterFn]
TransformModule = typing.TransformModule
@gin.register
class DirectNeuralParameterization(hk.Module):
"""Computes modal physics tendencies from the input state and forcing."""
def __init__(
self,
coords: coordinate_systems.CoordinateSystem,
dt: float,
physics_specs: Any,
aux_features: typing.AuxFeatures,
modal_to_nodal_features_module: FeaturesModule,
nodal_mapping_module: mappings.MappingModule,
tendency_transform_module: TransformModule,
prediction_mask: Optional[typing.Pytree] = None,
filter_module: Optional[StepFilterModule] = None,
name: Optional[str] = None,
):
super().__init__(name=name)
self.coords = coords
self.prediction_mask = prediction_mask
self.modal_to_nodal_features_fn = modal_to_nodal_features_module(
coords, dt, physics_specs, aux_features)
self.nodal_mapping_module = nodal_mapping_module
self.tendency_transform_fn = tendency_transform_module(
coords, dt, physics_specs, aux_features)
if filter_module is not None:
self.filter_fn = filter_module(
coords, dt, physics_specs, aux_features)
else:
self.filter_fn = lambda _, y: y # no filtering.
def __call__(
self,
inputs: typing.PyTreeState,
memory: Optional[typing.Pytree] = None,
diagnostics: Optional[typing.Pytree] = None,
randomness: Optional[typing.Pytree] = None,
forcing: Optional[Forcing] = None,
) -> typing.PyTreeState:
inputs, from_dict_fn = pytree_utils.as_dict(inputs)
if memory is not None:
memory, _ = pytree_utils.as_dict(memory)
prediction_mask = self.prediction_mask
if prediction_mask is None:
prediction_mask = pytree_utils.tree_map_over_nonscalars(
lambda _: True, inputs, scalar_fn=lambda _: False # pyrefly: ignore[bad-argument-type]
)
prediction_shapes = jax.tree_util.tree_map(
lambda x, y: x if y else None,
coordinate_systems.get_nodal_shapes(inputs, self.coords),
prediction_mask,
)
net = self.nodal_mapping_module(prediction_shapes)
nodal_inputs = self.modal_to_nodal_features_fn(
inputs, memory=memory, diagnostics=diagnostics, randomness=randomness,
forcing=forcing,
)
nodal_tendencies = net(nodal_inputs)
nodal_tendencies = self.tendency_transform_fn(nodal_tendencies)
modal_tendencies = self.coords.horizontal.to_modal(nodal_tendencies)
modal_tendencies = self.filter_fn(inputs, modal_tendencies)
return from_dict_fn(modal_tendencies)
@gin.register
class DivCurlNeuralParameterization(hk.Module):
"""Computes modal physics tendencies via `u, v` → `δ, ζ`."""
def __init__(
self,
coords: coordinate_systems.CoordinateSystem,
dt: float,
physics_specs: Any,
aux_features: typing.AuxFeatures,
modal_to_nodal_features_module: FeaturesModule,
nodal_mapping_module: mappings.MappingModule,
tendency_transform_module: TransformModule,
prediction_mask: Optional[typing.Pytree] = None,
filter_module: Optional[StepFilterModule] = None,
name: Optional[str] = None,
):
super().__init__(name=name)
self.coords = coords
self.prediction_mask = prediction_mask
self.modal_to_nodal_features_fn = modal_to_nodal_features_module(
coords, dt, physics_specs, aux_features)
self.nodal_mapping_module = nodal_mapping_module
self.tendency_transform_fn = tendency_transform_module(
coords, dt, physics_specs, aux_features)
self.get_nodal_shape_fn = (
lambda x: coordinate_systems.get_nodal_shapes(x, coords))
self.to_div_curl_fn = transforms.ToModalWithDivCurlTransform(
coords, dt, physics_specs, aux_features)
if filter_module is not None:
self.filter_fn = filter_module(
coords, dt, physics_specs, aux_features)
else:
self.filter_fn = lambda _, y: y # no filtering.
def __call__(
self,
inputs: typing.PyTreeState,
memory: Optional[typing.Pytree] = None,
diagnostics: Optional[typing.Pytree] = None,
randomness: Optional[typing.Pytree] = None,
forcing: Optional[Forcing] = None,
) -> typing.PyTreeState:
inputs = self.coords.with_dycore_sharding(inputs)
inputs, from_dict_fn = pytree_utils.as_dict(inputs)
if memory is not None:
memory = self.coords.with_dycore_sharding(memory)
memory, _ = pytree_utils.as_dict(memory)
prediction_mask = self.prediction_mask
if prediction_mask is None:
prediction_mask = pytree_utils.tree_map_over_nonscalars(
lambda _: True, inputs, scalar_fn=lambda _: False # pyrefly: ignore[bad-argument-type]
)
prediction_shapes = jax.tree_util.tree_map(
lambda x, y: self.get_nodal_shape_fn(x) if y else None,
inputs,
prediction_mask,
)
prediction_shapes['u'] = prediction_shapes.pop('divergence')
prediction_shapes['v'] = prediction_shapes.pop('vorticity')
net = self.nodal_mapping_module(prediction_shapes)
nodal_inputs = self.modal_to_nodal_features_fn(
inputs, memory=memory, diagnostics=diagnostics, randomness=randomness,
forcing=forcing,
)
nodal_inputs = self.coords.dycore_to_physics_sharding(nodal_inputs)
nodal_tendencies = net(nodal_inputs)
nodal_tendencies = self.coords.physics_to_dycore_sharding(nodal_tendencies)
nodal_tendencies = self.tendency_transform_fn(nodal_tendencies)
modal_tendencies = self.to_div_curl_fn(nodal_tendencies)
modal_tendencies = self.filter_fn(inputs, modal_tendencies)
outputs = from_dict_fn(modal_tendencies)
outputs = self.coords.with_dycore_sharding(outputs)
return outputs