Skip to content

Commit 76d75de

Browse files
authored
Merge pull request #1477 from fvitt/ctem_esmf_consrv
cam6_4_152: Use ESMF conservative regridding
2 parents a61da18 + c991caa commit 76d75de

4 files changed

Lines changed: 171 additions & 35 deletions

File tree

doc/ChangeLog

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,65 @@
11
===============================================================
22

3+
Tag name: cam6_4_152
4+
Originator(s): fvitt
5+
Date: 13 Feb 2026
6+
One-line Summary: TEM diagnostics updates
7+
Github PR URL: https://github.com/ESCOMP/CAM/pull/1477
8+
9+
Purpose of changes (include the issue number and title text for each relevant GitHub issue):
10+
11+
Issue #1475
12+
- Use ESMF 1st order conservative regridding method when computing zonal mean TEM diagnostics
13+
- Compute terms before regridding to avoid multiplication of errors
14+
15+
Describe any changes made to build system: N/A
16+
17+
Describe any changes made to the namelist: N/A
18+
19+
List any changes to the defaults for the boundary datasets: N/A
20+
21+
Describe any substantial timing or memory changes: N/A
22+
23+
Code reviewed by: jimmielin
24+
25+
List all files eliminated: N/A
26+
27+
List all files added and what they do: N/A
28+
29+
List all existing files that have been modified, and describe the changes:
30+
M src/utils/ctem_diags_mod.F90
31+
- compute terms before regridding to avoid multiplication of errors
32+
33+
M src/utils/esmf_lonlat_grid_mod.F90
34+
- need grid cell corners for conservation regridding
35+
36+
M src/utils/esmf_phys2lonlat_mod.F90
37+
- use ESMF conservative regridding
38+
39+
If there were any failures reported from running test_driver.sh on any test
40+
platform, and checkin with these failures has been OK'd by the gatekeeper,
41+
then copy the lines from the td.*.status files for the failed tests to the
42+
appropriate machine below. All failed tests must be justified.
43+
44+
derecho/intel/aux_cam:
45+
FAIL ERI_D_Ln18.ne16pg3_ne16pg3_mt232.FHIST_C4.derecho_intel.cam-outfrq3s_eri
46+
- pre-existing failure
47+
ERI bug in CICE -- See: https://github.com/ESCOMP/CESM_CICE/issues/34
48+
49+
DIFF ERS_Ln9.ne30pg3_ne30pg3_mg17.FHISTC_WXma.derecho_intel.cam-outfrq9s_ctem
50+
- differences in TEM diagnostics output, otherwise bit-for-bit
51+
52+
derecho/nvhpc/aux_cam: All PASS
53+
54+
izumi/nag/aux_cam: All PASS
55+
56+
izumi/gnu/aux_cam: All PASS
57+
58+
Summarize any changes to answers: bit-for-bit
59+
60+
===============================================================
61+
===============================================================
62+
363
Tag name: cam6_4_151
464
Originator(s): Haipeng Lin
565
Date: Feb 12, 2026

src/utils/ctem_diags_mod.F90

Lines changed: 65 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -243,6 +243,12 @@ subroutine ctem_diags_calc(phys_state)
243243
real(r8), target :: w_phys(pver,pcols,begchunk:endchunk)
244244
real(r8), target :: t_phys(pver,pcols,begchunk:endchunk)
245245
real(r8), target :: p_phys(pver,pcols,begchunk:endchunk)
246+
247+
real(r8), target :: uv_phys(pver,pcols,begchunk:endchunk)
248+
real(r8), target :: uw_phys(pver,pcols,begchunk:endchunk)
249+
real(r8), target :: vt_phys(pver,pcols,begchunk:endchunk)
250+
real(r8), target :: wt_phys(pver,pcols,begchunk:endchunk)
251+
246252
real(r8) :: ps_phys(pcols,begchunk:endchunk)
247253

248254
real(r8), target :: u_lonlat(beglon:endlon,beglat:endlat,pver)
@@ -258,16 +264,28 @@ subroutine ctem_diags_calc(phys_state)
258264
real(r8) :: wi_lonlat(beglon:endlon,beglat:endlat,pver)
259265
real(r8) :: ti_lonlat(beglon:endlon,beglat:endlat,pver)
260266

267+
real(r8), target :: uv_lonlat(beglon:endlon,beglat:endlat,pver)
268+
real(r8), target :: uw_lonlat(beglon:endlon,beglat:endlat,pver)
269+
real(r8), target :: vt_lonlat(beglon:endlon,beglat:endlat,pver)
270+
real(r8), target :: wt_lonlat(beglon:endlon,beglat:endlat,pver)
271+
272+
real(r8) :: uvi_lonlat(beglon:endlon,beglat:endlat,pver)
273+
real(r8) :: uwi_lonlat(beglon:endlon,beglat:endlat,pver)
274+
real(r8) :: vti_lonlat(beglon:endlon,beglat:endlat,pver)
275+
real(r8) :: wti_lonlat(beglon:endlon,beglat:endlat,pver)
276+
277+
261278
real(r8) :: u_zm(beglat:endlat,pver)
262279
real(r8) :: v_zm(beglat:endlat,pver)
263280
real(r8) :: w_zm(beglat:endlat,pver)
264281
real(r8) :: t_zm(beglat:endlat,pver)
265-
real(r8) :: ps_zm(beglat:endlat)
266282

267-
real(r8) :: ud_lonlat(beglon:endlon,beglat:endlat,pver)
268-
real(r8) :: vd_lonlat(beglon:endlon,beglat:endlat,pver)
269-
real(r8) :: wd_lonlat(beglon:endlon,beglat:endlat,pver)
270-
real(r8) :: td_lonlat(beglon:endlon,beglat:endlat,pver)
283+
real(r8) :: uv_zm(beglat:endlat,pver)
284+
real(r8) :: uw_zm(beglat:endlat,pver)
285+
real(r8) :: vt_zm(beglat:endlat,pver)
286+
real(r8) :: wt_zm(beglat:endlat,pver)
287+
288+
real(r8) :: ps_zm(beglat:endlat)
271289

272290
real(r8) :: vtp(beglon:endlon,beglat:endlat,pver)
273291
real(r8) :: wtp(beglon:endlon,beglat:endlat,pver)
@@ -316,8 +334,14 @@ subroutine ctem_diags_calc(phys_state)
316334
! mid point press
317335
p_phys(:,i,lchnk) = phys_state(lchnk)%pmid(i,:)
318336

337+
! surface pressure
319338
ps_phys(i,lchnk) = phys_state(lchnk)%ps(i)
320339

340+
uv_phys(:,i,lchnk) = u_phys(:,i,lchnk)*v_phys(:,i,lchnk)
341+
uw_phys(:,i,lchnk) = u_phys(:,i,lchnk)*w_phys(:,i,lchnk)
342+
vt_phys(:,i,lchnk) = v_phys(:,i,lchnk)*t_phys(:,i,lchnk)
343+
wt_phys(:,i,lchnk) = w_phys(:,i,lchnk)*t_phys(:,i,lchnk)
344+
321345
end do
322346
end do
323347

@@ -332,12 +356,20 @@ subroutine ctem_diags_calc(phys_state)
332356
physflds(3)%fld => w_phys
333357
physflds(4)%fld => t_phys
334358
physflds(5)%fld => p_phys
359+
physflds(6)%fld => uv_phys
360+
physflds(7)%fld => uw_phys
361+
physflds(8)%fld => vt_phys
362+
physflds(9)%fld => wt_phys
335363

336364
lonlatflds(1)%fld => u_lonlat
337365
lonlatflds(2)%fld => v_lonlat
338366
lonlatflds(3)%fld => w_lonlat
339367
lonlatflds(4)%fld => t_lonlat
340368
lonlatflds(5)%fld => p_lonlat
369+
lonlatflds(6)%fld => uv_lonlat
370+
lonlatflds(7)%fld => uw_lonlat
371+
lonlatflds(8)%fld => vt_lonlat
372+
lonlatflds(9)%fld => wt_lonlat
341373

342374
call esmf_phys2lonlat_regrid(physflds, lonlatflds)
343375

@@ -378,6 +410,18 @@ subroutine ctem_diags_calc(phys_state)
378410
call lininterp( t_lonlat(i,j,:), p_lonlat(i,j,:), pver, &
379411
ti_lonlat(i,j,:), pref_mid(:), pver )
380412

413+
call lininterp( uv_lonlat(i,j,:), p_lonlat(i,j,:), pver, &
414+
uvi_lonlat(i,j,:), pref_mid(:), pver )
415+
416+
call lininterp( uw_lonlat(i,j,:), p_lonlat(i,j,:), pver, &
417+
uwi_lonlat(i,j,:), pref_mid(:), pver )
418+
419+
call lininterp( vt_lonlat(i,j,:), p_lonlat(i,j,:), pver, &
420+
vti_lonlat(i,j,:), pref_mid(:), pver )
421+
422+
call lininterp( wt_lonlat(i,j,:), p_lonlat(i,j,:), pver, &
423+
wti_lonlat(i,j,:), pref_mid(:), pver )
424+
381425
end do
382426
end do
383427

@@ -394,32 +438,33 @@ subroutine ctem_diags_calc(phys_state)
394438
call esmf_zonal_mean_masked(wi_lonlat, wght, wsums, w_zm)
395439
call esmf_zonal_mean_masked(ti_lonlat, wght, wsums, t_zm)
396440

441+
call esmf_zonal_mean_masked(uvi_lonlat, wght, wsums, uv_zm)
442+
call esmf_zonal_mean_masked(uwi_lonlat, wght, wsums, uw_zm)
443+
call esmf_zonal_mean_masked(vti_lonlat, wght, wsums, vt_zm)
444+
call esmf_zonal_mean_masked(wti_lonlat, wght, wsums, wt_zm)
445+
397446
call t_stopf('ctem_diags_calc-zonal_mean-uvwt')
398447

399448
call t_startf('ctem_diags_calc-calc_dev_flx')
400449

401-
! Calculate zonal deviations and fluxes
450+
! Calculate fluxes
402451
do k = 1,pver
403452
do j = beglat,endlat
404453
do i = beglon,endlon
405454
if (wght(i,j,k)>0._r8) then
406-
ud_lonlat(i,j,k) = ui_lonlat(i,j,k) - u_zm(j,k)
407-
vd_lonlat(i,j,k) = vi_lonlat(i,j,k) - v_zm(j,k)
408-
wd_lonlat(i,j,k) = wi_lonlat(i,j,k) - w_zm(j,k)
409-
td_lonlat(i,j,k) = ti_lonlat(i,j,k) - t_zm(j,k)
410-
vtp(i,j,k) = vd_lonlat(i,j,k) * td_lonlat(i,j,k)
411-
wtp(i,j,k) = wd_lonlat(i,j,k) * td_lonlat(i,j,k)
412-
uwp(i,j,k) = ud_lonlat(i,j,k) * wd_lonlat(i,j,k)
413-
uvp(i,j,k) = ud_lonlat(i,j,k) * vd_lonlat(i,j,k)
455+
456+
! u'v' = (uv)zm - uzm*vzm
457+
458+
uvp(i,j,k) = uv_zm(j,k) - (u_zm(j,k)*v_zm(j,k))
459+
uwp(i,j,k) = uw_zm(j,k) - (u_zm(j,k)*w_zm(j,k))
460+
vtp(i,j,k) = vt_zm(j,k) - (v_zm(j,k)*t_zm(j,k))
461+
wtp(i,j,k) = wt_zm(j,k) - (w_zm(j,k)*t_zm(j,k))
462+
414463
else
415-
ud_lonlat(i,j,k) = fillvalue
416-
vd_lonlat(i,j,k) = fillvalue
417-
wd_lonlat(i,j,k) = fillvalue
418-
td_lonlat(i,j,k) = fillvalue
464+
uvp(i,j,k) = fillvalue
465+
uwp(i,j,k) = fillvalue
419466
vtp(i,j,k) = fillvalue
420467
wtp(i,j,k) = fillvalue
421-
uwp(i,j,k) = fillvalue
422-
uvp(i,j,k) = fillvalue
423468
end if
424469
end do
425470
end do

src/utils/esmf_lonlat_grid_mod.F90

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ module esmf_lonlat_grid_mod
99

1010
use ESMF, only: ESMF_Grid, ESMF_GridCreate1PeriDim, ESMF_GridAddCoord
1111
use ESMF, only: ESMF_GridGetCoord, ESMF_GridDestroy
12-
use ESMF, only: ESMF_KIND_R8, ESMF_INDEX_GLOBAL, ESMF_STAGGERLOC_CENTER
12+
use ESMF, only: ESMF_KIND_R8, ESMF_INDEX_GLOBAL, ESMF_STAGGERLOC_CENTER, ESMF_STAGGERLOC_CORNER
1313
use esmf_check_error_mod, only: check_esmf_error
1414

1515
implicit none
@@ -287,6 +287,7 @@ subroutine esmf_lonlat_grid_init(nlats_in)
287287

288288
! Set coordinates:
289289

290+
! cell centers
290291
call ESMF_GridAddCoord(lonlat_grid, staggerloc=ESMF_STAGGERLOC_CENTER, rc=ierr)
291292
call check_esmf_error(ierr, subname//'ESMF_GridAddCoord ERROR')
292293

@@ -314,6 +315,38 @@ subroutine esmf_lonlat_grid_init(nlats_in)
314315
end do
315316
end if
316317

318+
! cell corners
319+
call ESMF_GridAddCoord(lonlat_grid, staggerloc=ESMF_STAGGERLOC_CORNER, rc=ierr)
320+
call check_esmf_error(ierr, subname//'ESMF_GridAddCoord CORNER ERROR')
321+
322+
if (mytid<npes) then
323+
call ESMF_GridGetCoord(lonlat_grid, coordDim=1, &
324+
computationalLBound=lbnd, computationalUBound=ubnd, &
325+
farrayPtr=coordX, staggerloc=ESMF_STAGGERLOC_CORNER, rc=ierr)
326+
call check_esmf_error(ierr, subname//'ESMF_GridGetCoord for longitude corner coords ERROR')
327+
328+
lbnd_lon = lbnd(1)
329+
ubnd_lon = ubnd(1)
330+
do i = lbnd_lon, ubnd_lon
331+
coordX(i) = glons(i) - (0.5_r8 * delx)
332+
end do
333+
334+
call ESMF_GridGetCoord(lonlat_grid, coordDim=2, &
335+
computationalLBound=lbnd, computationalUBound=ubnd, &
336+
farrayPtr=coordY, staggerloc=ESMF_STAGGERLOC_CORNER, rc=ierr)
337+
call check_esmf_error(ierr, subname//'ESMF_GridGetCoord for latitude corner coords ERROR')
338+
339+
lbnd_lat = lbnd(1)
340+
ubnd_lat = min( ubnd(1), nlat)
341+
do i = lbnd_lat, ubnd_lat
342+
coordY(i) = glats(i) - (0.5_r8 * dely)
343+
end do
344+
if (ubnd(1) == nlat+1) then
345+
coordY(nlat+1) = 90._r8
346+
end if
347+
348+
end if
349+
317350
deallocate(nlons_task)
318351
deallocate(nlats_task)
319352

src/utils/esmf_phys2lonlat_mod.F90

Lines changed: 12 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ module esmf_phys2lonlat_mod
1313
use ESMF, only: ESMF_FieldCreate, ESMF_FieldRegridStore
1414
use ESMF, only: ESMF_FieldGet, ESMF_FieldRegrid
1515
use ESMF, only: ESMF_KIND_I4, ESMF_KIND_R8, ESMF_TYPEKIND_R8
16-
use ESMF, only: ESMF_REGRIDMETHOD_BILINEAR, ESMF_POLEMETHOD_ALLAVG, ESMF_EXTRAPMETHOD_NEAREST_IDAVG
16+
use ESMF, only: ESMF_REGRIDMETHOD_CONSERVE
1717
use ESMF, only: ESMF_TERMORDER_SRCSEQ, ESMF_MESHLOC_ELEMENT, ESMF_STAGGERLOC_CENTER
1818
use ESMF, only: ESMF_FieldDestroy, ESMF_RouteHandleDestroy
1919
use esmf_check_error_mod, only: check_esmf_error
@@ -46,7 +46,7 @@ module esmf_phys2lonlat_mod
4646
real(r8), pointer :: fld(:,:,:) => null()
4747
end type fields_bundle_t
4848

49-
integer, parameter :: nflds = 5
49+
integer, parameter :: nflds = 9
5050

5151
contains
5252

@@ -101,21 +101,19 @@ subroutine esmf_phys2lonlat_init()
101101
call check_esmf_error(rc, subname//'ESMF_FieldCreate 2D lonlat fld ERROR')
102102

103103
call ESMF_FieldRegridStore(srcField=physfld_3d, dstField=lonlatfld_3d, &
104-
regridMethod=ESMF_REGRIDMETHOD_BILINEAR, &
105-
polemethod=ESMF_POLEMETHOD_ALLAVG, &
106-
extrapMethod=ESMF_EXTRAPMETHOD_NEAREST_IDAVG, &
107-
routeHandle=rh_phys2lonlat_3d, factorIndexList=factorIndexList, &
108-
factorList=factorList, srcTermProcessing=smm_srctermproc, &
109-
pipelineDepth=smm_pipelinedep, rc=rc)
104+
regridMethod=ESMF_REGRIDMETHOD_CONSERVE, &
105+
routeHandle=rh_phys2lonlat_3d, &
106+
srcTermProcessing=smm_srctermproc, &
107+
pipelineDepth=smm_pipelinedep, &
108+
rc=rc)
110109
call check_esmf_error(rc, subname//'ESMF_FieldRegridStore 3D routehandle ERROR')
111110

112111
call ESMF_FieldRegridStore(srcField=physfld_2d, dstField=lonlatfld_2d, &
113-
regridMethod=ESMF_REGRIDMETHOD_BILINEAR, &
114-
polemethod=ESMF_POLEMETHOD_ALLAVG, &
115-
extrapMethod=ESMF_EXTRAPMETHOD_NEAREST_IDAVG, &
116-
routeHandle=rh_phys2lonlat_2d, factorIndexList=factorIndexList, &
117-
factorList=factorList, srcTermProcessing=smm_srctermproc, &
118-
pipelineDepth=smm_pipelinedep, rc=rc)
112+
regridMethod=ESMF_REGRIDMETHOD_CONSERVE, &
113+
routeHandle=rh_phys2lonlat_2d, &
114+
srcTermProcessing=smm_srctermproc, &
115+
pipelineDepth=smm_pipelinedep, &
116+
rc=rc)
119117
call check_esmf_error(rc, subname//'ESMF_FieldRegridStore 3D routehandle ERROR')
120118

121119
end subroutine esmf_phys2lonlat_init

0 commit comments

Comments
 (0)