Skip to content

Commit b96cb33

Browse files
Merge pull request #782 from NCAR/mom6-pot-temp
fix: potential temp to temp conversion for MOM6 model_interpolate
2 parents c696036 + 347f84f commit b96cb33

File tree

3 files changed

+160
-35
lines changed

3 files changed

+160
-35
lines changed

CHANGELOG.rst

+6-1
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,12 @@ individual files.
2222

2323
The changes are now listed with the most recent at the top.
2424

25-
**January 9 2024 :: Bug-fix 1D obs_diag. Tag v11.8.7**
25+
**January 14 2025 :: Bug-fix MOM6 potential temperature. Tag v11.8.8**
26+
27+
- MOM6 model_interpolate for potential temperature
28+
- Update lorenz workshop input.nmls to v11
29+
30+
**January 9 2025 :: Bug-fix 1D obs_diag. Tag v11.8.7**
2631

2732
- Added a dummy dimension so 1D obs_diag output can be used with
2833
MATLAB diagnostic tools

conf.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
author = 'Data Assimilation Research Section'
2222

2323
# The full version, including alpha/beta/rc tags
24-
release = '11.8.8'
24+
release = '11.8.9'
2525
root_doc = 'index'
2626

2727
# -- General configuration ---------------------------------------------------

models/MOM6/model_mod.f90

+153-33
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,8 @@ module model_mod
4949

5050
use obs_kind_mod, only : get_index_for_quantity, QTY_U_CURRENT_COMPONENT, &
5151
QTY_V_CURRENT_COMPONENT, QTY_LAYER_THICKNESS, &
52-
QTY_DRY_LAND, QTY_SALINITY
52+
QTY_DRY_LAND, QTY_SALINITY, QTY_TEMPERATURE, &
53+
QTY_POTENTIAL_TEMPERATURE
5354

5455
use ensemble_manager_mod, only : ensemble_type
5556

@@ -113,8 +114,8 @@ module model_mod
113114
integer, parameter :: NOT_IN_STATE = 12
114115
integer, parameter :: THICKNESS_NOT_IN_STATE = 13
115116
integer, parameter :: QUAD_LOCATE_FAILED = 14
116-
integer, parameter :: THICKNESS_QUAD_EVALUTATE_FAILED = 15
117-
integer, parameter :: QUAD_EVALUTATE_FAILED = 16
117+
integer, parameter :: THICKNESS_QUAD_EVALUATE_FAILED = 15
118+
integer, parameter :: QUAD_EVALUATE_FAILED = 16
118119
integer, parameter :: QUAD_ON_LAND = 17
119120
integer, parameter :: QUAD_ON_BASIN_EDGE = 18
120121
integer, parameter :: OBS_ABOVE_SURFACE = 20
@@ -220,25 +221,29 @@ end function get_model_size
220221
! 0 unless there is some problem in computing the interpolation in
221222
! which case a positive istatus should be returned.
222223

223-
subroutine model_interpolate(state_handle, ens_size, location, qty, expected_obs, istatus)
224+
subroutine model_interpolate(state_handle, ens_size, location, qty_in, expected_obs, istatus)
224225

225226

226227
type(ensemble_type), intent(in) :: state_handle
227228
integer, intent(in) :: ens_size
228229
type(location_type), intent(in) :: location
229-
integer, intent(in) :: qty
230+
integer, intent(in) :: qty_in
230231
real(r8), intent(out) :: expected_obs(ens_size) !< array of interpolated values
231232
integer, intent(out) :: istatus(ens_size)
232233

234+
real(r8), parameter :: CONCENTRATION_TO_PPT = 1000.0_r8
235+
236+
integer :: qty ! local qty
233237
integer :: which_vert, four_ilons(4), four_ilats(4), lev(ens_size,2)
234238
integer :: locate_status, quad_status
235239
real(r8) :: lev_fract(ens_size)
236240
real(r8) :: lon_lat_vert(3)
237241
real(r8) :: quad_vals(4, ens_size)
238242
real(r8) :: expected(ens_size, 2) ! level below and above obs
243+
real(r8) :: expected_pot_temp(ens_size), expected_salinity(ens_size), pressure_dbars(ens_size)
239244
type(quad_interp_handle) :: interp
240245
integer :: varid, i, e, thick_id
241-
integer(i8) :: th_indx, indx(ens_size)
246+
integer(i8) :: th_indx
242247
real(r8) :: depth_at_x(ens_size), thick_at_x(ens_size) ! depth, layer thickness at obs lat lon
243248
logical :: found(ens_size)
244249

@@ -247,6 +252,16 @@ subroutine model_interpolate(state_handle, ens_size, location, qty, expected_obs
247252
expected_obs(:) = MISSING_R8
248253
istatus(:) = 1
249254

255+
if (qty_in == QTY_TEMPERATURE) then
256+
qty = QTY_POTENTIAL_TEMPERATURE ! model has potential temperature
257+
if (get_varid_from_kind(dom_id, QTY_SALINITY) < 0) then ! Require salinity to convert to temperature
258+
istatus = NOT_IN_STATE
259+
return
260+
end if
261+
else
262+
qty = qty_in
263+
endif
264+
250265
varid = get_varid_from_kind(dom_id, qty)
251266
if (varid < 0) then ! not in state
252267
istatus = NOT_IN_STATE
@@ -310,7 +325,7 @@ subroutine model_interpolate(state_handle, ens_size, location, qty, expected_obs
310325
thick_at_x, &
311326
quad_status)
312327
if (quad_status /= 0) then
313-
istatus(:) = THICKNESS_QUAD_EVALUTATE_FAILED
328+
istatus(:) = THICKNESS_QUAD_EVALUATE_FAILED
314329
return
315330
endif
316331

@@ -338,6 +353,66 @@ subroutine model_interpolate(state_handle, ens_size, location, qty, expected_obs
338353
return
339354
endif
340355

356+
357+
select case (qty_in)
358+
case (QTY_TEMPERATURE)
359+
! convert from potential temperature to temperature
360+
361+
call state_on_quad(four_ilons, four_ilats, lon_lat_vert, ens_size, lev, lev_fract, interp, state_handle, varid, expected_pot_temp, quad_status)
362+
if (quad_status /= 0) then
363+
istatus(:) = QUAD_EVALUATE_FAILED
364+
return
365+
endif
366+
call state_on_quad(four_ilons, four_ilats, lon_lat_vert, ens_size, lev, lev_fract, interp, state_handle, get_varid_from_kind(dom_id, QTY_SALINITY), expected_salinity, quad_status)
367+
if (quad_status /= 0) then
368+
istatus(:) = QUAD_EVALUATE_FAILED
369+
return
370+
endif
371+
372+
pressure_dbars = 0.059808_r8*(exp(-0.025_r8*depth_at_x) - 1.0_r8) &
373+
+ 0.100766_r8*depth_at_x + 2.28405e-7_r8*lon_lat_vert(3)**2
374+
expected_obs = sensible_temp(expected_pot_temp, expected_salinity, pressure_dbars)
375+
376+
case (QTY_SALINITY) ! convert from g of salt per kg of seawater (model) to kg of salt per kg of seawater (observation)
377+
call state_on_quad(four_ilons, four_ilats, lon_lat_vert, ens_size, lev, lev_fract, interp, state_handle, varid, expected_obs, quad_status)
378+
if (quad_status /= 0) then
379+
istatus(:) = QUAD_EVALUATE_FAILED
380+
return
381+
endif
382+
expected_obs = expected_obs/CONCENTRATION_TO_PPT
383+
384+
case default
385+
call state_on_quad(four_ilons, four_ilats, lon_lat_vert, ens_size, lev, lev_fract, interp, state_handle, varid, expected_obs, quad_status)
386+
if (quad_status /= 0) then
387+
istatus(:) = QUAD_EVALUATE_FAILED
388+
return
389+
endif
390+
end select
391+
392+
istatus(:) = 0
393+
394+
end subroutine model_interpolate
395+
396+
!------------------------------------------------------------------
397+
! Interpolate on the quad, between two levels
398+
subroutine state_on_quad(four_ilons, four_ilats, lon_lat_vert, ens_size, lev, lev_fract, interp, state_handle, varid, expected_obs, quad_status)
399+
400+
integer, intent(in) :: four_ilons(4), four_ilats(4) ! indices into lon, lat
401+
real(r8), intent(in) :: lon_lat_vert(3) ! lon, lat, vert of obs
402+
integer, intent(in) :: ens_size
403+
integer, intent(in) :: lev(ens_size,2) ! levels below and above obs
404+
real(r8), intent(in) :: lev_fract(ens_size)
405+
type(quad_interp_handle), intent(in) :: interp
406+
type(ensemble_type), intent(in) :: state_handle
407+
integer, intent(in) :: varid ! which state variable
408+
real(r8), intent(out) :: expected_obs(ens_size)
409+
integer, intent(out) :: quad_status
410+
411+
integer :: i, e
412+
integer(i8) :: indx(ens_size)
413+
real(r8) :: quad_vals(4, ens_size)
414+
real(r8) :: expected(ens_size, 2) ! state value at level below and above obs
415+
341416
do i = 1, 2
342417
!HK which corner of the quad is which?
343418
! corner1
@@ -371,27 +446,15 @@ subroutine model_interpolate(state_handle, ens_size, location, qty, expected_obs
371446
quad_vals, & ! 4 corners x ens_size
372447
expected(:,i), &
373448
quad_status)
374-
if (quad_status /= 0) then
375-
istatus(:) = QUAD_EVALUTATE_FAILED
376-
return
377-
else
378-
istatus = 0
379-
endif
449+
if (quad_status /= 0) return
380450

381451
enddo
382452

383453
! Interpolate between levels
384454
! expected_obs = bot_val + lev_fract * (top_val - bot_val)
385455
expected_obs = expected(:,1) + lev_fract(:) * (expected(:,2) - expected(:,1))
386456

387-
if (qty == QTY_SALINITY) then ! convert from PSU (model) to MSU (obersvation)
388-
expected_obs = expected_obs/1000.0_r8
389-
endif
390-
391-
392-
end subroutine model_interpolate
393-
394-
457+
end subroutine state_on_quad
395458

396459
!------------------------------------------------------------------
397460
! Returns the smallest increment in time that the model is capable
@@ -627,28 +690,28 @@ subroutine read_horizontal_grid()
627690
mask_v(:,:) = .false.
628691
call nc_get_attribute_from_variable(ncid, 'geolon', '_FillValue', fillval)
629692
where (geolon == fillval) mask = .true.
630-
where (geolon == fillval) geolon = 72.51
631-
where (geolat == fillval) geolat = 42.56
693+
where (geolon == fillval) geolon = 72.51_r8
694+
where (geolat == fillval) geolat = 42.56_r8
632695

633696
call nc_get_attribute_from_variable(ncid, 'geolon_u', '_FillValue', fillval)
634697
where (geolon_u == fillval) mask_u = .true.
635-
where (geolon_u == fillval) geolon_u = 72.51
636-
where (geolat_u == fillval) geolat_u = 42.56
698+
where (geolon_u == fillval) geolon_u = 72.51_r8
699+
where (geolat_u == fillval) geolat_u = 42.56_r8
637700

638701
call nc_get_attribute_from_variable(ncid, 'geolon_v', '_FillValue', fillval)
639702
where (geolon_v == fillval) mask_v = .true.
640-
where (geolon_v == fillval) geolon_v = 72.51
641-
where (geolat_v == fillval) geolat_v = 42.56
703+
where (geolon_v == fillval) geolon_v = 72.51_r8
704+
where (geolat_v == fillval) geolat_v = 42.56_r8
642705

643706
! mom6 example files have longitude > 360 and longitudes < 0
644707
! DART uses [0,360]
645-
geolon = mod(geolon, 360.0)
646-
geolon_u = mod(geolon_u, 360.0)
647-
geolon_v = mod(geolon_v, 360.0)
708+
geolon = mod(geolon, 360.0_r8)
709+
geolon_u = mod(geolon_u, 360.0_r8)
710+
geolon_v = mod(geolon_v, 360.0_r8)
648711

649-
where (geolon < 0.0) geolon = geolon + 360
650-
where (geolon_u < 0.0) geolon_u = geolon_u + 360
651-
where (geolon_v < 0.0) geolon_v = geolon_v + 360
712+
where (geolon < 0.0) geolon = geolon + 360.0_r8
713+
where (geolon_u < 0.0) geolon_u = geolon_u + 360.0_r8
714+
where (geolon_v < 0.0) geolon_v = geolon_v + 360.0_r8
652715

653716
call nc_close_file(ncid)
654717

@@ -897,6 +960,63 @@ function get_interp_handle(qty)
897960

898961
end function
899962

963+
!------------------------------------------------------------
964+
! calculate sensible (in-situ) temperature from
965+
! local pressure, salinity, and potential temperature
966+
elemental function sensible_temp(potemp, s, lpres)
967+
968+
real(r8), intent(in) :: potemp ! potential temperature in C
969+
real(r8), intent(in) :: s ! salinity Practical Salinity Scale 1978 (PSS-78)
970+
real(r8), intent(in) :: lpres ! pressure in decibars
971+
real(r8) :: sensible_temp ! in-situ (sensible) temperature (C)
972+
973+
integer :: i,j,n
974+
real(r8) :: dp,p,q,r1,r2,r3,r4,r5,s1,t,x
975+
976+
s1 = s - 35.0_r8
977+
p = 0.0_r8
978+
t = potemp
979+
980+
dp = lpres - p
981+
n = int (abs(dp)/1000.0_r8) + 1
982+
dp = dp/n
983+
984+
do i=1,n
985+
do j=1,4
986+
987+
r1 = ((-2.1687e-16_r8 * t + 1.8676e-14_r8) * t - 4.6206e-13_r8) * p
988+
r2 = (2.7759e-12_r8*t - 1.1351e-10_r8) * s1
989+
r3 = ((-5.4481e-14_r8 * t + 8.733e-12_r8) * t - 6.7795e-10_r8) * t
990+
r4 = (r1 + (r2 + r3 + 1.8741e-8_r8)) * p + (-4.2393e-8_r8 * t+1.8932e-6_r8) * s1
991+
r5 = r4 + ((6.6228e-10_r8 * t-6.836e-8_r8) * t + 8.5258e-6_r8) * t + 3.5803e-5_r8
992+
993+
x = dp*r5
994+
995+
if (j == 1) then
996+
t = t + 0.5_r8 * x
997+
q = x
998+
p = p + 0.5_r8 * dp
999+
1000+
else if (j == 2) then
1001+
t = t + 0.29298322_r8 * (x-q)
1002+
q = 0.58578644_r8 * x + 0.121320344_r8 * q
1003+
1004+
else if (j == 3) then
1005+
t = t + 1.707106781_r8 * (x-q)
1006+
q = 3.414213562_r8*x - 4.121320344_r8*q
1007+
p = p + 0.5_r8*dp
1008+
1009+
else ! j must == 4
1010+
t = t + (x - 2.0_r8 * q) / 6.0_r8
1011+
1012+
endif
1013+
1014+
enddo ! j loop
1015+
enddo ! i loop
1016+
1017+
sensible_temp = t
1018+
1019+
end function sensible_temp
9001020

9011021
!------------------------------------------------------------------
9021022
! Verify that the namelist was filled in correctly, and check

0 commit comments

Comments
 (0)