Skip to content

Commit 98edc0b

Browse files
authored
feat(geom): add n_peri_dim to XYGeomSpec for tripolar support (#4693)
* feat(geom): add n_peri_dim to XYGeomSpec for tripolar support (#4691) Add an n_peri_dim field (default 0) to XYGeomSpec. When n_peri_dim==1 the factory calls ESMF_GridCreate1PeriDim with poleKindFlag=[MONOPOLE, BIPOLE] instead of ESMF_GridCreateNoPeriDim, enabling tripolar ocean grids as a first-class subcase of the XY geometry family. Includes hconfig parsing, equality check, and pfUnit tests. * test(geom): add tripolar factory test exercising ESMF_GridCreate1PeriDim
1 parent 02d88ea commit 98edc0b

6 files changed

Lines changed: 159 additions & 14 deletions

File tree

geom/XY/XYGeomFactory/create_basic_grid.F90

Lines changed: 22 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -17,15 +17,28 @@ module function create_basic_grid(spec, unusable, rc) result(grid)
1717
integer :: status
1818
type(ESMF_Info) :: infoh
1919

20-
grid = ESMF_GridCreateNoPeriDim( &
21-
countsPerDEDim1=spec%get_ims(), &
22-
countsPerDEDim2=spec%get_jms(), &
23-
indexFlag=ESMF_INDEX_DELOCAL, &
24-
gridEdgeLWidth=[0,0], &
25-
gridEdgeUWidth=[0,1], &
26-
coordDep1=[1,2], &
27-
coordDep2=[1,2], &
28-
coordSys=ESMF_COORDSYS_SPH_RAD, _RC)
20+
if (spec%get_n_peri_dim() == 0) then
21+
grid = ESMF_GridCreateNoPeriDim( &
22+
countsPerDEDim1=spec%get_ims(), &
23+
countsPerDEDim2=spec%get_jms(), &
24+
indexFlag=ESMF_INDEX_DELOCAL, &
25+
gridEdgeLWidth=[0,0], &
26+
gridEdgeUWidth=[0,1], &
27+
coordDep1=[1,2], &
28+
coordDep2=[1,2], &
29+
coordSys=ESMF_COORDSYS_SPH_RAD, _RC)
30+
else
31+
grid = ESMF_GridCreate1PeriDim( &
32+
countsPerDEDim1=spec%get_ims(), &
33+
countsPerDEDim2=spec%get_jms(), &
34+
poleKindFlag=[ESMF_POLEKIND_MONOPOLE, ESMF_POLEKIND_BIPOLE], &
35+
indexFlag=ESMF_INDEX_DELOCAL, &
36+
gridEdgeLWidth=[0,0], &
37+
gridEdgeUWidth=[0,1], &
38+
coordDep1=[1,2], &
39+
coordDep2=[1,2], &
40+
coordSys=ESMF_COORDSYS_SPH_RAD, _RC)
41+
end if
2942

3043
! Allocate centre coordinates
3144
call ESMF_GridAddCoord(grid, _RC)

geom/XY/XYGeomSpec.F90

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@ module mapl3g_XYGeomSpec
3131
integer, allocatable :: jms(:) ! length ny
3232
! Whether the file contains corner coordinates
3333
logical :: has_corners = .false.
34+
! Number of periodic dimensions (0=none, 1=periodic in X for tripolar)
35+
integer :: n_peri_dim = 0
3436
! Coordinate mode
3537
integer :: coord_mode = XY_COORD_STANDARD
3638
! ABI-specific metadata
@@ -62,6 +64,7 @@ module mapl3g_XYGeomSpec
6264
procedure :: get_jms
6365
procedure :: get_grid_file_name
6466
procedure :: get_has_corners
67+
procedure :: get_n_peri_dim
6568
procedure :: get_coord_mode
6669
procedure :: get_thin_factor
6770
procedure :: get_xdim_true
@@ -142,7 +145,7 @@ end subroutine get_horz_ij_index_r8
142145

143146
function new_XYGeomSpec( &
144147
grid_file_name, im_world, jm_world, lm, ims, jms, &
145-
has_corners, coord_mode, thin_factor, xdim_true, ydim_true, &
148+
has_corners, n_peri_dim, coord_mode, thin_factor, xdim_true, ydim_true, &
146149
index_name_x, index_name_y, &
147150
var_name_x, var_name_y, var_name_proj, att_name_proj) result(spec)
148151
type(XYGeomSpec) :: spec
@@ -153,6 +156,7 @@ function new_XYGeomSpec( &
153156
integer, optional, intent(in) :: ims(:)
154157
integer, optional, intent(in) :: jms(:)
155158
logical, optional, intent(in) :: has_corners
159+
integer, optional, intent(in) :: n_peri_dim
156160
integer, optional, intent(in) :: coord_mode
157161
integer, optional, intent(in) :: thin_factor
158162
integer, optional, intent(in) :: xdim_true
@@ -171,6 +175,7 @@ function new_XYGeomSpec( &
171175
if (present(ims)) spec%ims = ims
172176
if (present(jms)) spec%jms = jms
173177
if (present(has_corners)) spec%has_corners = has_corners
178+
if (present(n_peri_dim)) spec%n_peri_dim = n_peri_dim
174179
if (present(coord_mode)) spec%coord_mode = coord_mode
175180
if (present(thin_factor)) spec%thin_factor = thin_factor
176181
if (present(xdim_true)) spec%xdim_true = xdim_true
@@ -224,6 +229,11 @@ logical function get_has_corners(this) result(v)
224229
v = this%has_corners
225230
end function get_has_corners
226231

232+
integer function get_n_peri_dim(this) result(v)
233+
class(XYGeomSpec), intent(in) :: this
234+
v = this%n_peri_dim
235+
end function get_n_peri_dim
236+
227237
integer function get_coord_mode(this) result(v)
228238
class(XYGeomSpec), intent(in) :: this
229239
v = this%coord_mode

geom/XY/XYGeomSpec/equal_to.F90

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,11 @@ pure logical module function equal_to(a, b)
1212

1313
select type (b)
1414
type is (XYGeomSpec)
15-
equal_to = (a%im_world == b%im_world) .and. &
16-
(a%jm_world == b%jm_world) .and. &
17-
(a%lm == b%lm) .and. &
18-
(a%coord_mode == b%coord_mode) .and. &
15+
equal_to = (a%im_world == b%im_world) .and. &
16+
(a%jm_world == b%jm_world) .and. &
17+
(a%lm == b%lm) .and. &
18+
(a%n_peri_dim == b%n_peri_dim) .and. &
19+
(a%coord_mode == b%coord_mode) .and. &
1920
(a%thin_factor == b%thin_factor) .and. &
2021
(a%grid_file_name == b%grid_file_name)
2122
class default

geom/XY/XYGeomSpec/make_XYGeomSpec_from_hconfig.F90

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,11 +26,18 @@ module function make_XYGeomSpec_from_hconfig(hconfig, rc) result(spec)
2626
character(len=:), allocatable :: var_name_x, var_name_y
2727
character(len=:), allocatable :: var_name_proj, att_name_proj
2828
integer :: coord_mode
29+
integer :: n_peri_dim
2930

3031
! Required: grid_file_name
3132
grid_file_name = ESMF_HConfigAsString(hconfig, keyString='grid_file_name', _RC)
3233
spec%grid_file_name = grid_file_name
3334

35+
! Optional: n_peri_dim (0=non-periodic, 1=periodic in X for tripolar)
36+
n_peri_dim = 0
37+
found = ESMF_HConfigIsDefined(hconfig, keyString='n_peri_dim', _RC)
38+
if (found) n_peri_dim = ESMF_HConfigAsI4(hconfig, keyString='n_peri_dim', _RC)
39+
spec%n_peri_dim = n_peri_dim
40+
3441
! Optional vertical extent
3542
has_lm = ESMF_HConfigIsDefined(hconfig, keystring='LM', _RC)
3643
if (has_lm) then

geom/tests/Test_XYGeomFactory.pf

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,63 @@ contains
160160
call ESMF_HConfigDestroy(hconfig, rc=status)
161161
end subroutine test_make_geom_adds_mask
162162

163+
! -----------------------------------------------------------------------
164+
! make_geom: n_peri_dim=1 (tripolar) — exercises ESMF_GridCreate1PeriDim path
165+
! -----------------------------------------------------------------------
166+
167+
@test(type=ESMF_TestMethod, npes=[1])
168+
subroutine test_make_geom_tripolar(this)
169+
class(ESMF_TestMethod), intent(inout) :: this
170+
171+
integer, parameter :: IM = 8, JM = 4
172+
type(XYGeomFactory) :: factory
173+
class(GeomSpec), allocatable :: spec
174+
type(ESMF_HConfig) :: hconfig
175+
type(ESMF_Geom) :: geom
176+
type(ESMF_Grid) :: grid
177+
type(ESMF_GeomType_Flag) :: geomtype
178+
real(ESMF_KIND_R8), pointer :: fptr(:,:)
179+
integer :: status
180+
real(ESMF_KIND_R8), parameter :: PI = acos(-1.0_ESMF_KIND_R8)
181+
182+
_UNUSED_DUMMY(this)
183+
184+
call create_xy_file('test_xy_tripolar.nc', IM, JM, rc=status)
185+
@assert_that(status, is(0))
186+
187+
hconfig = ESMF_HConfigCreate( &
188+
content='{class: xy, grid_file_name: test_xy_tripolar.nc, n_peri_dim: 1}', rc=status)
189+
@assert_that(status, is(0))
190+
191+
spec = factory%make_spec(hconfig, rc=status)
192+
@assert_that(status, is(0))
193+
194+
geom = factory%make_geom(spec, rc=status)
195+
@assert_that(status, is(0))
196+
197+
call ESMF_GeomGet(geom, geomtype=geomtype, rc=status)
198+
@assert_that(status, is(0))
199+
@assertTrue(geomtype == ESMF_GEOMTYPE_GRID)
200+
201+
call ESMF_GeomGet(geom, grid=grid, rc=status)
202+
@assert_that(status, is(0))
203+
204+
! Verify centre coordinates are populated and in radians
205+
call ESMF_GridGetCoord(grid, coordDim=1, localDE=0, &
206+
staggerloc=ESMF_STAGGERLOC_CENTER, farrayPtr=fptr, rc=status)
207+
@assert_that(status, is(0))
208+
@assertTrue(all(fptr >= -PI))
209+
@assertTrue(all(fptr <= PI))
210+
211+
call ESMF_GridGetCoord(grid, coordDim=2, localDE=0, &
212+
staggerloc=ESMF_STAGGERLOC_CENTER, farrayPtr=fptr, rc=status)
213+
@assert_that(status, is(0))
214+
@assertTrue(all(fptr >= -PI/2.0_ESMF_KIND_R8))
215+
@assertTrue(all(fptr <= PI/2.0_ESMF_KIND_R8))
216+
217+
call ESMF_HConfigDestroy(hconfig, rc=status)
218+
end subroutine test_make_geom_tripolar
219+
163220
! -----------------------------------------------------------------------
164221
! make_file_metadata round-trip: metadata produced is recognized by supports_metadata
165222
! -----------------------------------------------------------------------

geom/tests/Test_XYGeomSpec.pf

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -212,4 +212,61 @@ contains
212212
call ESMF_HConfigDestroy(hconfig_b, rc=status)
213213
end subroutine test_equal_to_different_file
214214

215+
! -----------------------------------------------------------------------
216+
! n_peri_dim: hconfig round-trip and equal_to distinguish by n_peri_dim
217+
! -----------------------------------------------------------------------
218+
219+
@test(type=ESMF_TestMethod, npes=[1])
220+
subroutine test_n_peri_dim_from_hconfig(this)
221+
class(ESMF_TestMethod), intent(inout) :: this
222+
type(XYGeomSpec) :: spec
223+
type(ESMF_HConfig) :: hconfig
224+
integer :: status
225+
226+
_UNUSED_DUMMY(this)
227+
228+
call create_xy_file('test_xy_peri.nc', 4, 4, rc=status)
229+
@assert_that(status, is(0))
230+
231+
hconfig = ESMF_HConfigCreate( &
232+
content='{class: xy, grid_file_name: test_xy_peri.nc, n_peri_dim: 1}', rc=status)
233+
@assert_that(status, is(0))
234+
235+
spec = make_XYGeomSpec(hconfig, rc=status)
236+
@assert_that(status, is(0))
237+
@assert_that(spec%get_n_peri_dim(), is(1))
238+
239+
call ESMF_HConfigDestroy(hconfig, rc=status)
240+
end subroutine test_n_peri_dim_from_hconfig
241+
242+
@test(type=ESMF_TestMethod, npes=[1])
243+
subroutine test_equal_to_different_n_peri_dim(this)
244+
class(ESMF_TestMethod), intent(inout) :: this
245+
type(XYGeomSpec) :: a, b
246+
type(ESMF_HConfig) :: hconfig_a, hconfig_b
247+
integer :: status
248+
249+
_UNUSED_DUMMY(this)
250+
251+
call create_xy_file('test_xy_peri2.nc', 4, 4, rc=status)
252+
@assert_that(status, is(0))
253+
254+
hconfig_a = ESMF_HConfigCreate( &
255+
content='{class: xy, grid_file_name: test_xy_peri2.nc}', rc=status)
256+
@assert_that(status, is(0))
257+
hconfig_b = ESMF_HConfigCreate( &
258+
content='{class: xy, grid_file_name: test_xy_peri2.nc, n_peri_dim: 1}', rc=status)
259+
@assert_that(status, is(0))
260+
261+
a = make_XYGeomSpec(hconfig_a, rc=status)
262+
@assert_that(status, is(0))
263+
b = make_XYGeomSpec(hconfig_b, rc=status)
264+
@assert_that(status, is(0))
265+
266+
@assertFalse(a == b)
267+
268+
call ESMF_HConfigDestroy(hconfig_a, rc=status)
269+
call ESMF_HConfigDestroy(hconfig_b, rc=status)
270+
end subroutine test_equal_to_different_n_peri_dim
271+
215272
end module Test_XYGeomSpec

0 commit comments

Comments
 (0)