Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
4792227
Initial changes: Namelist replaced by dict
jjuyeonkim Sep 30, 2025
1ac933d
Creating utility function dycore_config_from_f90nml for creating Dyna…
jjuyeonkim Sep 30, 2025
4269fea
Instead of ndsl.Namelist, using f90nml.Namelist + dycore_config_from_…
jjuyeonkim Oct 1, 2025
df92423
Removing from_namelist and from_f90nml from DynamicalCoreConfig; Addi…
jjuyeonkim Oct 3, 2025
3b65f73
GEOS wrapper work around
jjuyeonkim Oct 3, 2025
d46eefe
Using self.config rather than self.namelist in TranslateDynCore
jjuyeonkim Oct 3, 2025
d0fcf5f
Linting and small tweaks
jjuyeonkim Oct 3, 2025
7a81f37
Adding --f90nml_namelist_only flag
jjuyeonkim Oct 3, 2025
9ea785b
more linting + comment revisions
jjuyeonkim Oct 3, 2025
917b4e7
Changed translate test flag: --legacy_namelist_support=False
jjuyeonkim Oct 6, 2025
df59228
Using --no_legacy_namelist flag for translate tests
jjuyeonkim Oct 6, 2025
b605a35
Adding target_groups to be parameter for dycore_config_from_f90nml
jjuyeonkim Oct 6, 2025
676f7fd
Cleanup: mainly using f90nml.Namelist where appropriate,
jjuyeonkim Oct 7, 2025
7b349da
Simple unit test for namelist_override + comment tweaks
jjuyeonkim Oct 9, 2025
e1937a6
Merge branch 'develop' into 20250925_namelist_helper
jjuyeonkim Oct 9, 2025
ff7fe0b
Adding from_f90nml back to DynamicalCoreConfig
jjuyeonkim Oct 10, 2025
73265cc
DynamicalCoreConfig from_dict, from_f90nml mods
jjuyeonkim Oct 15, 2025
f20d7aa
Merge branch 'develop' into 20250925_namelist_helper
jjuyeonkim Oct 15, 2025
1e3364f
Removing simple unit test
jjuyeonkim Oct 16, 2025
bd1c3a9
Adding self.config to TranslateCubedToLatLon
jjuyeonkim Oct 17, 2025
ec9acea
Update pyfv3/_config.py
jjuyeonkim Oct 17, 2025
a77a7a2
Comment fix
jjuyeonkim Oct 17, 2025
ad7f555
Merge branch '20250925_namelist_helper' of github.com:jjuyeonkim/PyFV…
jjuyeonkim Oct 17, 2025
9202d16
Using 'from __future__ import annotations'
jjuyeonkim Oct 17, 2025
8beb1cd
Adding note about fishy behavior in dycore config post init
jjuyeonkim Oct 17, 2025
3e3abda
Adding self.config into translate tests; Moving TranslateDycoreFortra…
jjuyeonkim Oct 20, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .github/workflows/translate.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ jobs:
--backend=numpy \
--which_modules=FvTp2d \
--threshold_overrides_file=./tests/savepoint/translate/overrides/standard.yaml \
--no_legacy_namelist \
./tests/savepoint

- name: Numpy D_SW
Expand All @@ -100,6 +101,7 @@ jobs:
--backend=numpy \
--which_modules=D_SW \
--threshold_overrides_file=./tests/savepoint/translate/overrides/standard.yaml \
--no_legacy_namelist \
./tests/savepoint

- name: Numpy Remapping
Expand All @@ -110,6 +112,7 @@ jobs:
--backend=numpy \
--which_modules=Remapping \
--threshold_overrides_file=./tests/savepoint/translate/overrides/standard.yaml \
--no_legacy_namelist \
./tests/savepoint

- name: Orchestrated dace-cpu Acoustics
Expand All @@ -126,4 +129,5 @@ jobs:
--which_rank=0 \
--which_modules=DynCore \
--threshold_overrides_file=./tests/savepoint/translate/overrides/standard.yaml \
--no_legacy_namelist \
./tests/savepoint
146 changes: 53 additions & 93 deletions pyfv3/_config.py
Original file line number Diff line number Diff line change
@@ -1,18 +1,26 @@
from __future__ import annotations

import dataclasses
from datetime import timedelta
from math import floor
from typing import Optional, Tuple

import f90nml
import yaml
from dacite import Config, from_dict

from ndsl.namelist import Namelist
from ndsl.utils import f90nml_as_dict


DEFAULT_INT = 0
DEFAULT_STR = ""
DEFAULT_FLOAT = 0.0
DEFAULT_BOOL = False
DEFAULT_DYCORE_NML_GROUPS = (
"main_nml",
"coupler_nml",
"fv_core_nml",
)


@dataclasses.dataclass(frozen=True)
Expand Down Expand Up @@ -278,6 +286,7 @@ class DynamicalCoreConfig:
sw_dynamics: bool = False
"""shallow water conditions"""
namelist_override: Optional[str] = None
target_nml_groups: Optional[Tuple[str, ...]] = DEFAULT_DYCORE_NML_GROUPS

def __post_init__(self):
if self.namelist_override is not None:
Expand All @@ -286,110 +295,61 @@ def __post_init__(self):
except FileNotFoundError:
print(f"{self.namelist_override} does not exist")
raise
dycore_config = self.from_f90nml(f90_nml)
# TODO: Find a better way to do below. Passing self.* as an argument
# to a class function of the same class is always a bit fishy.
dycore_config = self.from_f90nml(f90_nml, self.target_nml_groups)
Comment thread
romanc marked this conversation as resolved.
for var in dycore_config.__dict__.keys():
setattr(self, var, dycore_config.__dict__[var])
# Single tile cartesian grids
if self.grid_type > 3:
self.nf_omega = 0

@classmethod
def from_f90nml(cls, f90_namelist: f90nml.Namelist) -> "DynamicalCoreConfig":
namelist = Namelist.from_f90nml(f90_namelist)
return cls.from_namelist(namelist)
def from_f90nml(
cls,
nml: f90nml.Namelist,
target_groups: Tuple[str, ...] | None = DEFAULT_DYCORE_NML_GROUPS,
) -> DynamicalCoreConfig:
"""Uses the nml to create a DynamicalCoreConfig.

Args:
nml: f90nml.Namelist
target_groups: Tuple[str,...] | None
This list will be used to specify which groups in the nml to
use when initializing the DynamicalCoreConfig. If None, all
groups will be used. (Default: DEFAULT_DYCORE_NML_GROUPS)
"""
nml_dict = f90nml_as_dict(nml, flatten=True, target_groups=target_groups)
nml_dict["target_nml_groups"] = target_groups
return cls.from_dict(nml_dict)

@classmethod
def from_namelist(cls, namelist: Namelist) -> "DynamicalCoreConfig":
return cls(
dt_atmos=namelist.dt_atmos,
a_imp=namelist.a_imp,
beta=namelist.beta,
consv_te=namelist.consv_te,
d2_bg=namelist.d2_bg,
d2_bg_k1=namelist.d2_bg_k1,
d2_bg_k2=namelist.d2_bg_k2,
d4_bg=namelist.d4_bg,
d_con=namelist.d_con,
d_ext=namelist.d_ext,
dddmp=namelist.dddmp,
delt_max=namelist.delt_max,
do_sat_adj=namelist.do_sat_adj,
do_vort_damp=namelist.do_vort_damp,
fill=namelist.fill,
hord_dp=namelist.hord_dp,
hord_mt=namelist.hord_mt,
hord_tm=namelist.hord_tm,
hord_tr=namelist.hord_tr,
hord_vt=namelist.hord_vt,
hydrostatic=namelist.hydrostatic,
k_split=namelist.k_split,
ke_bg=namelist.ke_bg,
kord_mt=namelist.kord_mt,
kord_tm=namelist.kord_tm,
kord_tr=namelist.kord_tr,
kord_wz=namelist.kord_wz,
n_split=namelist.n_split,
nord=namelist.nord,
npx=namelist.npx,
npy=namelist.npy,
npz=namelist.npz,
ntiles=namelist.ntiles,
nwat=namelist.nwat,
p_fac=namelist.p_fac,
rf_cutoff=namelist.rf_cutoff,
tau=namelist.tau,
vtdm4=namelist.vtdm4,
z_tracer=namelist.z_tracer,
do_qa=namelist.do_qa,
layout=namelist.layout,
grid_type=namelist.grid_type,
u_max=namelist.u_max,
do_f3d=namelist.do_f3d,
inline_q=namelist.inline_q,
do_skeb=namelist.do_skeb,
check_negative=namelist.check_negative,
tau_r2g=namelist.tau_r2g,
tau_smlt=namelist.tau_smlt,
tau_g2r=namelist.tau_g2r,
tau_imlt=namelist.tau_imlt,
tau_i2s=namelist.tau_i2s,
tau_l2r=namelist.tau_l2r,
tau_g2v=namelist.tau_g2v,
tau_v2g=namelist.tau_v2g,
sat_adj0=namelist.sat_adj0,
ql_gen=namelist.ql_gen,
ql_mlt=namelist.ql_mlt,
qs_mlt=namelist.qs_mlt,
ql0_max=namelist.ql0_max,
t_sub=namelist.t_sub,
qi_gen=namelist.qi_gen,
qi_lim=namelist.qi_lim,
qi0_max=namelist.qi0_max,
rad_snow=namelist.rad_snow,
rad_rain=namelist.rad_rain,
rad_graupel=namelist.rad_graupel,
tintqs=namelist.tintqs,
dw_ocean=namelist.dw_ocean,
dw_land=namelist.dw_land,
icloud_f=namelist.icloud_f,
cld_min=namelist.cld_min,
tau_l2v=namelist.tau_l2v,
tau_v2l=namelist.tau_v2l,
c2l_ord=namelist.c2l_ord,
regional=namelist.regional,
m_split=namelist.m_split,
convert_ke=namelist.convert_ke,
breed_vortex_inline=namelist.breed_vortex_inline,
use_old_omega=namelist.use_old_omega,
rf_fast=namelist.rf_fast,
adiabatic=namelist.adiabatic,
nf_omega=namelist.nf_omega,
fv_sg_adj=namelist.fv_sg_adj,
n_sponge=namelist.n_sponge,
def from_dict(
cls,
data: dict,
) -> DynamicalCoreConfig:
"""Create a DynamicalCoreConfig from the given data.

Args:
data: "flattened" dictionary where the keys match the class member variables
"""
# NOTE: We're setting strict to False so that extra keys in the data are
# ignored. Eventually, we'd like to turn this to True once we move away from
# expecting dicts that are basically flattened yamls and f90nml files.
dacite_config = Config(
strict=False,
type_hooks={
Tuple[int, int]: lambda x: tuple(x),
Tuple[str, ...]: lambda x: tuple(x) if x is not None else None,
},
)
dycore_config = from_dict(
data_class=DynamicalCoreConfig, data=data, config=dacite_config
)
return dycore_config

@classmethod
def from_yaml(cls, yaml_config: str) -> "DynamicalCoreConfig":
def from_yaml(cls, yaml_config: str) -> DynamicalCoreConfig:
config = cls()
with open(yaml_config, "r") as f:
raw_config = yaml.safe_load(f)
Expand Down
3 changes: 2 additions & 1 deletion pyfv3/testing/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from .translate_data import TranslateDycoreFortranData2Py
from .translate_dyncore import TranslateDynCore
from .translate_fvdynamics import TranslateDycoreFortranData2Py, TranslateFVDynamics
from .translate_fvdynamics import TranslateFVDynamics
from .validation import enable_selective_validation


Expand Down
16 changes: 16 additions & 0 deletions pyfv3/testing/translate_data.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
from f90nml import Namelist

from ndsl import StencilFactory
from ndsl.stencils.testing import TranslateFortranData2Py
from pyfv3._config import DynamicalCoreConfig


class TranslateDycoreFortranData2Py(TranslateFortranData2Py):
def __init__(
self,
grid,
namelist: Namelist,
stencil_factory: StencilFactory,
):
super().__init__(grid, stencil_factory)
self.config = DynamicalCoreConfig.from_f90nml(namelist)
8 changes: 5 additions & 3 deletions pyfv3/testing/translate_dyncore.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
from f90nml import Namelist

import ndsl.dsl.gt4py_utils as utils
from ndsl import Namelist, Quantity, StencilFactory
from ndsl import Quantity, StencilFactory
from ndsl.constants import X_DIM, X_INTERFACE_DIM, Y_DIM, Y_INTERFACE_DIM, Z_DIM
from ndsl.stencils.testing import ParallelTranslate2PyState
from pyfv3._config import DynamicalCoreConfig
Expand Down Expand Up @@ -121,7 +123,7 @@ def __init__(
self.max_error = 2e-6
self.ignore_near_zero_errors["wsd"] = 1e-18
self.stencil_factory = stencil_factory
self.namelist = namelist
self.config = DynamicalCoreConfig.from_f90nml(namelist)

def compute_parallel(self, inputs, communicator):
# ak, bk, and phis are numpy arrays at this point and
Expand Down Expand Up @@ -167,7 +169,7 @@ def compute_parallel(self, inputs, communicator):
grid_type=self.grid.grid_type,
nested=self.grid.nested,
stretched_grid=self.grid.stretched_grid,
config=DynamicalCoreConfig.from_namelist(self.namelist).acoustic_dynamics,
config=self.config.acoustic_dynamics,
phis=phis,
wsd=wsd.data,
state=state,
Expand Down
20 changes: 5 additions & 15 deletions pyfv3/testing/translate_fvdynamics.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,10 @@
from typing import Any, Dict, Optional, Tuple

import pytest
from f90nml import Namelist

import ndsl.dsl.gt4py_utils as utils
from ndsl import Namelist, Quantity, StencilFactory
from ndsl import Quantity, StencilFactory
from ndsl.constants import (
X_DIM,
X_INTERFACE_DIM,
Expand All @@ -16,23 +17,12 @@
)
from ndsl.grid import GridData
from ndsl.performance import NullTimer
from ndsl.stencils.testing import ParallelTranslateBaseSlicing, TranslateFortranData2Py
from ndsl.stencils.testing import ParallelTranslateBaseSlicing
from pyfv3._config import DynamicalCoreConfig
from pyfv3.dycore_state import DycoreState
from pyfv3.stencils import fv_dynamics


class TranslateDycoreFortranData2Py(TranslateFortranData2Py):
def __init__(
self,
grid,
namelist: Namelist,
stencil_factory: StencilFactory,
):
super().__init__(grid, stencil_factory)
self.namelist = DynamicalCoreConfig.from_namelist(namelist)


class TranslateFVDynamics(ParallelTranslateBaseSlicing):
compute_grid_option = True
inputs: Dict[str, Any] = {
Expand Down Expand Up @@ -295,7 +285,7 @@ def __init__(
self.ignore_near_zero_errors["q_con"] = True
self.dycore: Optional[fv_dynamics.DynamicalCore] = None
self.stencil_factory = stencil_factory
self.namelist: DynamicalCoreConfig = DynamicalCoreConfig.from_namelist(namelist)
self.config = DynamicalCoreConfig.from_f90nml(namelist)

def state_from_inputs(self, inputs):
input_storages = super().state_from_inputs(inputs)
Expand Down Expand Up @@ -337,7 +327,7 @@ def compute_parallel(self, inputs, communicator):
stencil_factory=self.stencil_factory,
quantity_factory=self.grid.quantity_factory,
damping_coefficients=self.grid.damping_coefficients,
config=DynamicalCoreConfig.from_namelist(self.namelist),
config=self.config,
phis=state.phis,
state=state,
timestep=timedelta(seconds=inputs["bdt"]),
Expand Down
9 changes: 8 additions & 1 deletion pyfv3/wrappers/geos_wrapper.py
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,14 @@ def __init__(

self.backend = backend
self.namelist = namelist
self.dycore_config = pyfv3.DynamicalCoreConfig.from_f90nml(self.namelist)
# TODO: After pace unit tests have been updated, or universal
# loader from namelist/yaml has been implemented, create a
# dycore_config using default groups or creation from yaml.
# This is a temporary work-around for now.
self.dycore_config = pyfv3.DynamicalCoreConfig.from_f90nml(
self.namelist,
target_groups=None,
)
self.dycore_config.dt_atmos = bdt
assert self.dycore_config.dt_atmos != 0

Expand Down
15 changes: 8 additions & 7 deletions tests/savepoint/translate/translate_a2b_ord4.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
from typing import Any, Dict

from ndsl import Namelist, StencilFactory, orchestrate
from f90nml import Namelist

from ndsl import StencilFactory, orchestrate
from ndsl.constants import Z_DIM
from pyfv3.stencils import DivergenceDamping
from pyfv3.testing import TranslateDycoreFortranData2Py
Expand Down Expand Up @@ -51,11 +53,10 @@ def __init__(
stencil_factory: StencilFactory,
):
super().__init__(grid, namelist, stencil_factory)
assert namelist.grid_type < 3
assert self.config.grid_type < 3
self.in_vars["data_vars"] = {"wk": {}, "vort": {}, "delpc": {}, "nord_col": {}}
self.in_vars["parameters"] = ["dt"]
self.out_vars: Dict[str, Any] = {"wk": {}, "vort": {}}
self.namelist = namelist # type: ignore
self.stencil_factory = stencil_factory
self.compute_obj = A2B_Ord4Compute(stencil_factory)

Expand All @@ -69,10 +70,10 @@ def compute_from_storage(self, inputs):
self.grid.damping_coefficients,
self.grid.nested,
self.grid.stretched_grid,
self.namelist.dddmp,
self.namelist.d4_bg,
self.namelist.nord,
self.namelist.grid_type,
self.config.dddmp,
self.config.d4_bg,
self.config.nord,
self.config.grid_type,
nord_col,
nord_col,
)
Expand Down
Loading