MOM6
MOM_geothermal.F90
1 !> Implemented geothermal heating at the ocean bottom.
3 
4 ! This file is part of MOM6. See LICENSE.md for the license.
5 
6 use mom_diag_mediator, only : post_data, register_diag_field, safe_alloc_ptr
7 use mom_diag_mediator, only : register_static_field, time_type, diag_ctrl
8 use mom_domains, only : pass_var
9 use mom_error_handler, only : mom_error, fatal, warning
11 use mom_io, only : mom_read_data, slasher
12 use mom_grid, only : ocean_grid_type
15 use mom_verticalgrid, only : verticalgrid_type, get_thickness_units
17 
18 implicit none ; private
19 
20 #include <MOM_memory.h>
21 
22 public geothermal_entraining, geothermal_in_place, geothermal_init, geothermal_end
23 
24 !> Control structure for geothermal heating
25 type, public :: geothermal_cs ; private
26  real :: drcv_dt_inplace !< The value of dRcv_dT above which (dRcv_dT is
27  !! negative) the water is heated in place instead
28  !! of moving upward between layers [R degC-1 ~> kg m-3 degC-1].
29  real, pointer :: geo_heat(:,:) => null() !< The geothermal heat flux [J m-2 T-1 ~> W m-2].
30  real :: geothermal_thick !< The thickness over which geothermal heating is
31  !! applied [H ~> m or kg m-2].
32  logical :: apply_geothermal !< If true, geothermal heating will be applied
33  !! otherwise GEOTHERMAL_SCALE has been set to 0 and
34  !! there is no heat to apply.
35 
36  type(time_type), pointer :: time => null() !< A pointer to the ocean model's clock.
37  type(diag_ctrl), pointer :: diag => null() !< A structure that is used to
38  !! regulate the timing of diagnostic output.
39  integer :: id_internal_heat_heat_tendency = -1 !< ID for diagnostic of heat tendency
40  integer :: id_internal_heat_temp_tendency = -1 !< ID for diagnostic of temperature tendency
41  integer :: id_internal_heat_h_tendency = -1 !< ID for diagnostic of thickness tendency
42 
43 end type geothermal_cs
44 
45 contains
46 
47 !> Applies geothermal heating, including the movement of water
48 !! between isopycnal layers to match the target densities. The heating is
49 !! applied to the bottommost layers that occur within GEOTHERMAL_THICKNESS of the bottom. If
50 !! the partial derivative of the coordinate density with temperature is positive
51 !! or very small, the layers are simply heated in place. Any heat that can not
52 !! be applied to the ocean is returned (WHERE)?
53 subroutine geothermal_entraining(h, tv, dt, ea, eb, G, GV, US, CS, halo)
54  type(ocean_grid_type), intent(inout) :: g !< The ocean's grid structure.
55  type(verticalgrid_type), intent(in) :: gv !< The ocean's vertical grid structure.
56  real, dimension(SZI_(G),SZJ_(G),SZK_(G)), intent(inout) :: h !< Layer thicknesses [H ~> m or kg m-2]
57  type(thermo_var_ptrs), intent(inout) :: tv !< A structure containing pointers
58  !! to any available thermodynamic
59  !! fields. Absent fields have NULL
60  !! ptrs.
61  real, intent(in) :: dt !< Time increment [T ~> s].
62  real, dimension(SZI_(G),SZJ_(G),SZK_(G)), intent(inout) :: ea !< The amount of fluid moved
63  !! downward into a layer; this
64  !! should be increased due to mixed
65  !! layer detrainment [H ~> m or kg m-2]
66  real, dimension(SZI_(G),SZJ_(G),SZK_(G)), intent(inout) :: eb !< The amount of fluid moved upward
67  !! into a layer; this should be
68  !! increased due to mixed layer
69  !! entrainment [H ~> m or kg m-2].
70  type(unit_scale_type), intent(in) :: us !< A dimensional unit scaling type
71  type(geothermal_cs), pointer :: cs !< The control structure returned by
72  !! a previous call to
73  !! geothermal_init.
74  integer, optional, intent(in) :: halo !< Halo width over which to work
75  ! Local variables
76  real, dimension(SZI_(G)) :: &
77  heat_rem, & ! remaining heat [H degC ~> m degC or kg degC m-2]
78  h_geo_rem, & ! remaining thickness to apply geothermal heating [H ~> m or kg m-2]
79  rcv_bl, & ! coordinate density in the deepest variable density layer [R ~> kg m-3]
80  p_ref ! coordinate densities reference pressure [R L2 T-2 ~> Pa]
81 
82  real, dimension(2) :: &
83  t2, s2, & ! temp and saln in the present and target layers [degC] and [ppt]
84  drcv_dt_, & ! partial derivative of coordinate density wrt temp [R degC-1 ~> kg m-3 degC-1]
85  drcv_ds_ ! partial derivative of coordinate density wrt saln [R ppt-1 ~> kg m-3 ppt-1]
86 
87  real :: angstrom, h_neglect ! small thicknesses [H ~> m or kg m-2]
88  real :: rcv ! coordinate density of present layer [R ~> kg m-3]
89  real :: rcv_tgt ! coordinate density of target layer [R ~> kg m-3]
90  real :: drcv ! difference between Rcv and Rcv_tgt [R ~> kg m-3]
91  real :: drcv_dt ! partial derivative of coordinate density wrt temp
92  ! in the present layer [R degC-1 ~> kg m-3 degC-1]; usually negative
93  real :: h_heated ! thickness that is being heated [H ~> m or kg m-2]
94  real :: heat_avail ! heating available for the present layer [degC H ~> degC m or degC kg m-2]
95  real :: heat_in_place ! heating to warm present layer w/o movement between layers
96  ! [degC H ~> degC m or degC kg m-2]
97  real :: heat_trans ! heating available to move water from present layer to target
98  ! layer [degC H ~> degC m or degC kg m-2]
99  real :: heating ! heating used to move water from present layer to target layer
100  ! [degC H ~> degC m or degC kg m-2]
101  ! 0 <= heating <= heat_trans
102  real :: h_transfer ! thickness moved between layers [H ~> m or kg m-2]
103  real :: wt_in_place ! relative weighting that goes from 0 to 1 [nondim]
104  real :: i_h ! inverse thickness [H-1 ~> m-1 or m2 kg-1]
105  real :: dtemp ! temperature increase in a layer [degC]
106  real :: irho_cp ! inverse of heat capacity per unit layer volume
107  ! [degC H Q-1 R-1 Z-1 ~> degC m3 J-1 or degC kg J-1]
108 
109  real, dimension(SZI_(G),SZJ_(G),SZK_(G)) :: t_old ! Temperature of each layer
110  ! before any heat is added,
111  ! for diagnostics [degC]
112  real, dimension(SZI_(G),SZJ_(G),SZK_(G)) :: h_old ! Thickness of each layer
113  ! before any heat is added,
114  ! for diagnostics [H ~> m or kg m-2]
115  real, dimension(SZI_(G),SZJ_(G),SZK_(G)) :: work_3d ! Scratch variable used to
116  ! calculate change in heat
117  ! due to geothermal
118  real :: idt ! inverse of the timestep [T-1 ~> s-1]
119 
120  logical :: do_i(szi_(g))
121  logical :: compute_h_old, compute_t_old
122  integer :: i, j, k, is, ie, js, je, nz, k2, i2
123  integer :: isj, iej, num_left, nkmb, k_tgt
124 
125  is = g%isc ; ie = g%iec ; js = g%jsc ; je = g%jec ; nz = g%ke
126  if (present(halo)) then
127  is = g%isc-halo ; ie = g%iec+halo ; js = g%jsc-halo ; je = g%jec+halo
128  endif
129 
130  if (.not. associated(cs)) call mom_error(fatal, "MOM_geothermal: "//&
131  "Module must be initialized before it is used.")
132  if (.not.cs%apply_geothermal) return
133 
134  nkmb = gv%nk_rho_varies
135  irho_cp = 1.0 / (gv%H_to_RZ * tv%C_p)
136  angstrom = gv%Angstrom_H
137  h_neglect = gv%H_subroundoff
138  p_ref(:) = tv%P_Ref
139  idt = 1.0 / dt
140 
141  if (.not.associated(tv%T)) call mom_error(fatal, "MOM geothermal_entraining: "//&
142  "Geothermal heating can only be applied if T & S are state variables.")
143 
144 ! do j=js,je ; do i=is,ie
145 ! resid(i,j) = tv%internal_heat(i,j)
146 ! enddo ; enddo
147 
148  ! Conditionals for tracking diagnostic depdendencies
149  compute_h_old = cs%id_internal_heat_h_tendency > 0 &
150  .or. cs%id_internal_heat_heat_tendency > 0 &
151  .or. cs%id_internal_heat_temp_tendency > 0
152 
153  compute_t_old = cs%id_internal_heat_heat_tendency > 0 &
154  .or. cs%id_internal_heat_temp_tendency > 0
155 
156  if (cs%id_internal_heat_heat_tendency > 0) work_3d(:,:,:) = 0.0
157 
158  if (compute_h_old .or. compute_t_old) then ; do k=1,nz ; do j=js,je ; do i=is,ie
159  ! Save temperature and thickness before any changes are made (for diagnostics)
160  h_old(i,j,k) = h(i,j,k)
161  t_old(i,j,k) = tv%T(i,j,k)
162  enddo ; enddo ; enddo ; endif
163 
164 !$OMP parallel do default(none) shared(is,ie,js,je,G,GV,US,CS,dt,Irho_cp,nkmb,tv, &
165 !$OMP p_Ref,h,Angstrom,nz,H_neglect,eb, &
166 !$OMP h_old,T_old,work_3d,Idt) &
167 !$OMP private(heat_rem,do_i,h_geo_rem,num_left, &
168 !$OMP isj,iej,Rcv_BL,h_heated,heat_avail,k_tgt, &
169 !$OMP Rcv_tgt,Rcv,dRcv_dT,T2,S2,dRcv_dT_, &
170 !$OMP dRcv_dS_,heat_in_place,heat_trans, &
171 !$OMP wt_in_place,dTemp,dRcv,h_transfer,heating, &
172 !$OMP I_h)
173 
174  do j=js,je
175  ! 1. Only work on columns that are being heated.
176  ! 2. Find the deepest layer with any mass.
177  ! 3. Find the partial derivative of locally referenced potential density
178  ! and coordinate density with temperature, and the density of the layer
179  ! and the layer above.
180  ! 4. Heat a portion of the bottommost layer until it matches the target
181  ! density of the layer above, and move it.
182  ! 4a. In the case of variable density layers, heat but do not move.
183  ! 5. If there is still heat left over, repeat for the next layer up.
184  ! This subroutine updates thickness, T & S, and increments eb accordingly.
185 
186  ! 6. If there is not enough mass in the ocean, pass some of the heat up
187  ! from the ocean via the frazil field?
188 
189  num_left = 0
190  do i=is,ie
191  heat_rem(i) = g%mask2dT(i,j) * (cs%geo_heat(i,j) * (dt*irho_cp))
192  do_i(i) = .true. ; if (heat_rem(i) <= 0.0) do_i(i) = .false.
193  if (do_i(i)) num_left = num_left + 1
194  h_geo_rem(i) = cs%Geothermal_thick
195  enddo
196  if (num_left == 0) cycle
197 
198  ! Find the first and last columns that need to be worked on.
199  isj = ie+1 ; do i=is,ie ; if (do_i(i)) then ; isj = i ; exit ; endif ; enddo
200  iej = is-1 ; do i=ie,is,-1 ; if (do_i(i)) then ; iej = i ; exit ; endif ; enddo
201 
202  if (nkmb > 0) then
203  call calculate_density(tv%T(:,j,nkmb), tv%S(:,j,nkmb), p_ref(:), rcv_bl(:), &
204  tv%eqn_of_state, (/isj-(g%isd-1),iej-(g%isd-1)/) )
205  else
206  rcv_bl(:) = -1.0
207  endif
208 
209  do k=nz,1,-1
210  do i=isj,iej ; if (do_i(i)) then
211 
212  if (h(i,j,k) > angstrom) then
213  if ((h(i,j,k)-angstrom) >= h_geo_rem(i)) then
214  h_heated = h_geo_rem(i)
215  heat_avail = heat_rem(i)
216  h_geo_rem(i) = 0.0
217  else
218  h_heated = (h(i,j,k)-angstrom)
219  heat_avail = heat_rem(i) * (h_heated / &
220  (h_geo_rem(i) + h_neglect))
221  h_geo_rem(i) = h_geo_rem(i) - h_heated
222  endif
223 
224  if (k<=nkmb .or. nkmb<=0) then
225  ! Simply heat the layer; convective adjustment occurs later
226  ! if necessary.
227  k_tgt = k
228  elseif ((k==nkmb+1) .or. (gv%Rlay(k-1) < rcv_bl(i))) then
229  ! Add enough heat to match the lowest buffer layer density.
230  k_tgt = nkmb
231  rcv_tgt = rcv_bl(i)
232  else
233  ! Add enough heat to match the target density of layer k-1.
234  k_tgt = k-1
235  rcv_tgt = gv%Rlay(k-1)
236  endif
237 
238  if (k<=nkmb .or. nkmb<=0) then
239  rcv = 0.0 ; drcv_dt = 0.0 ! Is this OK?
240  else
241  call calculate_density(tv%T(i,j,k), tv%S(i,j,k), tv%P_Ref, &
242  rcv, tv%eqn_of_state)
243  t2(1) = tv%T(i,j,k) ; s2(1) = tv%S(i,j,k)
244  t2(2) = tv%T(i,j,k_tgt) ; s2(2) = tv%S(i,j,k_tgt)
245  call calculate_density_derivs(t2(:), s2(:), p_ref(:), drcv_dt_, drcv_ds_, &
246  tv%eqn_of_state, (/1,2/) )
247  drcv_dt = 0.5*(drcv_dt_(1) + drcv_dt_(2))
248  endif
249 
250  if ((drcv_dt >= 0.0) .or. (k<=nkmb .or. nkmb<=0)) then
251  ! This applies to variable density layers.
252  heat_in_place = heat_avail
253  heat_trans = 0.0
254  elseif (drcv_dt <= cs%dRcv_dT_inplace) then
255  ! This is the option that usually applies in isopycnal coordinates.
256  heat_in_place = min(heat_avail, max(0.0, h(i,j,k) * &
257  ((gv%Rlay(k)-rcv) / drcv_dt)))
258  heat_trans = heat_avail - heat_in_place
259  else
260  ! wt_in_place should go from 0 to 1.
261  wt_in_place = (cs%dRcv_dT_inplace - drcv_dt) / cs%dRcv_dT_inplace
262  heat_in_place = max(wt_in_place*heat_avail, &
263  h(i,j,k) * ((gv%Rlay(k)-rcv) / drcv_dt) )
264  heat_trans = heat_avail - heat_in_place
265  endif
266 
267  if (heat_in_place > 0.0) then
268  ! This applies to variable density layers. In isopycnal coordinates
269  ! this only arises for relatively fresh water near the freezing
270  ! point, in which case heating in place will eventually cause things
271  ! to sort themselves out, if only because the water will warm to
272  ! the temperature of maximum density.
273  dtemp = heat_in_place / (h(i,j,k) + h_neglect)
274  tv%T(i,j,k) = tv%T(i,j,k) + dtemp
275  heat_rem(i) = heat_rem(i) - heat_in_place
276  rcv = rcv + drcv_dt * dtemp
277  endif
278 
279  if (heat_trans > 0.0) then
280  ! The second expression might never be used, but will avoid
281  ! division by 0.
282  drcv = max(rcv - rcv_tgt, 0.0)
283 
284  ! dTemp = -dRcv / dRcv_dT
285  ! h_transfer = min(heat_rem(i) / dTemp, h(i,j,k)-Angstrom)
286  if ((-drcv_dt * heat_trans) >= drcv * (h(i,j,k)-angstrom)) then
287  h_transfer = h(i,j,k) - angstrom
288  heating = (h_transfer * drcv) / (-drcv_dt)
289  ! Since not all the heat has been applied, return the fraction
290  ! of the layer thickness that has not yet been fully heated to
291  ! the h_geo_rem.
292  h_geo_rem(i) = h_geo_rem(i) + h_heated * &
293  ((heat_avail - (heating + heat_in_place)) / heat_avail)
294  else
295  h_transfer = (-drcv_dt * heat_trans) / drcv
296  heating = heat_trans
297  endif
298  heat_rem(i) = heat_rem(i) - heating
299 
300  i_h = 1.0 / ((h(i,j,k_tgt) + h_neglect) + h_transfer)
301  tv%T(i,j,k_tgt) = ((h(i,j,k_tgt) + h_neglect) * tv%T(i,j,k_tgt) + &
302  (h_transfer * tv%T(i,j,k) + heating)) * i_h
303  tv%S(i,j,k_tgt) = ((h(i,j,k_tgt) + h_neglect) * tv%S(i,j,k_tgt) + &
304  h_transfer * tv%S(i,j,k)) * i_h
305 
306  h(i,j,k) = h(i,j,k) - h_transfer
307  h(i,j,k_tgt) = h(i,j,k_tgt) + h_transfer
308  eb(i,j,k_tgt) = eb(i,j,k_tgt) + h_transfer
309  if (k_tgt < k-1) then
310  do k2 = k_tgt+1,k-1
311  eb(i,j,k2) = eb(i,j,k2) + h_transfer
312  enddo
313  endif
314  endif
315 
316  if (heat_rem(i) <= 0.0) then
317  do_i(i) = .false. ; num_left = num_left-1
318  ! For efficiency, uncomment these?
319  ! if ((i==isj) .and. (num_left > 0)) then ; do i2=isj+1,iej ; if (do_i(i2)) then
320  ! isj = i2 ; exit ! Set the new starting value.
321  ! endif ; enddo ; endif
322  ! if ((i==iej) .and. (num_left > 0)) then ; do i2=iej-1,isj,-1 ; if (do_i(i2)) then
323  ! iej = i2 ; exit ! Set the new ending value.
324  ! endif ; enddo ; endif
325  endif
326  endif
327 
328  ! Calculate heat tendency due to addition and transfer of internal heat
329  if (cs%id_internal_heat_heat_tendency > 0) then
330  work_3d(i,j,k) = ((gv%H_to_RZ*tv%C_p) * idt) * (h(i,j,k) * tv%T(i,j,k) - h_old(i,j,k) * t_old(i,j,k))
331  endif
332 
333  endif ; enddo
334  if (num_left <= 0) exit
335  enddo ! k-loop
336 
337  if (associated(tv%internal_heat)) then ; do i=is,ie
338  tv%internal_heat(i,j) = tv%internal_heat(i,j) + gv%H_to_RZ * &
339  (g%mask2dT(i,j) * (cs%geo_heat(i,j) * (dt*irho_cp)) - heat_rem(i))
340  enddo ; endif
341  enddo ! j-loop
342 
343  ! Post diagnostic of 3D tendencies (heat, temperature, and thickness) due to internal heat
344  if (cs%id_internal_heat_heat_tendency > 0) then
345  call post_data(cs%id_internal_heat_heat_tendency, work_3d, cs%diag, alt_h=h_old)
346  endif
347  if (cs%id_internal_heat_temp_tendency > 0) then
348  do k=1,nz ; do j=js,je ; do i=is,ie
349  work_3d(i,j,k) = idt * (tv%T(i,j,k) - t_old(i,j,k))
350  enddo; enddo; enddo
351  call post_data(cs%id_internal_heat_temp_tendency, work_3d, cs%diag, alt_h=h_old)
352  endif
353  if (cs%id_internal_heat_h_tendency > 0) then
354  do k=1,nz ; do j=js,je ; do i=is,ie
355  work_3d(i,j,k) = idt * (h(i,j,k) - h_old(i,j,k))
356  enddo ; enddo ; enddo
357  call post_data(cs%id_internal_heat_h_tendency, work_3d, cs%diag, alt_h=h_old)
358  endif
359 
360 ! do j=js,je ; do i=is,ie
361 ! resid(i,j) = tv%internal_heat(i,j) - resid(i,j) - GV%H_to_RZ * &
362 ! (G%mask2dT(i,j) * (CS%geo_heat(i,j) * (dt*Irho_cp)))
363 ! enddo ; enddo
364 
365 end subroutine geothermal_entraining
366 
367 !> Applies geothermal heating to the bottommost layers that occur within GEOTHERMAL_THICKNESS of
368 !! the bottom, by simply heating the water in place. Any heat that can not be applied to the ocean
369 !! is returned (WHERE)?
370 subroutine geothermal_in_place(h, tv, dt, G, GV, US, CS, halo)
371  type(ocean_grid_type), intent(inout) :: g !< The ocean's grid structure.
372  type(verticalgrid_type), intent(in) :: gv !< The ocean's vertical grid structure.
373  real, dimension(SZI_(G),SZJ_(G),SZK_(G)), intent(in) :: h !< Layer thicknesses [H ~> m or kg m-2]
374  type(thermo_var_ptrs), intent(inout) :: tv !< A structure containing pointers
375  !! to any available thermodynamic
376  !! fields. Absent fields have NULL
377  !! ptrs.
378  real, intent(in) :: dt !< Time increment [T ~> s].
379  type(unit_scale_type), intent(in) :: us !< A dimensional unit scaling type
380  type(geothermal_cs), pointer :: cs !< The control structure returned by
381  !! a previous call to geothermal_init.
382  integer, optional, intent(in) :: halo !< Halo width over which to work
383 
384  ! Local variables
385  real, dimension(SZI_(G)) :: &
386  heat_rem, & ! remaining heat [H degC ~> m degC or kg degC m-2]
387  h_geo_rem ! remaining thickness to apply geothermal heating [H ~> m or kg m-2]
388 
389  real :: angstrom, h_neglect ! small thicknesses [H ~> m or kg m-2]
390  real :: heat_here ! heating applied to the present layer [degC H ~> degC m or degC kg m-2]
391  real :: dtemp ! temperature increase in a layer [degC]
392  real :: irho_cp ! inverse of heat capacity per unit layer volume
393  ! [degC H Q-1 R-1 Z-1 ~> degC m3 J-1 or degC kg J-1]
394 
395  real, dimension(SZI_(G),SZJ_(G),SZK_(G)) :: &
396  dtdt_diag ! Diagnostic of temperature tendency [degC T-1 ~> degC s-1] which might be
397  ! converted into a layer-integrated heat tendency [Q R Z T-1 ~> W m-2]
398  real :: idt ! inverse of the timestep [T-1 ~> s-1]
399  logical :: do_any ! True if there is more to be done on the current j-row.
400  logical :: calc_diags ! True if diagnostic tendencies are needed.
401  integer :: i, j, k, is, ie, js, je, nz, i2, isj, iej
402 
403  is = g%isc ; ie = g%iec ; js = g%jsc ; je = g%jec ; nz = g%ke
404  if (present(halo)) then
405  is = g%isc-halo ; ie = g%iec+halo ; js = g%jsc-halo ; je = g%jec+halo
406  endif
407 
408  if (.not. associated(cs)) call mom_error(fatal, "MOM_geothermal: "//&
409  "Module must be initialized before it is used.")
410  if (.not.cs%apply_geothermal) return
411 
412  irho_cp = 1.0 / (gv%H_to_RZ * tv%C_p)
413  angstrom = gv%Angstrom_H
414  h_neglect = gv%H_subroundoff
415  idt = 1.0 / dt
416 
417  if (.not.associated(tv%T)) call mom_error(fatal, "MOM geothermal_in_place: "//&
418  "Geothermal heating can only be applied if T & S are state variables.")
419 
420 ! do i=is,ie ; do j=js,je
421 ! resid(i,j) = tv%internal_heat(i,j)
422 ! enddo ; enddo
423 
424  ! Conditionals for tracking diagnostic depdendencies
425  calc_diags = (cs%id_internal_heat_heat_tendency > 0) .or. (cs%id_internal_heat_temp_tendency > 0)
426 
427  if (calc_diags) dtdt_diag(:,:,:) = 0.0
428 
429  !$OMP parallel do default(shared) private(heat_rem,do_any,h_geo_rem,isj,iej,heat_here,dTemp)
430  do j=js,je
431  ! Only work on columns that are being heated, and heat the near-bottom water.
432 
433  ! If there is not enough mass in the ocean, pass some of the heat up
434  ! from the ocean via the frazil field?
435 
436  do_any = .false.
437  do i=is,ie
438  heat_rem(i) = g%mask2dT(i,j) * (cs%geo_heat(i,j) * (dt*irho_cp))
439  if (heat_rem(i) > 0.0) do_any = .true.
440  h_geo_rem(i) = cs%Geothermal_thick
441  enddo
442  if (.not.do_any) cycle
443 
444  ! Find the first and last columns that need to be worked on.
445  isj = ie+1 ; do i=is,ie ; if (heat_rem(i) > 0.0) then ; isj = i ; exit ; endif ; enddo
446  iej = is-1 ; do i=ie,is,-1 ; if (heat_rem(i) > 0.0) then ; iej = i ; exit ; endif ; enddo
447 
448  do k=nz,1,-1
449  do_any = .false.
450  do i=isj,iej
451  if ((heat_rem(i) > 0.0) .and. (h(i,j,k) > angstrom)) then
452  ! Apply some or all of the remaining heat to this layer.
453  ! Convective adjustment occurs outside of this module if necessary.
454  if ((h(i,j,k)-angstrom) >= h_geo_rem(i)) then
455  heat_here = heat_rem(i)
456  h_geo_rem(i) = 0.0
457  heat_rem(i) = 0.0
458  else
459  heat_here = heat_rem(i) * ((h(i,j,k)-angstrom) / (h_geo_rem(i) + h_neglect))
460  h_geo_rem(i) = h_geo_rem(i) - (h(i,j,k)-angstrom)
461  heat_rem(i) = heat_rem(i) - heat_here
462  endif
463 
464  dtemp = heat_here / (h(i,j,k) + h_neglect)
465  tv%T(i,j,k) = tv%T(i,j,k) + dtemp
466  if (calc_diags) dtdt_diag(i,j,k) = dtemp * idt
467  endif
468 
469  if (heat_rem(i) > 0.0) do_any= .true.
470  enddo
471 
472  if (.not.do_any) exit
473  enddo ! k-loop
474 
475  if (associated(tv%internal_heat)) then ; do i=is,ie
476  tv%internal_heat(i,j) = tv%internal_heat(i,j) + gv%H_to_RZ * &
477  (g%mask2dT(i,j) * (cs%geo_heat(i,j) * (dt*irho_cp)) - heat_rem(i))
478  enddo ; endif
479  enddo ! j-loop
480 
481  ! Post diagnostics of 3D tendencies of heat and temperature due to geothermal heat
482  if (cs%id_internal_heat_temp_tendency > 0) then
483  call post_data(cs%id_internal_heat_temp_tendency, dtdt_diag, cs%diag, alt_h=h)
484  endif
485  if (cs%id_internal_heat_heat_tendency > 0) then
486  do k=1,nz ; do j=js,je ; do i=is,ie
487  ! Dangerously reuse dTdt_diag for a related variable with different units, going from
488  ! units of [degC T-1 ~> degC s-1] to units of [Q R Z T-1 ~> W m-2]
489  dtdt_diag(i,j,k) = (gv%H_to_RZ*tv%C_p) * (h(i,j,k) * dtdt_diag(i,j,k))
490  enddo ; enddo ; enddo
491  call post_data(cs%id_internal_heat_heat_tendency, dtdt_diag, cs%diag, alt_h=h)
492  endif
493 
494 ! do j=js,je ; do i=is,ie
495 ! resid(i,j) = tv%internal_heat(i,j) - resid(i,j) - GV%H_to_RZ * &
496 ! (G%mask2dT(i,j) * (CS%geo_heat(i,j) * (dt*Irho_cp)))
497 ! enddo ; enddo
498 
499 end subroutine geothermal_in_place
500 
501 !> Initialize parameters and allocate memory associated with the geothermal heating module.
502 subroutine geothermal_init(Time, G, GV, US, param_file, diag, CS, useALEalgorithm)
503  type(time_type), target, intent(in) :: time !< Current model time.
504  type(ocean_grid_type), intent(inout) :: g !< The ocean's grid structure.
505  type(verticalgrid_type), intent(in) :: gv !< The ocean's vertical grid structure.
506  type(unit_scale_type), intent(in) :: us !< A dimensional unit scaling type
507  type(param_file_type), intent(in) :: param_file !< A structure to parse for run-time
508  !! parameters.
509  type(diag_ctrl), target, intent(inout) :: diag !< Structure used to regulate diagnostic output.
510  type(geothermal_cs), pointer :: cs !< Pointer pointing to the module control
511  !! structure.
512  logical, optional, intent(in) :: usealealgorithm !< logical for whether to use ALE remapping
513 
514 ! This include declares and sets the variable "version".
515 #include "version_variable.h"
516  character(len=40) :: mdl = "MOM_geothermal" ! module name
517  character(len=48) :: thickness_units
518  ! Local variables
519  character(len=200) :: inputdir, geo_file, filename, geotherm_var
520  real :: geo_scale ! A constant heat flux or dimensionally rescaled geothermal flux scaling factor
521  ! [Q R Z T-1 ~> W m-2] or [Q R Z m2 s J-1 T-1 ~> 1]
522  integer :: i, j, isd, ied, jsd, jed, id
523  isd = g%isd ; ied = g%ied ; jsd = g%jsd ; jed = g%jed
524 
525  if (associated(cs)) then
526  call mom_error(warning, "geothermal_init called with an associated"// &
527  "associated control structure.")
528  return
529  else ; allocate(cs) ; endif
530 
531  cs%diag => diag
532  cs%Time => time
533 
534  ! write parameters to the model log.
535  call log_version(param_file, mdl, version, "")
536  call get_param(param_file, mdl, "GEOTHERMAL_SCALE", geo_scale, &
537  "The constant geothermal heat flux, a rescaling "//&
538  "factor for the heat flux read from GEOTHERMAL_FILE, or "//&
539  "0 to disable the geothermal heating.", &
540  units="W m-2 or various", default=0.0, scale=us%W_m2_to_QRZ_T)
541  cs%apply_geothermal = .not.(geo_scale == 0.0)
542  if (.not.cs%apply_geothermal) return
543 
544  call safe_alloc_ptr(cs%geo_heat, isd, ied, jsd, jed) ; cs%geo_heat(:,:) = 0.0
545 
546  call get_param(param_file, mdl, "GEOTHERMAL_FILE", geo_file, &
547  "The file from which the geothermal heating is to be "//&
548  "read, or blank to use a constant heating rate.", default=" ")
549  call get_param(param_file, mdl, "GEOTHERMAL_THICKNESS", cs%geothermal_thick, &
550  "The thickness over which to apply geothermal heating.", &
551  units="m", default=0.1, scale=gv%m_to_H)
552  call get_param(param_file, mdl, "GEOTHERMAL_DRHO_DT_INPLACE", cs%dRcv_dT_inplace, &
553  "The value of drho_dT above which geothermal heating "//&
554  "simply heats water in place instead of moving it between "//&
555  "isopycnal layers. This must be negative.", &
556  units="kg m-3 K-1", scale=us%kg_m3_to_R, default=-0.01)
557  if (cs%dRcv_dT_inplace >= 0.0) call mom_error(fatal, "geothermal_init: "//&
558  "GEOTHERMAL_DRHO_DT_INPLACE must be negative.")
559 
560  if (len_trim(geo_file) >= 1) then
561  call get_param(param_file, mdl, "INPUTDIR", inputdir, default=".")
562  inputdir = slasher(inputdir)
563  filename = trim(inputdir)//trim(geo_file)
564  call log_param(param_file, mdl, "INPUTDIR/GEOTHERMAL_FILE", filename)
565  call get_param(param_file, mdl, "GEOTHERMAL_VARNAME", geotherm_var, &
566  "The name of the geothermal heating variable in "//&
567  "GEOTHERMAL_FILE.", default="geo_heat")
568  call mom_read_data(filename, trim(geotherm_var), cs%geo_heat, g%Domain)
569  do j=jsd,jed ; do i=isd,ied
570  cs%geo_heat(i,j) = (g%mask2dT(i,j) * geo_scale) * cs%geo_heat(i,j)
571  enddo ; enddo
572  else
573  do j=jsd,jed ; do i=isd,ied
574  cs%geo_heat(i,j) = g%mask2dT(i,j) * geo_scale
575  enddo ; enddo
576  endif
577  call pass_var(cs%geo_heat, g%domain)
578 
579  thickness_units = get_thickness_units(gv)
580 
581  ! post the static geothermal heating field
582  id = register_static_field('ocean_model', 'geo_heat', diag%axesT1, &
583  'Geothermal heat flux into ocean', 'W m-2', conversion=us%QRZ_T_to_W_m2, &
584  cmor_field_name='hfgeou', cmor_units='W m-2', &
585  cmor_standard_name='upward_geothermal_heat_flux_at_sea_floor', &
586  cmor_long_name='Upward geothermal heat flux at sea floor', &
587  x_cell_method='mean', y_cell_method='mean', area_cell_method='mean')
588  if (id > 0) call post_data(id, cs%geo_heat, diag, .true.)
589 
590  ! Diagnostic for tendencies due to internal heat (in 3d)
591  cs%id_internal_heat_heat_tendency=register_diag_field('ocean_model', &
592  'internal_heat_heat_tendency', diag%axesTL, time, &
593  'Heat tendency (in 3D) due to internal (geothermal) sources', &
594  'W m-2', conversion=us%QRZ_T_to_W_m2, v_extensive=.true.)
595  cs%id_internal_heat_temp_tendency=register_diag_field('ocean_model', &
596  'internal_heat_temp_tendency', diag%axesTL, time, &
597  'Temperature tendency (in 3D) due to internal (geothermal) sources', &
598  'degC s-1', conversion=us%s_to_T, v_extensive=.true.)
599  if (present(usealealgorithm)) then ; if (.not.usealealgorithm) then
600  ! Do not offer this diagnostic if heating will be in place.
601  cs%id_internal_heat_h_tendency=register_diag_field('ocean_model', &
602  'internal_heat_h_tendency', diag%axesTL, time, &
603  'Thickness tendency (in 3D) due to internal (geothermal) sources', &
604  trim(thickness_units), conversion=gv%H_to_MKS*us%s_to_T, v_extensive=.true.)
605  endif ; endif
606 
607 end subroutine geothermal_init
608 
609 !> Clean up and deallocate memory associated with the geothermal heating module.
610 subroutine geothermal_end(CS)
611  type(geothermal_cs), pointer :: cs !< Geothermal heating control structure that
612  !! will be deallocated in this subroutine.
613 
614  if (associated(cs%geo_heat)) deallocate(cs%geo_heat)
615  if (associated(cs)) deallocate(cs)
616 end subroutine geothermal_end
617 
618 !> \namespace mom_geothermal
619 !!
620 !! Geothermal heating can be added either in a layered isopycnal mode, in which the heating raises the density
621 !! of the layer to the target density of the layer above, and then moves the water into that layer, or in a
622 !! simple Eulerian mode, in which the bottommost GEOTHERMAL_THICKNESS are heated. Geothermal heating will also
623 !! provide a buoyant source of bottom TKE that can be used to further mix the near-bottom water. In cold fresh
624 !! water lakes where heating increases density, water should be moved into deeper layers, but this is not
625 !! implemented yet.
626 
627 end module mom_geothermal
Implemented geothermal heating at the ocean bottom.
Ocean grid type. See mom_grid for details.
Definition: MOM_grid.F90:26
The following data type a list of diagnostic fields an their variants, as well as variables that cont...
Calculates density of sea water from T, S and P.
Definition: MOM_EOS.F90:68
A structure that can be parsed to read and document run-time parameters.
Provides the ocean grid type.
Definition: MOM_grid.F90:2
This module contains I/O framework code.
Definition: MOM_io.F90:2
The MOM6 facility to parse input files for runtime parameters.
An overloaded interface to log the values of various types of parameters.
Make a diagnostic available for averaging or output.
Describes various unit conversion factors.
Provides a transparent unit rescaling type to facilitate dimensional consistency testing.
Describes the decomposed MOM domain and has routines for communications across PEs.
Definition: MOM_domains.F90:2
The subroutines here provide convenient wrappers to the fms diag_manager interfaces with additional d...
Routines for error handling and I/O management.
Calculate the derivatives of density with temperature and salinity from T, S, and P.
Definition: MOM_EOS.F90:81
Provides subroutines for quantities specific to the equation of state.
Definition: MOM_EOS.F90:2
An overloaded interface to log version information about modules.
Describes the vertical ocean grid, including unit conversion factors.
Pointers to an assortment of thermodynamic fields that may be available, including potential temperat...
Provides a transparent vertical ocean grid type and supporting routines.
Provides transparent structures with groups of MOM6 variables and supporting routines.
Control structure for geothermal heating.
Do a halo update on an array.
Definition: MOM_domains.F90:54
Read a data field from a file.
Definition: MOM_io.F90:74
An overloaded interface to read and log the values of various types of parameters.