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
16 
17 implicit none ; private
18 
19 #include <MOM_memory.h>
20 
21 public geothermal, geothermal_init, geothermal_end
22 
23 !> Control structure for geothermal heating
24 type, public :: geothermal_cs ; private
25  real :: drcv_dt_inplace !< The value of dRcv_dT above which (dRcv_dT is
26  !! negative) the water is heated in place instead
27  !! of moving upward between layers [kg m-3 degC-1].
28  real, pointer :: geo_heat(:,:) => null() !< The geothermal heat flux [W m-2].
29  real :: geothermal_thick !< The thickness over which geothermal heating is
30  !! applied [m] (not [H]).
31  logical :: apply_geothermal !< If true, geothermal heating will be applied
32  !! otherwise GEOTHERMAL_SCALE has been set to 0 and
33  !! there is no heat to apply.
34 
35  type(time_type), pointer :: time => null() !< A pointer to the ocean model's clock.
36  type(diag_ctrl), pointer :: diag => null() !< A structure that is used to
37  !! regulate the timing of diagnostic output.
38 end type geothermal_cs
39 
40 contains
41 
42 !> Applies geothermal heating, including the movement of water
43 !! between isopycnal layers to match the target densities. The heating is
44 !! applied to the bottommost layers that occur within ### of the bottom. If
45 !! the partial derivative of the coordinate density with temperature is positive
46 !! or very small, the layers are simply heated in place. Any heat that can not
47 !! be applied to the ocean is returned (WHERE)?
48 subroutine geothermal(h, tv, dt, ea, eb, G, GV, CS, halo)
49  type(ocean_grid_type), intent(inout) :: g !< The ocean's grid structure.
50  type(verticalgrid_type), intent(in) :: gv !< The ocean's vertical grid structure.
51  real, dimension(SZI_(G),SZJ_(G),SZK_(G)), intent(inout) :: h !< Layer thicknesses [H ~> m or kg m-2]
52  type(thermo_var_ptrs), intent(inout) :: tv !< A structure containing pointers
53  !! to any available thermodynamic
54  !! fields. Absent fields have NULL
55  !! ptrs.
56  real, intent(in) :: dt !< Time increment [s].
57  real, dimension(SZI_(G),SZJ_(G),SZK_(G)), intent(inout) :: ea !< The amount of fluid moved
58  !! downward into a layer; this
59  !! should be increased due to mixed
60  !! layer detrainment [H ~> m or kg m-2]
61  real, dimension(SZI_(G),SZJ_(G),SZK_(G)), intent(inout) :: eb !< The amount of fluid moved upward
62  !! into a layer; this should be
63  !! increased due to mixed layer
64  !! entrainment [H ~> m or kg m-2].
65  type(geothermal_cs), pointer :: cs !< The control structure returned by
66  !! a previous call to
67  !! geothermal_init.
68  integer, optional, intent(in) :: halo !< Halo width over which to work
69  ! Local variables
70  real, dimension(SZI_(G)) :: &
71  heat_rem, & ! remaining heat [H degC ~> m degC or kg degC m-2]
72  h_geo_rem, & ! remaining thickness to apply geothermal heating [H ~> m or kg m-2]
73  rcv_bl, & ! coordinate density in the deepest variable density layer [kg m-3]
74  p_ref ! coordiante densities reference pressure [Pa]
75 
76  real, dimension(2) :: &
77  t2, s2, & ! temp and saln in the present and target layers [degC] and [ppt]
78  drcv_dt_, & ! partial derivative of coordinate density wrt temp [kg m-3 degC-1]
79  drcv_ds_ ! partial derivative of coordinate density wrt saln [kg m-3 ppt-1]
80 
81  real :: angstrom, h_neglect ! small thicknesses [H ~> m or kg m-2]
82  real :: rcv ! coordinate density of present layer [kg m-3]
83  real :: rcv_tgt ! coordinate density of target layer [kg m-3]
84  real :: drcv ! difference between Rcv and Rcv_tgt [kg m-3]
85  real :: drcv_dt ! partial derivative of coordinate density wrt temp
86  ! in the present layer [kg m-3 degC-1]; usually negative
87  real :: h_heated ! thickness that is being heated [H ~> m or kg m-2]
88  real :: heat_avail ! heating available for the present layer [degC H ~> degC m or degC kg m-2]
89  real :: heat_in_place ! heating to warm present layer w/o movement between layers
90  ! [degC H ~> degC m or degC kg m-2]
91  real :: heat_trans ! heating available to move water from present layer to target
92  ! layer [degC H ~> degC m or degC kg m-2]
93  real :: heating ! heating used to move water from present layer to target layer
94  ! [degC H ~> degC m or degC kg m-2]
95  ! 0 <= heating <= heat_trans
96  real :: h_transfer ! thickness moved between layers [H ~> m or kg m-2]
97  real :: wt_in_place ! relative weighting that goes from 0 to 1 [nondim]
98  real :: i_h ! inverse thickness [H-1 ~> m-1 or m2 kg-1]
99  real :: dtemp ! temperature increase in a layer [degC]
100  real :: irho_cp ! inverse of heat capacity per unit layer volume
101  ! [degC H m2 J-1 ~> degC m3 J-1 or degC kg J-1]
102 
103  logical :: do_i(szi_(g))
104  integer :: i, j, k, is, ie, js, je, nz, k2, i2
105  integer :: isj, iej, num_start, num_left, nkmb, k_tgt
106 
107 
108  is = g%isc ; ie = g%iec ; js = g%jsc ; je = g%jec ; nz = g%ke
109  if (present(halo)) then
110  is = g%isc-halo ; ie = g%iec+halo ; js = g%jsc-halo ; je = g%jec+halo
111  endif
112 
113  if (.not. associated(cs)) call mom_error(fatal, "MOM_geothermal: "//&
114  "Module must be initialized before it is used.")
115  if (.not.cs%apply_geothermal) return
116 
117  nkmb = gv%nk_rho_varies
118  irho_cp = 1.0 / (gv%H_to_kg_m2 * tv%C_p)
119  angstrom = gv%Angstrom_H
120  h_neglect = gv%H_subroundoff
121  p_ref(:) = tv%P_Ref
122 
123  if (.not.associated(tv%T)) call mom_error(fatal, "MOM geothermal: "//&
124  "Geothermal heating can only be applied if T & S are state variables.")
125 
126 ! do i=is,ie ; do j=js,je
127 ! resid(i,j) = tv%internal_heat(i,j)
128 ! enddo ; enddo
129 
130 !$OMP parallel do default(none) shared(is,ie,js,je,G,GV,CS,dt,Irho_cp,nkmb,tv, &
131 !$OMP p_Ref,h,Angstrom,nz,H_neglect,eb) &
132 !$OMP private(num_start,heat_rem,do_i,h_geo_rem,num_left,&
133 !$OMP isj,iej,Rcv_BL,h_heated,heat_avail,k_tgt, &
134 !$OMP Rcv_tgt,Rcv,dRcv_dT,T2,S2,dRcv_dT_, &
135 !$OMP dRcv_dS_,heat_in_place,heat_trans, &
136 !$OMP wt_in_place,dTemp,dRcv,h_transfer,heating, &
137 !$OMP I_h)
138 
139  do j=js,je
140  ! 1. Only work on columns that are being heated.
141  ! 2. Find the deepest layer with any mass.
142  ! 3. Find the partial derivative of locally referenced potential density
143  ! and coordinate density with temperature, and the density of the layer
144  ! and the layer above.
145  ! 4. Heat a portion of the bottommost layer until it matches the target
146  ! density of the layer above, and move it.
147  ! 4a. In the case of variable density layers, heat but do not move.
148  ! 5. If there is still heat left over, repeat for the next layer up.
149  ! This subroutine updates thickness, T & S, and increments eb accordingly.
150 
151  ! 6. If there is not enough mass in the ocean, pass some of the heat up
152  ! from the ocean via the frazil field?
153 
154  num_start = 0
155  do i=is,ie
156  heat_rem(i) = g%mask2dT(i,j) * (cs%geo_heat(i,j) * (dt*irho_cp))
157  do_i(i) = .true. ; if (heat_rem(i) <= 0.0) do_i(i) = .false.
158  if (do_i(i)) num_start = num_start + 1
159  h_geo_rem(i) = cs%Geothermal_thick * gv%m_to_H
160  enddo
161  if (num_start == 0) cycle
162  num_left = num_start
163 
164  ! Find the first and last columns that need to be worked on.
165  isj = ie+1 ; do i=is,ie ; if (do_i(i)) then ; isj = i ; exit ; endif ; enddo
166  iej = is-1 ; do i=ie,is,-1 ; if (do_i(i)) then ; iej = i ; exit ; endif ; enddo
167 
168  if (nkmb > 0) then
169  call calculate_density(tv%T(:,j,nkmb), tv%S(:,j,nkmb), p_ref(:), &
170  rcv_bl(:), isj, iej-isj+1, tv%eqn_of_state)
171  else
172  rcv_bl(:) = -1.0
173  endif
174 
175  do k=nz,1,-1
176  do i=isj,iej ; if (do_i(i)) then
177 
178  if (h(i,j,k) > angstrom) then
179  if ((h(i,j,k)-angstrom) >= h_geo_rem(i)) then
180  h_heated = h_geo_rem(i)
181  heat_avail = heat_rem(i)
182  h_geo_rem(i) = 0.0
183  else
184  h_heated = (h(i,j,k)-angstrom)
185  heat_avail = heat_rem(i) * (h_heated / &
186  (h_geo_rem(i) + h_neglect))
187  h_geo_rem(i) = h_geo_rem(i) - h_heated
188  endif
189 
190  if (k<=nkmb .or. nkmb<=0) then
191  ! Simply heat the layer; convective adjustment occurs later
192  ! if necessary.
193  k_tgt = k
194  elseif ((k==nkmb+1) .or. (gv%Rlay(k-1) < rcv_bl(i))) then
195  ! Add enough heat to match the lowest buffer layer density.
196  k_tgt = nkmb
197  rcv_tgt = rcv_bl(i)
198  else
199  ! Add enough heat to match the target density of layer k-1.
200  k_tgt = k-1
201  rcv_tgt = gv%Rlay(k-1)
202  endif
203 
204  if (k<=nkmb .or. nkmb<=0) then
205  rcv = 0.0 ; drcv_dt = 0.0 ! Is this OK?
206  else
207  call calculate_density(tv%T(i,j,k), tv%S(i,j,k), tv%P_Ref, &
208  rcv, tv%eqn_of_state)
209  t2(1) = tv%T(i,j,k) ; s2(1) = tv%S(i,j,k)
210  t2(2) = tv%T(i,j,k_tgt) ; s2(2) = tv%S(i,j,k_tgt)
211  call calculate_density_derivs(t2(:), s2(:), p_ref(:), &
212  drcv_dt_, drcv_ds_, 1, 2, tv%eqn_of_state)
213  drcv_dt = 0.5*(drcv_dt_(1) + drcv_dt_(2))
214  endif
215 
216  if ((drcv_dt >= 0.0) .or. (k<=nkmb .or. nkmb<=0)) then
217  ! This applies to variable density layers.
218  heat_in_place = heat_avail
219  heat_trans = 0.0
220  elseif (drcv_dt <= cs%dRcv_dT_inplace) then
221  ! This is the option that usually applies in isopycnal coordinates.
222  heat_in_place = min(heat_avail, max(0.0, h(i,j,k) * &
223  ((gv%Rlay(k)-rcv) / drcv_dt)))
224  heat_trans = heat_avail - heat_in_place
225  else
226  ! wt_in_place should go from 0 to 1.
227  wt_in_place = (cs%dRcv_dT_inplace - drcv_dt) / cs%dRcv_dT_inplace
228  heat_in_place = max(wt_in_place*heat_avail, &
229  h(i,j,k) * ((gv%Rlay(k)-rcv) / drcv_dt) )
230  heat_trans = heat_avail - heat_in_place
231  endif
232 
233  if (heat_in_place > 0.0) then
234  ! This applies to variable density layers. In isopycnal coordinates
235  ! this only arises for relatively fresh water near the freezing
236  ! point, in which case heating in place will eventually cause things
237  ! to sort themselves out, if only because the water will warm to
238  ! the temperature of maximum density.
239  dtemp = heat_in_place / (h(i,j,k) + h_neglect)
240  tv%T(i,j,k) = tv%T(i,j,k) + dtemp
241  heat_rem(i) = heat_rem(i) - heat_in_place
242  rcv = rcv + drcv_dt * dtemp
243  endif
244 
245  if (heat_trans > 0.0) then
246  ! The second expression might never be used, but will avoid
247  ! division by 0.
248  drcv = max(rcv - rcv_tgt, 0.0)
249 
250  ! dTemp = -dRcv / dRcv_dT
251  ! h_transfer = min(heat_rem(i) / dTemp, h(i,j,k)-Angstrom)
252  if ((-drcv_dt * heat_trans) >= drcv * (h(i,j,k)-angstrom)) then
253  h_transfer = h(i,j,k) - angstrom
254  heating = (h_transfer * drcv) / (-drcv_dt)
255  ! Since not all the heat has been applied, return the fraction
256  ! of the layer thickness that has not yet been fully heated to
257  ! the h_geo_rem.
258  h_geo_rem(i) = h_geo_rem(i) + h_heated * &
259  ((heat_avail - (heating + heat_in_place)) / heat_avail)
260  else
261  h_transfer = (-drcv_dt * heat_trans) / drcv
262  heating = heat_trans
263  endif
264  heat_rem(i) = heat_rem(i) - heating
265 
266  i_h = 1.0 / (h(i,j,k_tgt) + h_transfer + h_neglect)
267  tv%T(i,j,k_tgt) = (h(i,j,k_tgt) * tv%T(i,j,k_tgt) + &
268  (h_transfer * tv%T(i,j,k) + heating)) * i_h
269  tv%S(i,j,k_tgt) = (h(i,j,k_tgt) * tv%S(i,j,k_tgt) + &
270  h_transfer * tv%S(i,j,k)) * i_h
271 
272  h(i,j,k) = h(i,j,k) - h_transfer
273  h(i,j,k_tgt) = h(i,j,k_tgt) + h_transfer
274  eb(i,j,k_tgt) = eb(i,j,k_tgt) + h_transfer
275  if (k_tgt < k-1) then
276  do k2 = k_tgt+1,k-1
277  eb(i,j,k2) = eb(i,j,k2) + h_transfer
278  enddo
279  endif
280  endif
281 
282  if (heat_rem(i) <= 0.0) then
283  do_i(i) = .false. ; num_left = num_left-1
284  ! For efficiency, uncomment these?
285  ! if ((i==isj) .and. (num_left > 0)) then
286  ! do i2=isj+1,iej ; if (do_i(i2)) then
287  ! isj = i2 ; exit ! Set the new starting value.
288  ! endif ; enddo
289  ! endif
290  ! if ((i==iej) .and. (num_left > 0)) then
291  ! do i2=iej-1,isj,-1 ; if (do_i(i2)) then
292  ! iej = i2 ; exit ! Set the new ending value.
293  ! endif ; enddo
294  ! endif
295  endif
296  endif
297  endif ; enddo
298  if (num_left <= 0) exit
299  enddo ! k-loop
300 
301  if (associated(tv%internal_heat)) then ; do i=is,ie
302  tv%internal_heat(i,j) = tv%internal_heat(i,j) + gv%H_to_kg_m2 * &
303  (g%mask2dT(i,j) * (cs%geo_heat(i,j) * (dt*irho_cp)) - heat_rem(i))
304  enddo ; endif
305  enddo ! j-loop
306 
307 ! do i=is,ie ; do j=js,je
308 ! resid(i,j) = tv%internal_heat(i,j) - resid(i,j) - GV%H_to_kg_m2 * &
309 ! (G%mask2dT(i,j) * (CS%geo_heat(i,j) * (dt*Irho_cp)))
310 ! enddo ; enddo
311 
312 end subroutine geothermal
313 
314 !> Initialize parameters and allocate memory associated with the geothermal heating module.
315 subroutine geothermal_init(Time, G, param_file, diag, CS)
316  type(time_type), target, intent(in) :: time !< Current model time.
317  type(ocean_grid_type), intent(inout) :: g !< The ocean's grid structure.
318  type(param_file_type), intent(in) :: param_file !< A structure to parse for run-time
319  !! parameters.
320  type(diag_ctrl), target, intent(inout) :: diag !< Structure used to regulate diagnostic output.
321  type(geothermal_cs), pointer :: cs !< Pointer pointing to the module control
322  !! structure.
323 ! This include declares and sets the variable "version".
324 #include "version_variable.h"
325  character(len=40) :: mdl = "MOM_geothermal" ! module name
326  ! Local variables
327  character(len=200) :: inputdir, geo_file, filename, geotherm_var
328  real :: scale
329  integer :: i, j, isd, ied, jsd, jed, id
330  isd = g%isd ; ied = g%ied ; jsd = g%jsd ; jed = g%jed
331 
332  if (associated(cs)) then
333  call mom_error(warning, "geothermal_init called with an associated"// &
334  "associated control structure.")
335  return
336  else ; allocate(cs) ; endif
337 
338  cs%diag => diag
339  cs%Time => time
340 
341  ! write parameters to the model log.
342  call log_version(param_file, mdl, version, "")
343  call get_param(param_file, mdl, "GEOTHERMAL_SCALE", scale, &
344  "The constant geothermal heat flux, a rescaling "//&
345  "factor for the heat flux read from GEOTHERMAL_FILE, or "//&
346  "0 to disable the geothermal heating.", &
347  units="W m-2 or various", default=0.0)
348  cs%apply_geothermal = .not.(scale == 0.0)
349  if (.not.cs%apply_geothermal) return
350 
351  call safe_alloc_ptr(cs%geo_heat, isd, ied, jsd, jed) ; cs%geo_heat(:,:) = 0.0
352 
353  call get_param(param_file, mdl, "GEOTHERMAL_FILE", geo_file, &
354  "The file from which the geothermal heating is to be "//&
355  "read, or blank to use a constant heating rate.", default=" ")
356  call get_param(param_file, mdl, "GEOTHERMAL_THICKNESS", cs%geothermal_thick, &
357  "The thickness over which to apply geothermal heating.", &
358  units="m", default=0.1)
359  call get_param(param_file, mdl, "GEOTHERMAL_DRHO_DT_INPLACE", cs%dRcv_dT_inplace, &
360  "The value of drho_dT above which geothermal heating "//&
361  "simply heats water in place instead of moving it between "//&
362  "isopycnal layers. This must be negative.", &
363  units="kg m-3 K-1", default=-0.01)
364  if (cs%dRcv_dT_inplace >= 0.0) call mom_error(fatal, "geothermal_init: "//&
365  "GEOTHERMAL_DRHO_DT_INPLACE must be negative.")
366 
367  if (len_trim(geo_file) >= 1) then
368  call get_param(param_file, mdl, "INPUTDIR", inputdir, default=".")
369  inputdir = slasher(inputdir)
370  filename = trim(inputdir)//trim(geo_file)
371  call log_param(param_file, mdl, "INPUTDIR/GEOTHERMAL_FILE", filename)
372  call get_param(param_file, mdl, "GEOTHERMAL_VARNAME", geotherm_var, &
373  "The name of the geothermal heating variable in "//&
374  "GEOTHERMAL_FILE.", default="geo_heat")
375  call mom_read_data(filename, trim(geotherm_var), cs%geo_heat, g%Domain)
376  do j=jsd,jed ; do i=isd,ied
377  cs%geo_heat(i,j) = (g%mask2dT(i,j) * scale) * cs%geo_heat(i,j)
378  enddo ; enddo
379  else
380  do j=jsd,jed ; do i=isd,ied
381  cs%geo_heat(i,j) = g%mask2dT(i,j) * scale
382  enddo ; enddo
383  endif
384  call pass_var(cs%geo_heat, g%domain)
385 
386  ! post the static geothermal heating field
387  id = register_static_field('ocean_model', 'geo_heat', diag%axesT1, &
388  'Geothermal heat flux into ocean', 'W m-2', &
389  cmor_field_name='hfgeou', cmor_units='W m-2', &
390  cmor_standard_name='upward_geothermal_heat_flux_at_sea_floor', &
391  cmor_long_name='Upward geothermal heat flux at sea floor', &
392  x_cell_method='mean', y_cell_method='mean', area_cell_method='mean')
393  if (id > 0) call post_data(id, cs%geo_heat, diag, .true.)
394 
395 end subroutine geothermal_init
396 
397 !> Clean up and deallocate memory associated with the geothermal heating module.
398 subroutine geothermal_end(CS)
399  type(geothermal_cs), pointer :: cs !< Geothermal heating control structure that
400  !! will be deallocated in this subroutine.
401 
402  if (associated(cs%geo_heat)) deallocate(cs%geo_heat)
403  if (associated(cs)) deallocate(cs)
404 end subroutine geothermal_end
405 
406 !> \namespace mom_geothermal
407 !!
408 !! Geothermal heating can be added either in a layered isopycnal mode, in which the heating raises the density
409 !! of the layer to the target density of the layer above, and then moves the water into that layer, or in a
410 !! simple Eulerian mode, in which the bottommost GEOTHERMAL_THICKNESS are heated. Geothermal heating will also
411 !! provide a buoyant source of bottom TKE that can be used to further mix the near-bottom water. In cold fresh
412 !! water lakes where heating increases density, water should be moved into deeper layers, but this is not
413 !! implemented yet.
414 
415 end module mom_geothermal
mom_verticalgrid
Provides a transparent vertical ocean grid type and supporting routines.
Definition: MOM_verticalGrid.F90:2
mom_file_parser::log_version
An overloaded interface to log version information about modules.
Definition: MOM_file_parser.F90:109
mom_diag_mediator
The subroutines here provide convenient wrappers to the fms diag_manager interfaces with additional d...
Definition: MOM_diag_mediator.F90:3
mom_variables::thermo_var_ptrs
Pointers to an assortment of thermodynamic fields that may be available, including potential temperat...
Definition: MOM_variables.F90:82
mom_eos
Provides subroutines for quantities specific to the equation of state.
Definition: MOM_EOS.F90:2
mom_domains::pass_var
Do a halo update on an array.
Definition: MOM_domains.F90:49
mom_file_parser::param_file_type
A structure that can be parsed to read and document run-time parameters.
Definition: MOM_file_parser.F90:54
mom_file_parser::get_param
An overloaded interface to read and log the values of various types of parameters.
Definition: MOM_file_parser.F90:102
mom_io
This module contains I/O framework code.
Definition: MOM_io.F90:2
mom_diag_mediator::post_data
Make a diagnostic available for averaging or output.
Definition: MOM_diag_mediator.F90:70
mom_geothermal
Implemented geothermal heating at the ocean bottom.
Definition: MOM_geothermal.F90:2
mom_verticalgrid::verticalgrid_type
Describes the vertical ocean grid, including unit conversion factors.
Definition: MOM_verticalGrid.F90:24
mom_domains
Describes the decomposed MOM domain and has routines for communications across PEs.
Definition: MOM_domains.F90:2
mom_variables
Provides transparent structures with groups of MOM6 variables and supporting routines.
Definition: MOM_variables.F90:2
mom_geothermal::geothermal_cs
Control structure for geothermal heating.
Definition: MOM_geothermal.F90:24
mom_io::mom_read_data
Read a data field from a file.
Definition: MOM_io.F90:74
mom_file_parser
The MOM6 facility to parse input files for runtime parameters.
Definition: MOM_file_parser.F90:2
mom_eos::calculate_density_derivs
Calculate the derivatives of density with temperature and salinity from T, S, and P.
Definition: MOM_EOS.F90:70
mom_grid
Provides the ocean grid type.
Definition: MOM_grid.F90:2
mom_file_parser::log_param
An overloaded interface to log the values of various types of parameters.
Definition: MOM_file_parser.F90:96
mom_error_handler
Routines for error handling and I/O management.
Definition: MOM_error_handler.F90:2
mom_grid::ocean_grid_type
Ocean grid type. See mom_grid for details.
Definition: MOM_grid.F90:25
mom_diag_mediator::diag_ctrl
The following data type a list of diagnostic fields an their variants, as well as variables that cont...
Definition: MOM_diag_mediator.F90:239
mom_eos::calculate_density
Calculates density of sea water from T, S and P.
Definition: MOM_EOS.F90:60