Skip to content
Merged
Show file tree
Hide file tree
Changes from 15 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
111 changes: 2 additions & 109 deletions pyfv3/_config.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,10 @@
import dataclasses
from datetime import timedelta
from math import floor
from typing import Optional, Tuple
from typing import Optional

import f90nml
import yaml

from ndsl.namelist import Namelist


DEFAULT_INT = 0
DEFAULT_STR = ""
Expand Down Expand Up @@ -195,7 +192,7 @@ class DynamicalCoreConfig:
vtdm4: float = DEFAULT_FLOAT
z_tracer: bool = DEFAULT_BOOL
do_qa: bool = DEFAULT_BOOL
layout: Tuple[int, int] = (1, 1)
layout: tuple[int, int] = (1, 1)
grid_type: int = 0
u_max: float = 350.0
"""max windspeed for dp config"""
Expand Down Expand Up @@ -280,114 +277,10 @@ class DynamicalCoreConfig:
namelist_override: Optional[str] = None

def __post_init__(self):
if self.namelist_override is not None:
try:
f90_nml = f90nml.read(self.namelist_override)
except FileNotFoundError:
print(f"{self.namelist_override} does not exist")
raise
dycore_config = self.from_f90nml(f90_nml)
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)

@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,
)

@classmethod
def from_yaml(cls, yaml_config: str) -> "DynamicalCoreConfig":
config = cls()
Expand Down
10 changes: 6 additions & 4 deletions pyfv3/testing/translate_dyncore.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
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
from pyfv3.dycore_state import DycoreState
from pyfv3.stencils import dyn_core
from pyfv3.utils.namelist import dycore_config_from_f90nml


class TranslateDynCore(ParallelTranslate2PyState):
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 = dycore_config_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
11 changes: 6 additions & 5 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 @@ -17,9 +18,9 @@
from ndsl.grid import GridData
from ndsl.performance import NullTimer
from ndsl.stencils.testing import ParallelTranslateBaseSlicing, TranslateFortranData2Py
from pyfv3._config import DynamicalCoreConfig
from pyfv3.dycore_state import DycoreState
from pyfv3.stencils import fv_dynamics
from pyfv3.utils.namelist import dycore_config_from_f90nml


class TranslateDycoreFortranData2Py(TranslateFortranData2Py):
Expand All @@ -30,7 +31,7 @@ def __init__(
stencil_factory: StencilFactory,
):
super().__init__(grid, stencil_factory)
self.namelist = DynamicalCoreConfig.from_namelist(namelist)
self.config = dycore_config_from_f90nml(namelist)


class TranslateFVDynamics(ParallelTranslateBaseSlicing):
Expand Down Expand Up @@ -295,7 +296,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 = dycore_config_from_f90nml(namelist)

def state_from_inputs(self, inputs):
input_storages = super().state_from_inputs(inputs)
Expand Down Expand Up @@ -337,7 +338,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
60 changes: 60 additions & 0 deletions pyfv3/utils/namelist.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
from dacite import Config, from_dict
from f90nml import Namelist

from ndsl.utils import f90nml_as_dict, load_f90nml_as_dict
from pyfv3._config import DynamicalCoreConfig


DEFAULT_DYCORE_NML_GROUPS = (
"main_nml",
"coupler_nml",
"fv_core_nml",
)


def dycore_config_from_f90nml(
nml: Namelist,
use_default_groups: bool = True,
target_groups: list[str] | None = None,
) -> DynamicalCoreConfig:
"""Uses the nml to create a DynamicalCoreConfig.
Only the DEFAULT_DYCORE_NML_GROUPS from the nml are considered
when initializing the DynamicalCoreConfig. If the nml
has a 'namelist_override' key, then that will be used to
load an additional namelist file to override the
DynamicalCoreConfig values.

Args:
nml: f90nml.Namelist
use_default_groups: bool. If True, the DEFAULT_DYCORE_NML_GROUPS
will be used for initializing the config. Otherwise,
parameters from the target_groups will be used to initialize
the DynaicalCoreConfig instead. (Default: True)
target_groups: list[str] | None. If use_default_groups is False,
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: None)
If use_default_groups is True, this parameter is ignored.
"""
if use_default_groups:
target_groups = DEFAULT_DYCORE_NML_GROUPS
nml_dict = f90nml_as_dict(nml, flatten=True, target_groups=target_groups)
dacite_config = Config(type_hooks={tuple[int, int]: tuple[int, int]})
dycore_config = from_dict(
data_class=DynamicalCoreConfig, data=nml_dict, config=dacite_config
)

# Override parameters if a namelist_override exists.
# NOTE: We're doing the patching of the dycore based on the namelist_override
# here, rather than DynamicalCoreConfig.__post_init__ as one way to avoid
# circular dependencies. At this point, I don't know which group might
# have the namelist_override defined, so I'm loading it and patching
# directly rather than using f90nml.Namelist.patch()
if dycore_config.namelist_override is not None:
override_dict = load_f90nml_as_dict(
dycore_config.namelist_override, flatten=True, target_groups=target_groups
)
for key, value in override_dict.items():
if key in dycore_config.__dataclass_fields__:
setattr(dycore_config, key, value)
return dycore_config
11 changes: 10 additions & 1 deletion pyfv3/wrappers/geos_wrapper.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
from ndsl.logging import ndsl_log
from ndsl.optional_imports import cupy as cp
from ndsl.utils import safe_assign_array
from pyfv3.utils.namelist import dycore_config_from_f90nml


class StencilBackendCompilerOverride:
Expand Down Expand Up @@ -116,7 +117,15 @@ 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 = dycore_config_from_f90nml(
namelist,
use_default_groups=False,
target_groups=None,
)
self.dycore_config.dt_atmos = bdt
assert self.dycore_config.dt_atmos != 0

Expand Down
Loading
Loading