← back to index

src/parameterizations/vertical/MOM_geothermal.F90

portedportable, not yet portedexecuted, not portableexecutable, not hit by this run

1! This file is part of MOM6, the Modular Ocean Model version 6.
2! See the LICENSE file for licensing information.
3! SPDX-License-Identifier: Apache-2.0
4
5!> Implemented geothermal heating at the ocean bottom.
6module MOM_geothermal
7
8use MOM_diag_mediator, only : post_data, register_diag_field, safe_alloc_alloc
9use MOM_diag_mediator, only : register_static_field, time_type, diag_ctrl
10use MOM_domains, only : pass_var
11use MOM_error_handler, only : MOM_error, FATAL, WARNING
12use MOM_file_parser, only : get_param, log_param, log_version, param_file_type
13use MOM_io, only : MOM_read_data, slasher
14use MOM_grid, only : ocean_grid_type
15use MOM_unit_scaling, only : unit_scale_type
16use MOM_variables, only : thermo_var_ptrs
17use MOM_verticalGrid, only : verticalGrid_type, get_thickness_units
18use MOM_EOS, only : calculate_density, calculate_density_derivs, EOS_domain
19use MOM_EOS, only : calculate_specific_vol_derivs
20
21implicit none ; private
22
23#include <MOM_memory.h>
24
25public geothermal_entraining, geothermal_in_place, geothermal_init, geothermal_end
26
27!> Control structure for geothermal heating
28type, public :: geothermal_CS ; private
29 logical :: initialized = .false. !< True if this control structure has been initialized.
30 real :: dRcv_dT_inplace !< The value of dRcv_dT above which (dRcv_dT is negative) the
31 !! water is heated in place instead of moving upward between
32 !! layers in non-ALE layered mode [R C-1 ~> kg m-3 degC-1]
33 real, allocatable, dimension(:,:) :: geo_heat !< The geothermal heat flux [Q R Z T-1 ~> W m-2]
34 real :: geothermal_thick !< The thickness over which geothermal heating is
35 !! applied [H ~> m or kg m-2]
36 logical :: apply_geothermal !< If true, geothermal heating will be applied. This is false if
37 !! GEOTHERMAL_SCALE is 0 and there is no heat to apply.
38
39 type(time_type), pointer :: Time => NULL() !< A pointer to the ocean model's clock
40 type(diag_ctrl), pointer :: diag => NULL() !< A structure that is used to regulate the timing
41 !! timing of diagnostic output
42 integer :: id_internal_heat_heat_tendency = -1 !< ID for diagnostic of heat tendency
43 integer :: id_internal_heat_temp_tendency = -1 !< ID for diagnostic of temperature tendency
44 integer :: id_internal_heat_h_tendency = -1 !< ID for diagnostic of thickness tendency
45 integer :: id_geothermal_buoyancy_flux = -1 !< ID for diagnostic of bottom buoyancy flux
46end type geothermal_CS
47
48contains
49
50!> Applies geothermal heating, including the movement of water
51!! between isopycnal layers to match the target densities. The heating is
52!! applied to the bottommost layers that occur within GEOTHERMAL_THICKNESS of the bottom. If
53!! the partial derivative of the coordinate density with temperature is positive
54!! or very small, the layers are simply heated in place. Any heat that can not
55!! be applied to the ocean is returned (WHERE)?
560subroutine geothermal_entraining(h, tv, dt, ea, eb, G, GV, US, CS, halo)
57 type(ocean_grid_type), intent(inout) :: G !< The ocean's grid structure.
58 type(verticalGrid_type), intent(in) :: GV !< The ocean's vertical grid structure.
59 real, dimension(SZI_(G),SZJ_(G),SZK_(GV)), intent(inout) :: h !< Layer thicknesses [H ~> m or kg m-2]
60 type(thermo_var_ptrs), intent(inout) :: tv !< A structure containing pointers
61 !! to any available thermodynamic fields.
62 real, intent(in) :: dt !< Time increment [T ~> s].
63 real, dimension(SZI_(G),SZJ_(G),SZK_(GV)), intent(inout) :: ea !< The amount of fluid moved
64 !! downward into a layer; this
65 !! should be increased due to mixed
66 !! layer detrainment [H ~> m or kg m-2]
67 real, dimension(SZI_(G),SZJ_(G),SZK_(GV)), intent(inout) :: eb !< The amount of fluid moved upward
68 !! into a layer; this should be
69 !! increased due to mixed layer
70 !! entrainment [H ~> m or kg m-2].
71 type(unit_scale_type), intent(in) :: US !< A dimensional unit scaling type
72 type(geothermal_CS), intent(in) :: CS !< The control structure returned by
73 !! a previous call to
74 !! geothermal_init.
75 integer, optional, intent(in) :: halo !< Halo width over which to work
76 ! Local variables
77 real, dimension(SZI_(G)) :: &
780 heat_rem, & ! remaining heat [H C ~> m degC or kg degC m-2]
790 h_geo_rem, & ! remaining thickness to apply geothermal heating [H ~> m or kg m-2]
800 Rcv_BL, & ! coordinate density in the deepest variable density layer [R ~> kg m-3]
810 p_ref ! coordinate densities reference pressure [R L2 T-2 ~> Pa]
82
83 real, dimension(2) :: &
84 T2, S2, & ! temp and saln in the present and target layers [C ~> degC] and [S ~> ppt]
85 dRcv_dT_, & ! partial derivative of coordinate density wrt temp [R C-1 ~> kg m-3 degC-1]
86 dRcv_dS_ ! partial derivative of coordinate density wrt saln [R S-1 ~> kg m-3 ppt-1]
87
88 real :: Angstrom, H_neglect ! small thicknesses [H ~> m or kg m-2]
89 real :: Rcv ! coordinate density of present layer [R ~> kg m-3]
90 real :: Rcv_tgt ! coordinate density of target layer [R ~> kg m-3]
91 real :: dRcv ! difference between Rcv and Rcv_tgt [R ~> kg m-3]
92 real :: dRcv_dT ! partial derivative of coordinate density wrt temp
93 ! in the present layer [R C-1 ~> kg m-3 degC-1]; usually negative
94 real :: h_heated ! thickness that is being heated [H ~> m or kg m-2]
95 real :: heat_avail ! heating available for the present layer [C H ~> degC m or degC kg m-2]
96 real :: heat_in_place ! heating to warm present layer w/o movement between layers
97 ! [C H ~> degC m or degC kg m-2]
98 real :: heat_trans ! heating available to move water from present layer to target
99 ! layer [C H ~> degC m or degC kg m-2]
100 real :: heating ! heating used to move water from present layer to target layer
101 ! [C H ~> degC m or degC kg m-2]
102 ! 0 <= heating <= heat_trans
103 real :: h_transfer ! thickness moved between layers [H ~> m or kg m-2]
104 real :: wt_in_place ! relative weighting that goes from 0 to 1 [nondim]
105 real :: I_h ! inverse thickness [H-1 ~> m-1 or m2 kg-1]
106 real :: dTemp ! temperature increase in a layer [C ~> degC]
107 real :: Irho_cp ! inverse of heat capacity per unit layer volume
108 ! [C H Q-1 R-1 Z-1 ~> degC m3 J-1 or degC kg J-1]
109
110 real, dimension(SZI_(G),SZJ_(G),SZK_(GV)) :: &
1110 T_old, & ! Temperature of each layer before any heat is added, for diagnostics [C ~> degC]
1120 h_old, & ! Thickness of each layer before any heat is added, for diagnostics [H ~> m or kg m-2]
1130 work_3d ! Scratch variable used to calculate changes due to geothermal [various]
114 real :: Idt ! inverse of the timestep [T-1 ~> s-1]
115
1160 logical :: do_i(SZI_(G))
117 logical :: compute_h_old, compute_T_old
118 integer :: i, j, k, is, ie, js, je, nz, k2
119 integer :: isj, iej, num_left, nkmb, k_tgt
120
1210 is = G%isc ; ie = G%iec ; js = G%jsc ; je = G%jec ; nz = GV%ke
1220 if (present(halo)) then
1230 is = G%isc-halo ; ie = G%iec+halo ; js = G%jsc-halo ; je = G%jec+halo
124 endif
125
1260 if (.not. CS%initialized) call MOM_error(FATAL, "MOM_geothermal: "//&
1270 "Module must be initialized before it is used.")
1280 if (.not.CS%apply_geothermal) return
129
1300 nkmb = GV%nk_rho_varies
1310 Irho_cp = 1.0 / (GV%H_to_RZ * tv%C_p)
1320 Angstrom = GV%Angstrom_H
1330 H_neglect = GV%H_subroundoff
1340 p_ref(:) = tv%P_Ref
1350 Idt = 1.0 / dt
136
1370 if (.not.associated(tv%T)) call MOM_error(FATAL, "MOM geothermal_entraining: "//&
1380 "Geothermal heating can only be applied if T & S are state variables.")
139
140! do j=js,je ; do i=is,ie
141! resid(i,j) = tv%internal_heat(i,j)
142! enddo ; enddo
143
144 ! Conditionals for tracking diagnostic depdendencies
145 compute_h_old = CS%id_internal_heat_h_tendency > 0 &
146 .or. CS%id_internal_heat_heat_tendency > 0 &
1470 .or. CS%id_internal_heat_temp_tendency > 0
148
149 compute_T_old = CS%id_internal_heat_heat_tendency > 0 &
1500 .or. CS%id_internal_heat_temp_tendency > 0
151
1520 if (CS%id_internal_heat_heat_tendency > 0) work_3d(:,:,:) = 0.0
153
1540 if (compute_h_old .or. compute_T_old) then ; do k=1,nz ; do j=js,je ; do i=is,ie
155 ! Save temperature and thickness before any changes are made (for diagnostics)
1560 h_old(i,j,k) = h(i,j,k)
1570 T_old(i,j,k) = tv%T(i,j,k)
158 enddo ; enddo ; enddo ; endif
159
160!$OMP parallel do default(none) shared(is,ie,js,je,G,GV,US,CS,dt,Irho_cp,nkmb,tv, &
161!$OMP p_Ref,h,Angstrom,nz,H_neglect,eb, &
162!$OMP h_old,T_old,work_3d,Idt) &
163!$OMP private(heat_rem,do_i,h_geo_rem,num_left, &
164!$OMP isj,iej,Rcv_BL,h_heated,heat_avail,k_tgt, &
165!$OMP Rcv_tgt,Rcv,dRcv_dT,T2,S2,dRcv_dT_, &
166!$OMP dRcv_dS_,heat_in_place,heat_trans, &
167!$OMP wt_in_place,dTemp,dRcv,h_transfer,heating, &
168!$OMP I_h)
169
1700 do j=js,je
171 ! 1. Only work on columns that are being heated.
172 ! 2. Find the deepest layer with any mass.
173 ! 3. Find the partial derivative of locally referenced potential density
174 ! and coordinate density with temperature, and the density of the layer
175 ! and the layer above.
176 ! 4. Heat a portion of the bottommost layer until it matches the target
177 ! density of the layer above, and move it.
178 ! 4a. In the case of variable density layers, heat but do not move.
179 ! 5. If there is still heat left over, repeat for the next layer up.
180 ! This subroutine updates thickness, T & S, and increments eb accordingly.
181
182 ! 6. If there is not enough mass in the ocean, pass some of the heat up
183 ! from the ocean via the frazil field?
184
1850 num_left = 0
1860 do i=is,ie
1870 heat_rem(i) = G%mask2dT(i,j) * (CS%geo_heat(i,j) * (dt*Irho_cp))
1880 do_i(i) = .true. ; if (heat_rem(i) <= 0.0) do_i(i) = .false.
1890 if (do_i(i)) num_left = num_left + 1
1900 h_geo_rem(i) = CS%Geothermal_thick
191 enddo
1920 if (num_left == 0) cycle
193
194 ! Find the first and last columns that need to be worked on.
1950 isj = ie+1 ; do i=is,ie ; if (do_i(i)) then ; isj = i ; exit ; endif ; enddo
1960 iej = is-1 ; do i=ie,is,-1 ; if (do_i(i)) then ; iej = i ; exit ; endif ; enddo
197
1980 if (nkmb > 0) then
199 call calculate_density(tv%T(:,j,nkmb), tv%S(:,j,nkmb), p_Ref(:), Rcv_BL(:), &
2000 tv%eqn_of_state, (/isj-(G%isd-1),iej-(G%isd-1)/) )
201 else
2020 Rcv_BL(:) = -1.0
203 endif
204
2050 do k=nz,1,-1
2060 do i=isj,iej ; if (do_i(i)) then
207
2080 if (h(i,j,k) > Angstrom) then
2090 if ((h(i,j,k)-Angstrom) >= h_geo_rem(i)) then
2100 h_heated = h_geo_rem(i)
2110 heat_avail = heat_rem(i)
2120 h_geo_rem(i) = 0.0
213 else
2140 h_heated = (h(i,j,k)-Angstrom)
215 heat_avail = heat_rem(i) * (h_heated / &
2160 (h_geo_rem(i) + H_neglect))
2170 h_geo_rem(i) = h_geo_rem(i) - h_heated
218 endif
219
2200 if (k<=nkmb .or. nkmb<=0) then
221 ! Simply heat the layer; convective adjustment occurs later
222 ! if necessary.
2230 k_tgt = k
2240 elseif ((k==nkmb+1) .or. (GV%Rlay(k-1) < Rcv_BL(i))) then
225 ! Add enough heat to match the lowest buffer layer density.
2260 k_tgt = nkmb
2270 Rcv_tgt = Rcv_BL(i)
228 else
229 ! Add enough heat to match the target density of layer k-1.
2300 k_tgt = k-1
2310 Rcv_tgt = GV%Rlay(k-1)
232 endif
233
2340 if (k<=nkmb .or. nkmb<=0) then
2350 Rcv = 0.0 ; dRcv_dT = 0.0 ! Is this OK?
236 else
237 call calculate_density(tv%T(i,j,k), tv%S(i,j,k), tv%P_Ref, &
2380 Rcv, tv%eqn_of_state)
2390 T2(1) = tv%T(i,j,k) ; S2(1) = tv%S(i,j,k)
2400 T2(2) = tv%T(i,j,k_tgt) ; S2(2) = tv%S(i,j,k_tgt)
241 call calculate_density_derivs(T2(:), S2(:), p_Ref(:), dRcv_dT_, dRcv_dS_, &
2420 tv%eqn_of_state, (/1,2/) )
2430 dRcv_dT = 0.5*(dRcv_dT_(1) + dRcv_dT_(2))
244 endif
245
2460 if ((dRcv_dT >= 0.0) .or. (k<=nkmb .or. nkmb<=0)) then
247 ! This applies to variable density layers.
2480 heat_in_place = heat_avail
2490 heat_trans = 0.0
2500 elseif (dRcv_dT <= CS%dRcv_dT_inplace) then
251 ! This is the option that usually applies in isopycnal coordinates.
252 heat_in_place = min(heat_avail, max(0.0, h(i,j,k) * &
2530 ((GV%Rlay(k)-Rcv) / dRcv_dT)))
2540 heat_trans = heat_avail - heat_in_place
255 else
256 ! wt_in_place should go from 0 to 1.
2570 wt_in_place = (CS%dRcv_dT_inplace - dRcv_dT) / CS%dRcv_dT_inplace
258 heat_in_place = max(wt_in_place*heat_avail, &
2590 h(i,j,k) * ((GV%Rlay(k)-Rcv) / dRcv_dT) )
2600 heat_trans = heat_avail - heat_in_place
261 endif
262
2630 if (heat_in_place > 0.0) then
264 ! This applies to variable density layers. In isopycnal coordinates
265 ! this only arises for relatively fresh water near the freezing
266 ! point, in which case heating in place will eventually cause things
267 ! to sort themselves out, if only because the water will warm to
268 ! the temperature of maximum density.
2690 dTemp = heat_in_place / (h(i,j,k) + H_neglect)
2700 tv%T(i,j,k) = tv%T(i,j,k) + dTemp
2710 heat_rem(i) = heat_rem(i) - heat_in_place
2720 Rcv = Rcv + dRcv_dT * dTemp
273 endif
274
2750 if (heat_trans > 0.0) then
276 ! The second expression might never be used, but will avoid
277 ! division by 0.
2780 dRcv = max(Rcv - Rcv_tgt, 0.0)
279
280 ! dTemp = -dRcv / dRcv_dT
281 ! h_transfer = min(heat_rem(i) / dTemp, h(i,j,k)-Angstrom)
2820 if ((-dRcv_dT * heat_trans) >= dRcv * (h(i,j,k)-Angstrom)) then
2830 h_transfer = h(i,j,k) - Angstrom
2840 heating = (h_transfer * dRcv) / (-dRcv_dT)
285 ! Since not all the heat has been applied, return the fraction
286 ! of the layer thickness that has not yet been fully heated to
287 ! the h_geo_rem.
288 h_geo_rem(i) = h_geo_rem(i) + h_heated * &
2890 ((heat_avail - (heating + heat_in_place)) / heat_avail)
290 else
2910 h_transfer = (-dRcv_dT * heat_trans) / dRcv
2920 heating = heat_trans
293 endif
2940 heat_rem(i) = heat_rem(i) - heating
295
2960 I_h = 1.0 / ((h(i,j,k_tgt) + H_neglect) + h_transfer)
297 tv%T(i,j,k_tgt) = ((h(i,j,k_tgt) + H_neglect) * tv%T(i,j,k_tgt) + &
2980 (h_transfer * tv%T(i,j,k) + heating)) * I_h
299 tv%S(i,j,k_tgt) = ((h(i,j,k_tgt) + H_neglect) * tv%S(i,j,k_tgt) + &
3000 h_transfer * tv%S(i,j,k)) * I_h
301
3020 h(i,j,k) = h(i,j,k) - h_transfer
3030 h(i,j,k_tgt) = h(i,j,k_tgt) + h_transfer
3040 eb(i,j,k_tgt) = eb(i,j,k_tgt) + h_transfer
3050 if (k_tgt < k-1) then
3060 do k2 = k_tgt+1,k-1
3070 eb(i,j,k2) = eb(i,j,k2) + h_transfer
308 enddo
309 endif
310 endif
311
3120 if (heat_rem(i) <= 0.0) then
3130 do_i(i) = .false. ; num_left = num_left-1
314 ! For efficiency, uncomment these?
315 ! if ((i==isj) .and. (num_left > 0)) then ; do i2=isj+1,iej ; if (do_i(i2)) then
316 ! isj = i2 ; exit ! Set the new starting value.
317 ! endif ; enddo ; endif
318 ! if ((i==iej) .and. (num_left > 0)) then ; do i2=iej-1,isj,-1 ; if (do_i(i2)) then
319 ! iej = i2 ; exit ! Set the new ending value.
320 ! endif ; enddo ; endif
321 endif
322 endif
323
324 ! Calculate heat tendency due to addition and transfer of internal heat
3250 if (CS%id_internal_heat_heat_tendency > 0) then
3260 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))
327 endif
328
329 endif ; enddo
3300 if (num_left <= 0) exit
331 enddo ! k-loop
332
3330 if (associated(tv%internal_heat)) then ; do i=is,ie
334 tv%internal_heat(i,j) = tv%internal_heat(i,j) + GV%H_to_RZ * &
3350 (G%mask2dT(i,j) * (CS%geo_heat(i,j) * (dt*Irho_cp)) - heat_rem(i))
336 enddo ; endif
337 enddo ! j-loop
338
339 ! Post diagnostic of 3D tendencies (heat, temperature, and thickness) due to internal heat
3400 if (CS%id_internal_heat_heat_tendency > 0) then
3410 call post_data(CS%id_internal_heat_heat_tendency, work_3d, CS%diag, alt_h=h_old)
342 endif
3430 if (CS%id_internal_heat_temp_tendency > 0) then
3440 do k=1,nz ; do j=js,je ; do i=is,ie
3450 work_3d(i,j,k) = Idt * (tv%T(i,j,k) - T_old(i,j,k))
346 enddo ; enddo ; enddo
3470 call post_data(CS%id_internal_heat_temp_tendency, work_3d, CS%diag, alt_h=h_old)
348 endif
3490 if (CS%id_internal_heat_h_tendency > 0) then
3500 do k=1,nz ; do j=js,je ; do i=is,ie
3510 work_3d(i,j,k) = Idt * (h(i,j,k) - h_old(i,j,k))
352 enddo ; enddo ; enddo
3530 call post_data(CS%id_internal_heat_h_tendency, work_3d, CS%diag, alt_h=h_old)
354 endif
355
356! do j=js,je ; do i=is,ie
357! resid(i,j) = tv%internal_heat(i,j) - resid(i,j) - GV%H_to_RZ * &
358! (G%mask2dT(i,j) * (CS%geo_heat(i,j) * (dt*Irho_cp)))
359! enddo ; enddo
360
361end subroutine geothermal_entraining
362
363!> Applies geothermal heating to the bottommost layers that occur within GEOTHERMAL_THICKNESS of
364!! the bottom, by simply heating the water in place. Any heat that can not be applied to the ocean
365!! is returned (WHERE)?
36612subroutine geothermal_in_place(h, tv, dt, G, GV, US, CS, BFlx_geothermal, halo)
367 type(ocean_grid_type), intent(inout) :: G !< The ocean's grid structure.
368 type(verticalGrid_type), intent(in) :: GV !< The ocean's vertical grid structure.
369 real, dimension(SZI_(G),SZJ_(G),SZK_(GV)), intent(in) :: h !< Layer thicknesses [H ~> m or kg m-2]
370 type(thermo_var_ptrs), intent(inout) :: tv !< A structure containing pointers
371 !! to any available thermodynamic fields.
372 real, intent(in) :: dt !< Time increment [T ~> s].
373 type(unit_scale_type), intent(in) :: US !< A dimensional unit scaling type
374 type(geothermal_CS), intent(in) :: CS !< Geothermal heating control struct
375 real, dimension(SZI_(G), SZJ_(G)), intent(out) :: BFlx_geothermal !< Geothermal buoyancy flux
376 !! in [Z2 T-3 ~> m2 s-3]
377 integer, optional, intent(in) :: halo !< Halo width over which to work
378
379
380 ! Local variables
381 real, dimension(SZI_(G)) :: &
38224 heat_rem, & ! remaining heat [H C ~> m degC or kg degC m-2]
38324 h_geo_rem, & ! remaining thickness to apply geothermal heating [H ~> m or kg m-2]
38412 bottom_pressure, & ! Hydrostatic pressure in bottom layer [R L2 T-2 ~> Pa]
38524 dRhodT, & ! Partial derivative of density with temperature [R C-1 ~> kg m-3 degC-1]
38624 dRhodS, & ! Partial derivative of density with salinity [R S-1 ~> kg m-3 ppt-1]
38724 dSpVdT, & ! Partial derivative of specific volume with temperature [R-1 C-1 ~> m3 kg-1 degC-1]
38824 dSpVdS ! Partial derivative of specific volume with salinity [R-1 S-1 ~> m3 kg-1 ppt-1]
389
390 real :: Angstrom, H_neglect ! small thicknesses [H ~> m or kg m-2]
391 real :: heat_here ! heating applied to the present layer [C H ~> degC m or degC kg m-2]
392 real :: dTemp ! temperature increase in a layer [C ~> degC]
393 real :: Irho_cp ! inverse of heat capacity per unit layer volume
394 ! [C H Q-1 R-1 Z-1 ~> degC m3 J-1 or degC kg J-1]
395
396 real, dimension(SZI_(G),SZJ_(G),SZK_(GV)) :: &
39712 dTdt_diag ! Diagnostic of temperature tendency [C T-1 ~> degC s-1] which might be
398 ! converted into a layer-integrated heat tendency [Q R Z T-1 ~> W m-2]
399 real :: Idt ! inverse of the timestep [T-1 ~> s-1]
400 real :: H_to_Pres ! A conversion factor from thicknesses to pressure [R L2 T-2 H-1 ~> Pa m-1 or Pa m2 kg-1]
401 real :: I_Cp ! 1.0 / C_p [C Q-1 ~> kg degC J-1]
402 real :: I_Rho0Squared ! 1.0 / rho_0^2 (Boussinesq only) [R-2 ~> m6 kg-2]
403 logical :: do_any ! True if there is more to be done on the current j-row.
404 logical :: calc_diags ! True if diagnostic tendencies are needed.
405 logical :: nonBous ! If true, do not make the Boussinesq approximation.
406 integer, dimension(2) :: EOSdom ! The i-computational domain for the equation of state
407 integer :: i, j, k, is, ie, js, je, nz, isj, iej
408
40912 is = G%isc ; ie = G%iec ; js = G%jsc ; je = G%jec ; nz = GV%ke
41012 if (present(halo)) then
41112 is = G%isc-halo ; ie = G%iec+halo ; js = G%jsc-halo ; je = G%jec+halo
412 endif
413
41412 if (.not. CS%initialized) call MOM_error(FATAL, "MOM_geothermal: "//&
4150 "Module must be initialized before it is used.")
41612 if (.not.CS%apply_geothermal) return
417
41812 nonBous = .not.(GV%Boussinesq .or. GV%semi_Boussinesq)
41912 Irho_cp = 1.0 / (GV%H_to_RZ * tv%C_p)
42012 Angstrom = GV%Angstrom_H
42112 H_neglect = GV%H_subroundoff
42212 Idt = 1.0 / dt
42312 H_to_pres = GV%H_to_RZ * GV%g_Earth
42412 I_Cp = 1. /tv%C_p
42512 if (.not.nonBous) I_Rho0squared = 1. / (GV%Rho0**2)
42612 EOSdom(:) = EOS_domain(G%HI)
427
42812 if (.not.associated(tv%T)) call MOM_error(FATAL, "MOM geothermal_in_place: "//&
4290 "Geothermal heating can only be applied if T & S are state variables.")
430
431! do j=js,je ; do i=is,ie
432! resid(i,j) = tv%internal_heat(i,j)
433! enddo ; enddo
434
435 ! Conditionals for tracking diagnostic depdendencies
43612 calc_diags = (CS%id_internal_heat_heat_tendency > 0) .or. (CS%id_internal_heat_temp_tendency > 0)
437105276 BFlx_geothermal(:,:) = 0.0
438
43912 if (calc_diags) dTdt_diag(:,:,:) = 0.0
440
441 !$OMP parallel do default(shared) private(heat_rem,do_any,h_geo_rem,isj,iej,heat_here,dTemp)
442756 do j=js,je
44395976 bottom_pressure(:) = 0.0
4446864144 do k=1,nz ; do i=is,ie
4456863400 bottom_pressure(i) = bottom_pressure(i) + H_to_pres * h(i,j,k)
446 enddo ; enddo
447744 if (nonBous) then
4480 dSpVdT(:) = 0.0
4490 dSpVdS(:) = 0.0
450 call calculate_specific_vol_derivs(tv%T(:,j,nz), tv%S(:,j,nz), bottom_pressure, dSpVdT, dSpVdS, &
4510 tv%eqn_of_state, EOSdom)
4520 do i=is,ie
4530 BFlx_geothermal(i,j) = ( (GV%g_Earth_Z_T2 * dSpVdT(i)) * (CS%geo_heat(i,j)*I_Cp) ) * G%mask2dT(i,j)
454 enddo
455 else
45695976 dRhodT(:) = 0.0
45795976 dRhodS(:) = 0.0
458 call calculate_density_derivs(tv%T(:,j,nz), tv%S(:,j,nz), bottom_pressure, dRhodT, dRhodS, &
459744 tv%eqn_of_state, EOSdom)
46091512 do i=is,ie
461 BFlx_geothermal(i,j) = - ( (GV%g_Earth_Z_T2*I_Rho0squared) * ((I_Cp*dRhodT(i))*CS%geo_heat(i,j)) ) &
46291512 * G%mask2dT(i,j)
463 enddo
464 endif
465
466
467
468 ! Only work on columns that are being heated, and heat the near-bottom water.
469
470 ! If there is not enough mass in the ocean, pass some of the heat up
471 ! from the ocean via the frazil field?
472
473744 do_any = .false.
47491512 do i=is,ie
47590768 heat_rem(i) = G%mask2dT(i,j) * (CS%geo_heat(i,j) * (dt*Irho_cp))
47690768 if (heat_rem(i) > 0.0) do_any = .true.
47791512 h_geo_rem(i) = CS%Geothermal_thick
478 enddo
479744 if (.not.do_any) cycle
480
481 ! Find the first and last columns that need to be worked on.
4827476 isj = ie+1 ; do i=is,ie ; if (heat_rem(i) > 0.0) then ; isj = i ; exit ; endif ; enddo
4837476 iej = is-1 ; do i=ie,is,-1 ; if (heat_rem(i) > 0.0) then ; iej = i ; exit ; endif ; enddo
484
48543164 do k=nz,1,-1
48643164 do_any = .false.
4874428588 do i=isj,iej
4884385424 if ((heat_rem(i) > 0.0) .and. (h(i,j,k) > Angstrom)) then
489 ! Apply some or all of the remaining heat to this layer.
490 ! Convective adjustment occurs outside of this module if necessary.
4911476973 if ((h(i,j,k)-Angstrom) >= h_geo_rem(i)) then
49260600 heat_here = heat_rem(i)
49360600 h_geo_rem(i) = 0.0
49460600 heat_rem(i) = 0.0
495 else
4961416373 heat_here = heat_rem(i) * ((h(i,j,k)-Angstrom) / (h_geo_rem(i) + H_neglect))
4971416373 h_geo_rem(i) = h_geo_rem(i) - (h(i,j,k)-Angstrom)
4981416373 heat_rem(i) = heat_rem(i) - heat_here
499 endif
500
5011476973 dTemp = heat_here / (h(i,j,k) + H_neglect)
5021476973 tv%T(i,j,k) = tv%T(i,j,k) + dTemp
5031476973 if (calc_diags) dTdt_diag(i,j,k) = dTemp * Idt
504 endif
505
5064428588 if (heat_rem(i) > 0.0) do_any= .true.
507 enddo
508
50943164 if (.not.do_any) exit
510 enddo ! k-loop
511
51288572 if (associated(tv%internal_heat)) then ; do i=is,ie
513 tv%internal_heat(i,j) = tv%internal_heat(i,j) + GV%H_to_RZ * &
51488560 (G%mask2dT(i,j) * (CS%geo_heat(i,j) * (dt*Irho_cp)) - heat_rem(i))
515 enddo ; endif
516 enddo ! j-loop
517
518 ! Post diagnostics of 3D tendencies of heat and temperature due to geothermal heat
51912 if (CS%id_internal_heat_temp_tendency > 0) then
5200 call post_data(CS%id_internal_heat_temp_tendency, dTdt_diag, CS%diag, alt_h=h)
521 endif
52212 if (CS%id_internal_heat_heat_tendency > 0) then
5230 do k=1,nz ; do j=js,je ; do i=is,ie
524 ! Dangerously reuse dTdt_diag for a related variable with different units, going from
525 ! units of [C T-1 ~> degC s-1] to units of [Q R Z T-1 ~> W m-2]
5260 dTdt_diag(i,j,k) = (GV%H_to_RZ*tv%C_p) * (h(i,j,k) * dTdt_diag(i,j,k))
527 enddo ; enddo ; enddo
5280 call post_data(CS%id_internal_heat_heat_tendency, dTdt_diag, CS%diag, alt_h=h)
529 endif
53012 if (CS%id_geothermal_buoyancy_flux > 0) then
5310 call post_data(CS%id_geothermal_buoyancy_flux, BFlx_geothermal, CS%diag)
532 endif
533! do j=js,je ; do i=is,ie
534! resid(i,j) = tv%internal_heat(i,j) - resid(i,j) - GV%H_to_RZ * &
535! (G%mask2dT(i,j) * (CS%geo_heat(i,j) * (dt*Irho_cp)))
536! enddo ; enddo
537
538end subroutine geothermal_in_place
539
540!> Initialize parameters and allocate memory associated with the geothermal heating module.
5411subroutine geothermal_init(Time, G, GV, US, param_file, diag, CS, useALEalgorithm)
542 type(time_type), target, intent(in) :: Time !< Current model time.
543 type(ocean_grid_type), intent(inout) :: G !< The ocean's grid structure.
544 type(verticalGrid_type), intent(in) :: GV !< The ocean's vertical grid structure.
545 type(unit_scale_type), intent(in) :: US !< A dimensional unit scaling type
546 type(param_file_type), intent(in) :: param_file !< A structure to parse for run-time
547 !! parameters.
548 type(diag_ctrl), target, intent(inout) :: diag !< Structure used to regulate diagnostic output.
549 type(geothermal_CS), intent(inout) :: CS !< Geothermal heating control struct
550 logical, optional, intent(in) :: useALEalgorithm !< logical for whether to use ALE remapping
551
552! This include declares and sets the variable "version".
553#include "version_variable.h"
554 character(len=40) :: mdl = "MOM_geothermal" ! module name
555 character(len=48) :: thickness_units
556 ! Local variables
557 character(len=200) :: inputdir, geo_file, filename, geotherm_var
558 real :: geo_scale ! A constant heat flux or dimensionally rescaled geothermal flux scaling factor
559 ! [Q R Z T-1 ~> W m-2] or [Q R Z m2 s J-1 T-1 ~> nondim]
560 integer :: i, j, isd, ied, jsd, jed, id
5611 isd = G%isd ; ied = G%ied ; jsd = G%jsd ; jed = G%jed
562
5631 CS%initialized = .true.
5641 CS%diag => diag
5651 CS%Time => Time
566
567 ! write parameters to the model log.
5681 call log_version(param_file, mdl, version, "")
569 call get_param(param_file, mdl, "GEOTHERMAL_SCALE", geo_scale, &
570 "The constant geothermal heat flux, a rescaling "//&
571 "factor for the heat flux read from GEOTHERMAL_FILE, or "//&
572 "0 to disable the geothermal heating.", &
5731 units="W m-2 or various", default=0.0, scale=US%W_m2_to_QRZ_T)
5741 CS%apply_geothermal = .not.(geo_scale == 0.0)
5751 if (.not.CS%apply_geothermal) return
576
5778773 call safe_alloc_alloc(CS%geo_heat, isd, ied, jsd, jed) ; CS%geo_heat(:,:) = 0.0
578
579 call get_param(param_file, mdl, "GEOTHERMAL_FILE", geo_file, &
580 "The file from which the geothermal heating is to be "//&
5811 "read, or blank to use a constant heating rate.", default=" ")
582 call get_param(param_file, mdl, "GEOTHERMAL_THICKNESS", CS%geothermal_thick, &
583 "The thickness over which to apply geothermal heating.", &
5841 units="m", default=0.1, scale=GV%m_to_H)
585 call get_param(param_file, mdl, "GEOTHERMAL_DRHO_DT_INPLACE", CS%dRcv_dT_inplace, &
586 "The value of drho_dT above which geothermal heating "//&
587 "simply heats water in place instead of moving it between "//&
588 "isopycnal layers. This must be negative.", &
589 units="kg m-3 K-1", scale=US%kg_m3_to_R*US%C_to_degC, default=-0.01, &
5901 do_not_log=((GV%nk_rho_varies<=0).or.(GV%nk_rho_varies>=GV%ke)) )
5911 if (CS%dRcv_dT_inplace >= 0.0) call MOM_error(FATAL, "geothermal_init: "//&
5920 "GEOTHERMAL_DRHO_DT_INPLACE must be negative.")
593
5941 if (len_trim(geo_file) >= 1) then
5950 call get_param(param_file, mdl, "INPUTDIR", inputdir, default=".")
5960 inputdir = slasher(inputdir)
5970 filename = trim(inputdir)//trim(geo_file)
5980 call log_param(param_file, mdl, "INPUTDIR/GEOTHERMAL_FILE", filename)
599 call get_param(param_file, mdl, "GEOTHERMAL_VARNAME", geotherm_var, &
600 "The name of the geothermal heating variable in GEOTHERMAL_FILE.", &
6010 default="geo_heat")
6020 call MOM_read_data(filename, trim(geotherm_var), CS%geo_heat, G%Domain)
6030 do j=jsd,jed ; do i=isd,ied
6040 CS%geo_heat(i,j) = (G%mask2dT(i,j) * geo_scale) * CS%geo_heat(i,j)
605 enddo ; enddo
606 else
6078773 do j=jsd,jed ; do i=isd,ied
6088772 CS%geo_heat(i,j) = G%mask2dT(i,j) * geo_scale
609 enddo ; enddo
610 endif
6111 call pass_var(CS%geo_heat, G%domain)
612
6131 thickness_units = get_thickness_units(GV)
614
615 ! post the static geothermal heating field
616 id = register_static_field('ocean_model', 'geo_heat', diag%axesT1, &
617 'Geothermal heat flux into ocean', 'W m-2', conversion=US%QRZ_T_to_W_m2, &
618 cmor_field_name='hfgeou', &
619 cmor_standard_name='upward_geothermal_heat_flux_at_sea_floor', &
620 cmor_long_name='Upward geothermal heat flux at sea floor', &
6211 x_cell_method='mean', y_cell_method='mean', area_cell_method='mean')
6221 if (id > 0) call post_data(id, CS%geo_heat, diag, .true.)
623
624 CS%id_geothermal_buoyancy_flux = register_diag_field('ocean_model', &
625 'geo_bflx', diag%axesT1, Time, 'Geothermal buoyancy flux into ocean', &
6261 'm2 s-3', conversion=US%Z_to_m**2*US%s_to_T**3)
627
628 ! Diagnostic for tendencies due to internal heat (in 3d)
629 CS%id_internal_heat_heat_tendency = register_diag_field('ocean_model', &
630 'internal_heat_heat_tendency', diag%axesTL, Time, &
631 'Heat tendency (in 3D) due to internal (geothermal) sources', &
6321 'W m-2', conversion=US%QRZ_T_to_W_m2, v_extensive=.true.)
633 CS%id_internal_heat_temp_tendency = register_diag_field('ocean_model', &
634 'internal_heat_temp_tendency', diag%axesTL, Time, &
635 'Temperature tendency (in 3D) due to internal (geothermal) sources', &
6361 'degC s-1', conversion=US%C_to_degC*US%s_to_T, v_extensive=.true.)
6371 if (.not.useALEalgorithm) then
638 ! Do not offer this diagnostic if heating will be in place.
639 CS%id_internal_heat_h_tendency = register_diag_field('ocean_model', &
640 'internal_heat_h_tendency', diag%axesTL, Time, &
641 'Thickness tendency (in 3D) due to internal (geothermal) sources', &
6420 trim(thickness_units)//' s-1', conversion=GV%H_to_MKS*US%s_to_T, v_extensive=.true.)
643 endif
644
645end subroutine geothermal_init
646
647!> Clean up and deallocate memory associated with the geothermal heating module.
6481subroutine geothermal_end(CS)
649 type(geothermal_CS), intent(inout) :: CS !< Geothermal heating control struct
6501 if (allocated(CS%geo_heat)) deallocate(CS%geo_heat)
6511end subroutine geothermal_end
652
653!> \namespace mom_geothermal
654!!
655!! Geothermal heating can be added either in a layered isopycnal mode, in which the heating raises the density
656!! of the layer to the target density of the layer above, and then moves the water into that layer, or in a
657!! simple Eulerian mode, in which the bottommost GEOTHERMAL_THICKNESS are heated. Geothermal heating will also
658!! provide a buoyant source of bottom TKE that can be used to further mix the near-bottom water. In cold fresh
659!! water lakes where heating increases density, water should be moved into deeper layers, but this is not
660!! implemented yet.
661
6620end module MOM_geothermal