| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| """Defines `decoder` modules that map model state to output data format.""" |
|
|
| import functools |
| from typing import Any, Callable, Dict, Optional, Tuple, TypeVar |
| import zlib |
|
|
| from dinosaur import coordinate_systems |
| from dinosaur import primitive_equations |
| from dinosaur import pytree_utils |
| from dinosaur import scales |
| from dinosaur import spherical_harmonic |
| from dinosaur import typing |
| from dinosaur import vertical_interpolation |
| from dinosaur import weatherbench_utils |
| from dinosaur import xarray_utils |
| import gin |
| import haiku as hk |
| import jax |
| import jax.numpy as jnp |
| from model.legacy import diagnostics |
| from model.legacy import features |
| from model.legacy import filters |
| from model.legacy import mappings |
| from model.legacy import orographies |
| from model.legacy import perturbations |
| from model.legacy import stochastic |
| from model.legacy import transforms |
| import numpy as np |
|
|
|
|
| |
| |
|
|
| |
| |
|
|
| DataState = typing.DataState |
| DiagnosticModule = diagnostics.DiagnosticModule |
| FeaturesModule = features.FeaturesModule |
| FilterModule = Callable[..., typing.PyTreeFilterFn] |
| Forcing = typing.Forcing |
| MappingModule = mappings.MappingModule |
| PyTreeState = typing.PyTreeState |
| ModelState = typing.ModelState |
| TransformModule = typing.TransformModule |
| OrographyModule = orographies.OrographyModule |
| PerturbationModule = perturbations.PerturbationModule |
| RandomnessModule = stochastic.RandomnessModule |
|
|
|
|
| @gin.register |
| class DecoderIdentityTransform(hk.Module): |
| """Transformation that returns inputs without modification.""" |
|
|
| def __init__( |
| self, |
| coords: coordinate_systems.CoordinateSystem, |
| dt: float, |
| physics_specs: Any, |
| aux_features: Dict[str, Any], |
| output_coords: coordinate_systems.CoordinateSystem, |
| name: Optional[str] = None, |
| ): |
| super().__init__(name=name) |
| del coords, dt, physics_specs, aux_features, output_coords |
|
|
| def __call__(self, inputs: PyTreeState) -> PyTreeState: |
| return inputs |
|
|
|
|
| @gin.register |
| class DecoderFilterTransform(hk.Module): |
| """Transformation that returns truncated and filtered modal inputs.""" |
|
|
| def __init__( |
| self, |
| coords: coordinate_systems.CoordinateSystem, |
| dt: float, |
| physics_specs: Any, |
| aux_features: Dict[str, Any], |
| output_coords: coordinate_systems.CoordinateSystem, |
| filter_module: FilterModule = filters.DataNoFilter, |
| return_nodal: bool = True, |
| name: Optional[str] = None, |
| ): |
| super().__init__(name=name) |
| self.output_coords = output_coords |
| self.filter_fn = filter_module(coords, dt, physics_specs, aux_features) |
| self.return_nodal = return_nodal |
|
|
| def __call__(self, inputs: PyTreeState) -> PyTreeState: |
| modal_inputs = coordinate_systems.maybe_to_modal(inputs, self.output_coords) |
| filtered_inputs = self.filter_fn(modal_inputs) |
| if self.return_nodal: |
| return self.output_coords.horizontal.to_nodal(filtered_inputs) |
| return filtered_inputs |
|
|
|
|
| @gin.register |
| class OutputModalToModalTransform(hk.Module): |
| """Transformation that truncates modal state to output coords.""" |
|
|
| def __init__( |
| self, |
| coords: coordinate_systems.CoordinateSystem, |
| dt: float, |
| physics_specs: Any, |
| aux_features: Dict[str, Any], |
| output_coords: coordinate_systems.CoordinateSystem, |
| name: Optional[str] = None, |
| ): |
| super().__init__(name=name) |
| self.coords = coords |
| self.output_coords = output_coords |
|
|
| def __call__(self, inputs: PyTreeState) -> PyTreeState: |
| downsample_fn = coordinate_systems.get_spectral_downsample_fn( |
| self.coords, self.output_coords |
| ) |
| return downsample_fn(inputs) |
|
|
|
|
| @gin.register |
| class OutputModalToNodalTransform(hk.Module): |
| """Transformation that converts modal state to nodal representation.""" |
|
|
| def __init__( |
| self, |
| coords: coordinate_systems.CoordinateSystem, |
| dt: float, |
| physics_specs: Any, |
| aux_features: Dict[str, Any], |
| output_coords: coordinate_systems.CoordinateSystem, |
| name: Optional[str] = None, |
| ): |
| super().__init__(name=name) |
| self.coords = coords |
| self.output_coords = output_coords |
|
|
| def __call__(self, inputs: PyTreeState) -> PyTreeState: |
| to_nodal_fn = self.output_coords.horizontal.to_nodal |
| downsample_fn = coordinate_systems.get_spectral_downsample_fn( |
| self.coords, self.output_coords |
| ) |
| return jax.tree_util.tree_map( |
| lambda x: to_nodal_fn(downsample_fn(x)), inputs |
| ) |
|
|
|
|
| @gin.register |
| class OutputNodalToModalTransform(hk.Module): |
| """Transformation that converts nodal state to modal representation.""" |
|
|
| def __init__( |
| self, |
| coords: coordinate_systems.CoordinateSystem, |
| dt: float, |
| physics_specs: Any, |
| aux_features: Dict[str, Any], |
| output_coords: coordinate_systems.CoordinateSystem, |
| name: Optional[str] = None, |
| ): |
| super().__init__(name=name) |
| self.output_coords = output_coords |
|
|
| def __call__(self, inputs: PyTreeState) -> PyTreeState: |
| return self.output_coords.horizontal.to_modal(inputs) |
|
|
|
|
| @gin.register |
| class ModalOutputLearnedAdaptorTransform(hk.Module): |
| """Transformation using a tower to adapt modal outputs to the data domain.""" |
|
|
| def __init__( |
| self, |
| coords: coordinate_systems.CoordinateSystem, |
| dt: float, |
| physics_specs: Any, |
| aux_features: Dict[str, Any], |
| output_coords: coordinate_systems.CoordinateSystem, |
| modal_to_nodal_features_module: FeaturesModule, |
| nodal_mapping_module: MappingModule, |
| output_transform_module: TransformModule, |
| name: Optional[str] = None, |
| ): |
| del output_coords |
| super().__init__(name=name) |
| self.coords = coords |
| 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.output_transform_fn = output_transform_module( |
| coords, dt, physics_specs, aux_features |
| ) |
| self.get_nodal_shape_fn = lambda x: coordinate_systems.get_nodal_shapes( |
| x, coords |
| ) |
|
|
| def __call__(self, inputs: PyTreeState) -> PyTreeState: |
| """Applies transform to modal inputs, returns modal outputs.""" |
| inputs, from_dict_fn = pytree_utils.as_dict(inputs) |
| prediction_shapes = jax.tree_util.tree_map(self.get_nodal_shape_fn, inputs) |
| |
| sim_time_shape = prediction_shapes.pop('sim_time', None) |
| net = self.nodal_mapping_module(prediction_shapes) |
| nodal_input_features = self.modal_to_nodal_features_fn(inputs, None) |
| nodal_corrections = self.output_transform_fn(net(nodal_input_features)) |
| corrections = self.coords.horizontal.to_modal(nodal_corrections) |
| if sim_time_shape is not None: |
| corrections['sim_time'] = 0.0 |
| outputs = jax.tree_util.tree_map(lambda x, y: x + y, inputs, corrections) |
| return from_dict_fn(outputs) |
|
|
|
|
| @gin.register |
| class NodalOutputLearnedAdaptorTransform(hk.Module): |
| """Transformation using a tower to adapt nodal outputs to the data domain.""" |
|
|
| def __init__( |
| self, |
| coords: coordinate_systems.CoordinateSystem, |
| dt: float, |
| physics_specs: Any, |
| aux_features: Dict[str, Any], |
| output_coords: coordinate_systems.CoordinateSystem, |
| nodal_to_nodal_features_module: FeaturesModule, |
| nodal_mapping_module: MappingModule, |
| output_transform_module: TransformModule, |
| name: Optional[str] = None, |
| ): |
| del output_coords |
| super().__init__(name=name) |
| self.coords = coords |
| self.nodal_to_nodal_features_fn = nodal_to_nodal_features_module( |
| coords, dt, physics_specs, aux_features |
| ) |
| self.nodal_mapping_module = nodal_mapping_module |
| self.output_transform_fn = output_transform_module( |
| coords, dt, physics_specs, aux_features |
| ) |
| self.get_nodal_shape_fn = lambda x: coordinate_systems.get_nodal_shapes( |
| x, coords |
| ) |
|
|
| def __call__(self, inputs: PyTreeState) -> PyTreeState: |
| """Applies transform to nodal inputs, returns nodal outputs.""" |
| inputs, from_dict_fn = pytree_utils.as_dict(inputs) |
| prediction_shapes = jax.tree_util.tree_map(self.get_nodal_shape_fn, inputs) |
| |
| sim_time_shape = prediction_shapes.pop('sim_time', None) |
| net = self.nodal_mapping_module(prediction_shapes) |
| input_features = self.nodal_to_nodal_features_fn(inputs, None) |
| corrections = self.output_transform_fn(net(input_features)) |
| if sim_time_shape is not None: |
| corrections['sim_time'] = 0.0 |
| outputs = jax.tree_util.tree_map(lambda x, y: x + y, inputs, corrections) |
| return from_dict_fn(outputs) |
|
|
|
|
| @gin.register |
| class DecoderCombinedTransform(hk.Module): |
| """Module that applies multiple transformations sequentially.""" |
|
|
| def __init__( |
| self, |
| coords: coordinate_systems.CoordinateSystem, |
| dt: float, |
| physics_specs: Any, |
| aux_features: typing.AuxFeatures, |
| output_coords: coordinate_systems.CoordinateSystem, |
| transforms: Tuple[TransformModule, ...], |
| name: Optional[str] = None, |
| ): |
| super().__init__(name=name) |
| self.transform_fns = [ |
| module(coords, dt, physics_specs, aux_features, output_coords) |
| for module in transforms |
| ] |
|
|
| def __call__(self, inputs: PyTreeState) -> PyTreeState: |
| for transform_fn in self.transform_fns: |
| inputs = transform_fn(inputs) |
| return inputs |
|
|
|
|
| @gin.register |
| class IdentityDecoder(hk.Module): |
| """Decoder that returns model state unaltered.""" |
|
|
| def __init__( |
| self, |
| coords: coordinate_systems.CoordinateSystem, |
| dt: float, |
| physics_specs: Any, |
| aux_features: Dict[str, Any], |
| output_coords: coordinate_systems.CoordinateSystem, |
| name: Optional[str] = None, |
| ): |
| del coords, dt, physics_specs, aux_features, output_coords |
| super().__init__(name=name) |
|
|
| def __call__(self, x: ModelState, forcing: Forcing) -> DataState: |
| del forcing |
| return x.state |
|
|
|
|
| @gin.register |
| class StateToDictDecoder(hk.Module): |
| """Decoder that returns a dict representation of a model state.""" |
|
|
| def __init__( |
| self, |
| coords: coordinate_systems.CoordinateSystem, |
| dt: float, |
| physics_specs: Any, |
| aux_features: Dict[str, Any], |
| output_coords: coordinate_systems.CoordinateSystem, |
| transform_module: TransformModule = DecoderIdentityTransform, |
| name: Optional[str] = None, |
| ): |
| super().__init__(name=name) |
| self.transform_fn = transform_module( |
| coords, dt, physics_specs, aux_features, output_coords |
| ) |
|
|
| def __call__(self, x: ModelState, forcing: Forcing) -> DataState: |
| del forcing |
| state_dict, _ = pytree_utils.as_dict(x.state) |
| return self.transform_fn(state_dict) |
|
|
|
|
| @gin.register |
| class LeapfrogSliceDecoder(hk.Module): |
| """Decoder that returns one slice out of a leapfrog pair.""" |
|
|
| def __init__( |
| self, |
| coords: coordinate_systems.CoordinateSystem, |
| dt: float, |
| physics_specs: Any, |
| aux_features: Dict[str, Any], |
| output_coords: coordinate_systems.CoordinateSystem, |
| slice_id: int = 0, |
| transform_module: TransformModule = DecoderIdentityTransform, |
| name: Optional[str] = None, |
| ): |
| super().__init__(name=name) |
| self.slice_id = slice_id |
| self.transform_fn = transform_module( |
| coords, dt, physics_specs, aux_features, output_coords |
| ) |
|
|
| def __call__(self, x: ModelState, forcing: Forcing) -> DataState: |
| del forcing |
| return self.transform_fn(x.state[self.slice_id]) |
|
|
|
|
| @gin.register |
| class LeapfrogSliceDictDecoder(hk.Module): |
| """Decoder that returns one slice out of a leapfrog pair as dictionary.""" |
|
|
| def __init__( |
| self, |
| coords: coordinate_systems.CoordinateSystem, |
| dt: float, |
| physics_specs: Any, |
| aux_features: Dict[str, Any], |
| output_coords: coordinate_systems.CoordinateSystem, |
| slice_id: int = 0, |
| transform_module: TransformModule = DecoderIdentityTransform, |
| name: Optional[str] = None, |
| ): |
| super().__init__(name=name) |
| self.slice_id = slice_id |
| self.transform_fn = transform_module( |
| coords, dt, physics_specs, aux_features, output_coords |
| ) |
|
|
| def __call__(self, x: ModelState, forcing: Forcing) -> DataState: |
| del forcing |
| state_dict, _ = pytree_utils.as_dict(x.state[self.slice_id]) |
| return self.transform_fn(state_dict) |
|
|
|
|
| @gin.configurable |
| class PrimitiveToWeatherbenchDecoder(hk.Module): |
| """Decoder that converts `StateWithTime` to `weatherbench.State`.""" |
|
|
| def __init__( |
| self, |
| coords: coordinate_systems.CoordinateSystem, |
| dt: float, |
| physics_specs: Any, |
| aux_features: Dict[str, Any], |
| output_coords: coordinate_systems.CoordinateSystem, |
| time_axis: int = 0, |
| orography_module: OrographyModule = orographies.ClippedOrography, |
| transform_module: TransformModule = DecoderIdentityTransform, |
| name: Optional[str] = None, |
| ): |
| super().__init__(name=name) |
| ref_temps = aux_features[xarray_utils.REF_TEMP_KEY] |
| self.ref_temps = ref_temps[..., np.newaxis, np.newaxis] |
| self.output_coords = output_coords |
| self.coords = coords |
| self.physics_specs = physics_specs |
| self.velocity_fn = functools.partial( |
| spherical_harmonic.vor_div_to_uv_nodal, |
| output_coords.horizontal, |
| ) |
| modal_orography_init_fn = orography_module( |
| coords, dt, physics_specs, aux_features |
| ) |
| orography = modal_orography_init_fn() |
| self.nodal_orography = coords.horizontal.to_nodal(orography) |
| self.geopotential_fn = functools.partial( |
| primitive_equations.get_geopotential_with_moisture, |
| nodal_orography=self.nodal_orography, |
| coordinates=coords.vertical, |
| gravity_acceleration=physics_specs.gravity_acceleration, |
| ideal_gas_constant=physics_specs.ideal_gas_constant, |
| water_vapor_gas_constant=physics_specs.water_vapor_gas_constant, |
| ) |
| self.transform_fn = transform_module( |
| coords, dt, physics_specs, aux_features, output_coords |
| ) |
|
|
| def primitive_to_weatherbench( |
| self, |
| inputs: primitive_equations.StateWithTime, |
| ) -> weatherbench_utils.State: |
| """Converts pe_state to weatherbench state on pressure levels.""" |
| |
| to_nodal_fn = self.output_coords.horizontal.to_nodal |
| u, v = self.velocity_fn( |
| vorticity=inputs.vorticity, divergence=inputs.divergence |
| ) |
| t = self.ref_temps + to_nodal_fn(inputs.temperature_variation) |
| tracers = to_nodal_fn(inputs.tracers) |
| z = self.geopotential_fn(t, tracers['specific_humidity']) |
| surface_pressure = jnp.exp(to_nodal_fn(inputs.log_surface_pressure)) |
| u, v, t, z, tracers, surface_pressure = ( |
| self.coords.dycore_to_physics_sharding( |
| (u, v, t, z, tracers, surface_pressure) |
| ) |
| ) |
| interpolate_with_linear_extrap_fn = ( |
| vertical_interpolation.vectorize_vertical_interpolation( |
| vertical_interpolation.linear_interp_with_linear_extrap |
| ) |
| ) |
| interpolate_with_constant_extrap_fn = ( |
| vertical_interpolation.vectorize_vertical_interpolation( |
| vertical_interpolation.vertical_interpolation |
| ) |
| ) |
| regrid_with_linear_fn = functools.partial( |
| vertical_interpolation.interp_sigma_to_pressure, |
| pressure_coords=self.output_coords.vertical, |
| sigma_coords=self.coords.vertical, |
| surface_pressure=surface_pressure, |
| interpolate_fn=interpolate_with_linear_extrap_fn, |
| ) |
| regrid_with_constant_fn = functools.partial( |
| vertical_interpolation.interp_sigma_to_pressure, |
| pressure_coords=self.output_coords.vertical, |
| sigma_coords=self.coords.vertical, |
| surface_pressure=surface_pressure, |
| interpolate_fn=interpolate_with_constant_extrap_fn, |
| ) |
| |
| |
| |
| return weatherbench_utils.State( |
| u=regrid_with_constant_fn(u), |
| v=regrid_with_constant_fn(v), |
| t=regrid_with_linear_fn(t), |
| z=regrid_with_linear_fn(z), |
| sim_time=inputs.sim_time, |
| tracers=regrid_with_constant_fn(tracers), |
| ) |
|
|
| def __call__( |
| self, inputs: ModelState, forcing: Forcing |
| ) -> DataState: |
| del forcing |
| wb_on_sigma = self.primitive_to_weatherbench(inputs.state) |
| return self.transform_fn(wb_on_sigma.asdict()) |
|
|
|
|
| _DECODER_SALT = zlib.crc32(b'decoder') |
|
|
|
|
| def _decoder_prng_key( |
| randomness: typing.RandomnessState, |
| ) -> typing.PRNGKeyArray | None: |
| """Get a PRNG Key suitable for decoder randomness.""" |
| if randomness.prng_key is None: |
| return None |
| salt = jnp.uint32(_DECODER_SALT) + jnp.uint32(randomness.prng_step) |
| return jax.random.fold_in(randomness.prng_key, salt) |
|
|
|
|
| @gin.register |
| class LearnedPrimitiveToWeatherbenchDecoder(PrimitiveToWeatherbenchDecoder): |
| """Similar to `PrimitiveToWeatherbenchDecoder` with learned interpolation.""" |
|
|
| def __init__( |
| self, |
| coords: coordinate_systems.CoordinateSystem, |
| dt: float, |
| physics_specs: Any, |
| aux_features: Dict[str, Any], |
| output_coords: coordinate_systems.CoordinateSystem, |
| modal_to_nodal_model_features_module: FeaturesModule, |
| modal_to_nodal_data_features_module: FeaturesModule, |
| correction_transform_module: TransformModule, |
| nodal_mapping_module: MappingModule, |
| prediction_mask: typing.Pytree, |
| time_axis: int = 0, |
| orography_module: OrographyModule = orographies.ClippedOrography, |
| transform_module: TransformModule = DecoderIdentityTransform, |
| randomness_module: RandomnessModule = stochastic.ZerosRandomField, |
| perturbation_module: PerturbationModule = perturbations.NoPerturbation, |
| diagnostics_module: DiagnosticModule = diagnostics.NoDiagnostics, |
| name: Optional[str] = None, |
| ): |
| super().__init__( |
| coords=coords, |
| dt=dt, |
| physics_specs=physics_specs, |
| aux_features=aux_features, |
| output_coords=output_coords, |
| time_axis=time_axis, |
| orography_module=orography_module, |
| name=name, |
| ) |
| self.prediction_mask = prediction_mask |
| |
| self.model_features_fn = modal_to_nodal_model_features_module( |
| coords, dt, physics_specs, aux_features |
| ) |
| self.data_features_fn = modal_to_nodal_data_features_module( |
| output_coords, dt, physics_specs, aux_features |
| ) |
| self.corrections_transform_fn = correction_transform_module( |
| coords, dt, physics_specs, aux_features |
| ) |
| |
| self.nodal_mapping_module = nodal_mapping_module |
| self.get_nodal_shape_fn = lambda x: coordinate_systems.get_nodal_shapes( |
| x, output_coords |
| ) |
| self.transform_fn = transform_module( |
| coords, dt, physics_specs, aux_features, output_coords |
| ) |
| self.randomness_fn = randomness_module( |
| coords, dt, physics_specs, aux_features |
| ) |
| self.perturbation_fn = perturbation_module( |
| coords, dt, physics_specs, aux_features |
| ) |
| self.diagnostic_fn = diagnostics_module( |
| coords, dt, physics_specs, aux_features |
| ) |
|
|
| def __call__( |
| self, inputs: ModelState, forcing: Forcing |
| ) -> DataState: |
| randomness = self.randomness_fn.unconditional_sample( |
| _decoder_prng_key(inputs.randomness) |
| ) |
| prognostics = self.perturbation_fn( |
| inputs=self.coords.with_dycore_sharding(inputs.state), |
| state=None, |
| randomness=self.coords.with_dycore_sharding(randomness.nodal_value), |
| ) |
| inputs.state = prognostics |
|
|
| |
| |
| decoder_diagnostics = self.diagnostic_fn(inputs, None) |
| wb_on_pressure_dict = self.primitive_to_weatherbench(prognostics).asdict() |
| wb_on_pressure_modal = coordinate_systems.maybe_to_modal( |
| self.coords.physics_to_dycore_sharding(wb_on_pressure_dict), |
| self.output_coords, |
| ) |
| wb_on_pressure_dict['diagnostics'] = decoder_diagnostics |
| prediction_mask = pytree_utils.replace_with_matching_or_default( |
| wb_on_pressure_dict, self.prediction_mask, default=False) |
| prediction_shapes = jax.tree_util.tree_map( |
| lambda x, y: self.get_nodal_shape_fn(x) if y else None, |
| wb_on_pressure_dict, |
| prediction_mask, |
| ) |
| net = self.nodal_mapping_module(prediction_shapes) |
| model_features = self.model_features_fn( |
| prognostics.asdict(), forcing=forcing, |
| randomness=randomness.nodal_value |
| ) |
| data_features = self.data_features_fn(wb_on_pressure_modal, forcing=forcing) |
| data_features = transforms.add_prefix(data_features, 'data_') |
| model_features = transforms.add_prefix(model_features, 'model_') |
| all_features = self.coords.dycore_to_physics_sharding( |
| data_features | model_features |
| ) |
|
|
| nodal_outputs = self.corrections_transform_fn(net(all_features)) |
| add_fn = lambda x, y: x + y if y is not None else x |
| wb_on_pressure_dict = jax.tree_util.tree_map( |
| add_fn, wb_on_pressure_dict, nodal_outputs |
| ) |
| return self.transform_fn(wb_on_pressure_dict) |
|
|
|
|
| @gin.register |
| class DimensionalPrimitiveToWeatherbenchDecoder(PrimitiveToWeatherbenchDecoder): |
| """Same as PrimitiveToWeatherbenchDecoder, but with dimensional output.""" |
|
|
| def __init__( |
| self, |
| coords: coordinate_systems.CoordinateSystem, |
| dt: float, |
| physics_specs: Any, |
| aux_features: Dict[str, Any], |
| output_coords: coordinate_systems.CoordinateSystem, |
| inputs_to_units_mapping: Dict[str, str], |
| time_axis: int = 0, |
| orography_module: OrographyModule = orographies.ClippedOrography, |
| transform_module: TransformModule = DecoderIdentityTransform, |
| name: Optional[str] = None, |
| ): |
| nondim_pressure_centers = physics_specs.nondimensionalize( |
| output_coords.vertical.centers * scales.units.millibar |
| ) |
| nondim_output_coords = coordinate_systems.CoordinateSystem( |
| output_coords.horizontal, |
| vertical_interpolation.PressureCoordinates(nondim_pressure_centers), |
| spmd_mesh=output_coords.spmd_mesh, |
| ) |
| super().__init__( |
| coords, |
| dt, |
| physics_specs, |
| aux_features, |
| output_coords=nondim_output_coords, |
| time_axis=time_axis, |
| orography_module=orography_module, |
| transform_module=transform_module, |
| name=name, |
| ) |
| self.redimensionalize_fn = transforms.RedimensionalizeTransform( |
| coords, |
| dt, |
| physics_specs, |
| aux_features, |
| output_coords=output_coords, |
| inputs_to_units_mapping=inputs_to_units_mapping, |
| ) |
|
|
| def __call__( |
| self, inputs: ModelState, forcing: Forcing |
| ) -> DataState: |
| return self.redimensionalize_fn(super().__call__(inputs, forcing)) |
|
|
|
|
| @gin.configurable |
| class DimensionalLearnedPrimitiveToWeatherbenchDecoder( |
| LearnedPrimitiveToWeatherbenchDecoder |
| ): |
| """Same as LearnedPrimitiveToWeatherbenchDecoder, but with dimensional output.""" |
|
|
| def __init__( |
| self, |
| coords: coordinate_systems.CoordinateSystem, |
| dt: float, |
| physics_specs: Any, |
| aux_features: Dict[str, Any], |
| output_coords: coordinate_systems.CoordinateSystem, |
| modal_to_nodal_model_features_module: FeaturesModule, |
| modal_to_nodal_data_features_module: FeaturesModule, |
| nodal_mapping_module: MappingModule, |
| correction_transform_module: TransformModule, |
| prediction_mask: typing.Pytree, |
| inputs_to_units_mapping: Dict[str, str], |
| time_axis: int = 0, |
| orography_module: OrographyModule = orographies.ClippedOrography, |
| transform_module: TransformModule = DecoderIdentityTransform, |
| randomness_module: RandomnessModule = stochastic.ZerosRandomField, |
| perturbation_module: PerturbationModule = perturbations.NoPerturbation, |
| diagnostics_module: DiagnosticModule = diagnostics.NoDiagnostics, |
| name: Optional[str] = None, |
| ): |
| nondim_pressure_centers = physics_specs.nondimensionalize( |
| output_coords.vertical.centers * scales.units.millibar |
| ) |
| nondim_output_coords = coordinate_systems.CoordinateSystem( |
| output_coords.horizontal, |
| vertical_interpolation.PressureCoordinates(nondim_pressure_centers), |
| spmd_mesh=output_coords.spmd_mesh, |
| ) |
| super().__init__( |
| coords, |
| dt, |
| physics_specs, |
| aux_features, |
| output_coords=nondim_output_coords, |
| modal_to_nodal_model_features_module=( |
| modal_to_nodal_model_features_module |
| ), |
| modal_to_nodal_data_features_module=modal_to_nodal_data_features_module, |
| nodal_mapping_module=nodal_mapping_module, |
| correction_transform_module=correction_transform_module, |
| prediction_mask=prediction_mask, |
| time_axis=time_axis, |
| orography_module=orography_module, |
| transform_module=transform_module, |
| randomness_module=randomness_module, |
| perturbation_module=perturbation_module, |
| diagnostics_module=diagnostics_module, |
| name=name, |
| ) |
| self.redimensionalize_fn = transforms.RedimensionalizeTransform( |
| coords, |
| dt, |
| physics_specs, |
| aux_features, |
| output_coords=output_coords, |
| inputs_to_units_mapping=inputs_to_units_mapping, |
| ) |
|
|
| def __call__( |
| self, inputs: ModelState, forcing: Forcing |
| ) -> DataState: |
| return self.redimensionalize_fn(super().__call__(inputs, forcing)) |
|
|