Skip to content

Commit 95c62c0

Browse files
authored
Merge pull request #64 from jjuyeonkim/20250618_baroclinic12_ss_2_clean
Baroclinic Steady Test Case Initialization
2 parents 63f8e88 + bb91ed2 commit 95c62c0

File tree

4 files changed

+86
-31
lines changed

4 files changed

+86
-31
lines changed

.github/workflows/pace_tests.yaml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
11
# TODO: Re-activate after PR 60
2+
# TODO: Temporarily removing tests due to dependent changes in
3+
# Pace#129, PyFV3#64 PRs; tests should be reverted back after
4+
# these PRs are merged.
5+
26
# name: "pace main tests"
37
# on:
48
# pull_request:

pyfv3/initialization/analytic_init.py

Lines changed: 31 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -9,40 +9,43 @@
99
from pyfv3.dycore_state import DycoreState
1010

1111

12-
class Cases(Enum, metaclass=MetaEnumStr):
13-
baroclinic = "baroclinic"
12+
class AnalyticCase(Enum, metaclass=MetaEnumStr):
13+
baroclinic_instability = "baroclinic_instability"
14+
baroclinic_steady = "baroclinic_steady"
1415
rossby = "rossby"
1516
tropicalcyclone = "tropicalcyclone"
1617

1718

1819
def init_analytic_state(
19-
analytic_init_case: str,
20+
analytic_init_case: AnalyticCase,
2021
grid_data: GridData,
2122
quantity_factory: QuantityFactory,
2223
adiabatic: bool,
2324
hydrostatic: bool,
2425
moist_phys: bool,
26+
sw_dynamics: bool,
2527
comm: Communicator,
2628
) -> DycoreState:
2729
"""
2830
This method initializes the chosen analytic test case type
2931
Args:
30-
analytic_init_str: test case specifier
32+
analytic_init_case: test case specifier
3133
grid_data: current selected grid data values
3234
quantity_factory: inclusion of QuantityFactory class
3335
adiabatic: flag for adiabatic methods
3436
hydrostatic: flag for hydrostatic methods
3537
moist_phys: flag for including moisture physics methods
38+
sw_dynamics: flag for shallow water conditions (e.g., rossby)
3639
comm: inclusion of CubedSphereCommunicator class
3740
3841
Returns:
3942
an instance of DycoreState class
4043
"""
41-
# Cases that expect Cubed Sphere Communicator
4244
spherical_cases = [
43-
Cases.baroclinic.value,
44-
Cases.tropicalcyclone.value,
45-
Cases.rossby.value,
45+
AnalyticCase.baroclinic_instability,
46+
AnalyticCase.baroclinic_steady,
47+
AnalyticCase.tropicalcyclone,
48+
AnalyticCase.rossby,
4649
]
4750

4851
if analytic_init_case in spherical_cases: # type: ignore
@@ -53,23 +56,40 @@ def init_analytic_state(
5356
f"got {type(comm).__name__} instead."
5457
)
5558

56-
if analytic_init_case == Cases.baroclinic.value: # type: ignore
59+
if analytic_init_case == AnalyticCase.baroclinic_instability: # type: ignore
5760
return bc.init_baroclinic_state(
5861
grid_data=grid_data,
5962
quantity_factory=quantity_factory,
6063
adiabatic=adiabatic,
6164
hydrostatic=hydrostatic,
6265
moist_phys=moist_phys,
66+
is_steady=False,
6367
comm=comm,
6468
)
65-
elif analytic_init_case == Cases.tropicalcyclone.value: # type: ignore
69+
elif analytic_init_case == AnalyticCase.baroclinic_steady: # type: ignore
70+
return bc.init_baroclinic_state(
71+
grid_data=grid_data,
72+
quantity_factory=quantity_factory,
73+
adiabatic=adiabatic,
74+
hydrostatic=hydrostatic,
75+
moist_phys=moist_phys,
76+
is_steady=True,
77+
comm=comm,
78+
)
79+
elif analytic_init_case == AnalyticCase.tropicalcyclone: # type: ignore
6680
return tc.init_tc_state(
6781
grid_data=grid_data,
6882
quantity_factory=quantity_factory,
6983
hydrostatic=hydrostatic,
7084
comm=comm,
7185
)
72-
elif analytic_init_case == Cases.rossby.value: # type: ignore
86+
elif analytic_init_case == AnalyticCase.rossby: # type: ignore
87+
# TODO sw_dynamics check is awkward here, and should be moved.
88+
if sw_dynamics is False:
89+
raise ValueError(
90+
"Rossby initialization requires dynamical core config "
91+
"sw_dynamics flag to be True."
92+
)
7393
return rossby.init_rossby_state(
7494
grid_data=grid_data,
7595
quantity_factory=quantity_factory,

pyfv3/initialization/test_cases/initialize_baroclinic.py

Lines changed: 41 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
1-
import math
2-
31
import numpy as np
42

53
import ndsl.constants as constants
64
import ndsl.dsl.gt4py_utils as utils
75
from ndsl import CubedSphereCommunicator, QuantityFactory
6+
from ndsl.dsl.typing import Float
87
from ndsl.grid import GridData
98
from ndsl.grid.gnomonic import great_circle_distance_lon_lat, lon_lat_midpoint
109
from pyfv3.dycore_state import DycoreState
@@ -13,22 +12,27 @@
1312

1413
# maximum windspeed amplitude - close to windspeed of zonal-mean time-mean
1514
# jet stream in troposphere
16-
U0 = 35.0 # From Table VI of DCMIP2016
15+
U0 = Float(35.0) # From Table VI of DCMIP2016
1716
# [lon, lat] of zonal wind perturbation centerpoint at 20E, 40N
18-
PCEN = [math.pi / 9.0, 2.0 * math.pi / 9.0] # From Table VI of DCMIP2016
19-
U1 = 1.0
20-
SURFACE_PRESSURE = 1.0e5 # units of (Pa), from Table VI of DCMIP2016
21-
# NOTE RADIUS = 6.3712e6 in FV3 vs Jabowski paper 6.371229e6
22-
R = constants.RADIUS / 10.0 # Perturbation radiusfor test case 13
17+
PCEN = [
18+
constants.PI / Float(9.0),
19+
Float(2.0) * constants.PI / Float(9.0),
20+
] # From Table VI of DCMIP2016
21+
SURFACE_PRESSURE = Float(1.0e5) # units of (Pa), from Table VI of DCMIP2016
2322
NHALO = constants.N_HALO_DEFAULT
2423

2524

26-
def apply_perturbation(u_component, up, lon, lat):
25+
def apply_perturbation(u_component, up, lon, lat, is_steady: bool = False):
2726
"""
2827
Apply a Gaussian perturbation to intiate a baroclinic wave in JRMS2006
2928
up is the maximum amplitude of the perturbation
3029
modifies u_component to include the perturbation of radius R
3130
"""
31+
if is_steady:
32+
R = Float(1.0) # Steady State radius for test case 12
33+
else:
34+
# NOTE RADIUS = 6.3712e6 in FV3 vs Jabowski paper 6.371229e6
35+
R = constants.RADIUS / Float(10.0) # Perturbation radius for test case 13
3236
r = np.zeros((u_component.shape[0], u_component.shape[1], 1))
3337
# Equation (11), distance from perturbation at 20E, 40N in JRMS2006
3438
r = great_circle_distance_lon_lat(PCEN[0], lon, PCEN[1], lat, constants.RADIUS, np)[
@@ -43,9 +47,21 @@ def apply_perturbation(u_component, up, lon, lat):
4347
)
4448

4549

46-
def baroclinic_perturbed_zonal_wind(eta_v, lon, lat):
50+
def baroclinic_perturbed_zonal_wind(
51+
eta_v,
52+
lon,
53+
lat,
54+
is_steady: bool = False,
55+
):
4756
u = zonal_wind(eta_v, lat)
48-
apply_perturbation(u, U1, lon, lat)
57+
58+
if is_steady:
59+
u1 = Float(0.0) # Steady State for test case 12
60+
# TODO: Check if Fortran side is actually 0 (not initialized)
61+
else:
62+
u1 = Float(1.0) # Perturbation case for test case 13
63+
64+
apply_perturbation(u, u1, lon, lat, is_steady=is_steady)
4965
return u
5066

5167

@@ -59,12 +75,16 @@ def wind_component_calc(
5975
islice_grid,
6076
jslice,
6177
jslice_grid,
78+
is_steady: bool = False,
6279
):
6380
slice_grid = (islice_grid, jslice_grid)
6481
slice_3d = (islice, jslice, slice(None))
6582
u_component = np.zeros(shape)
6683
u_component[slice_3d] = baroclinic_perturbed_zonal_wind(
67-
eta_v, lon[slice_grid], lat[slice_grid]
84+
eta_v,
85+
lon[slice_grid],
86+
lat[slice_grid],
87+
is_steady=is_steady,
6888
)
6989
u_component[slice_3d] = init_utils.local_coordinate_transformation(
7090
u_component[slice_3d],
@@ -95,6 +115,7 @@ def initialize_zonal_wind(
95115
jslice,
96116
jslice_grid,
97117
axis,
118+
is_steady: bool = False,
98119
):
99120
shape = u.shape
100121
uu1 = wind_component_calc(
@@ -107,6 +128,7 @@ def initialize_zonal_wind(
107128
islice,
108129
jslice,
109130
jslice_grid,
131+
is_steady=is_steady,
110132
)
111133
uu3 = wind_component_calc(
112134
shape,
@@ -118,6 +140,7 @@ def initialize_zonal_wind(
118140
islice_grid,
119141
jslice,
120142
jslice,
143+
is_steady=is_steady,
121144
)
122145
upper = (slice(None),) * axis + (slice(0, -1),)
123146
lower = (slice(None),) * axis + (slice(1, None),)
@@ -132,6 +155,7 @@ def initialize_zonal_wind(
132155
islice,
133156
jslice,
134157
jslice,
158+
is_steady=is_steady,
135159
)
136160
u[islice, jslice, :] = 0.25 * (uu1 + 2.0 * uu2 + uu3)[islice, jslice, :]
137161

@@ -161,6 +185,7 @@ def baroclinic_initialization(
161185
hydrostatic,
162186
nx,
163187
ny,
188+
is_steady: bool = False,
164189
):
165190
"""
166191
Calls methods that compute initial state via the Jablonowski perturbation test case
@@ -191,6 +216,7 @@ def baroclinic_initialization(
191216
jslice=slice(0, ny),
192217
jslice_grid=slice(1, ny + 1),
193218
axis=1,
219+
is_steady=is_steady,
194220
)
195221

196222
initialize_zonal_wind(
@@ -206,6 +232,7 @@ def baroclinic_initialization(
206232
jslice=slice(0, ny + 1),
207233
jslice_grid=slice(0, ny + 1),
208234
axis=0,
235+
is_steady=is_steady,
209236
)
210237

211238
slice_3d = (slice(0, nx), slice(0, ny), slice(None))
@@ -251,6 +278,7 @@ def init_baroclinic_state(
251278
hydrostatic: bool,
252279
moist_phys: bool,
253280
comm: CubedSphereCommunicator,
281+
is_steady: bool = False,
254282
) -> DycoreState:
255283
"""
256284
Create a DycoreState object with quantities initialized to the Jablonowski &
@@ -322,6 +350,7 @@ def init_baroclinic_state(
322350
hydrostatic=hydrostatic,
323351
nx=nx,
324352
ny=ny,
353+
is_steady=is_steady,
325354
)
326355

327356
init_utils.p_var(

pyfv3/initialization/test_cases/initialize_rossby.py

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
1-
""" Test case initialization for Rossby-Haurwitz wave 4
1+
"""Test case initialization for Rossby-Haurwitz wave 4
22
33
Corresponds to Fortran shallow-water test #6 found in tools/test_cases.F90 of
44
https://github.com/NOAA-GFDL/GFDL_atmos_cubed_sphere.git
55
"""
66

7+
from types import SimpleNamespace
8+
79
import numpy as np
810

911
from ndsl import CubedSphereCommunicator, QuantityFactory, constants
@@ -20,11 +22,11 @@
2022
GH0 = Float(8.0e3) * constants.GRAV
2123

2224

23-
def _preinit_for_all_sw(numpy_state: DycoreState, shape):
25+
def _preinit_for_all_sw(numpy_state: SimpleNamespace, shape):
2426
"""Pre-initialization for all shallow water tests
2527
2628
Args:
27-
numpy_state: DycoreState modified to update pe, pt, delp
29+
numpy_state: SimpleNamespace modified to update pe, pt, delp
2830
shape: tuple
2931
"""
3032
numpy_state.pe[:] = 0.0
@@ -108,11 +110,11 @@ def _calc_rossby_delp(grid_data: GridData):
108110
)
109111

110112

111-
def _init_for_rossby(numpy_state: DycoreState, grid_data: GridData, shape):
113+
def _init_for_rossby(numpy_state: SimpleNamespace, grid_data: GridData, shape):
112114
"""Initialization specific to Rossby-Haurwitz wave test
113115
114116
Args
115-
numpy_state: DycoreState, modified to update the phis, delp, u, v
117+
numpy_state: SimpleNamespace, modified to update the phis, delp, u, v
116118
grid_Data: GridData
117119
"""
118120
numpy_state.phis[:] = 0.0
@@ -154,11 +156,11 @@ def _init_for_rossby(numpy_state: DycoreState, grid_data: GridData, shape):
154156
# NOTE: test_cases.F90 has dtoa and atoc calls, but not implemented here.
155157

156158

157-
def _postinit_for_all_sw(numpy_state: DycoreState):
159+
def _postinit_for_all_sw(numpy_state: SimpleNamespace):
158160
"""Post-initialization from test_cases.F90 that applies to all shallow water tests
159161
160162
Args
161-
numpy_state: DycoreState - modified
163+
numpy_state: SimpleNamespace - modified
162164
"""
163165

164166
# NOTE: The cl/cl2 tracers from the original test_cases.F90 aren't brought over.
@@ -176,7 +178,7 @@ def init_rossby_state(
176178
comm: CubedSphereCommunicator,
177179
) -> DycoreState:
178180
"""
179-
Create a DycoreState TODO: explain more
181+
Create an initial DycoreState for Rossby
180182
181183
Args:
182184
grid_data: current selected grid data values

0 commit comments

Comments
 (0)