18 implicit none ;
private
20 #include <MOM_memory.h>
22 public geothermal_entraining, geothermal_in_place, geothermal_init, geothermal_end
26 real :: drcv_dt_inplace
29 real,
pointer :: geo_heat(:,:) => null()
30 real :: geothermal_thick
32 logical :: apply_geothermal
36 type(time_type),
pointer :: time => null()
39 integer :: id_internal_heat_heat_tendency = -1
40 integer :: id_internal_heat_temp_tendency = -1
41 integer :: id_internal_heat_h_tendency = -1
53 subroutine geothermal_entraining(h, tv, dt, ea, eb, G, GV, US, CS, halo)
56 real,
dimension(SZI_(G),SZJ_(G),SZK_(G)),
intent(inout) :: h
61 real,
intent(in) :: dt
62 real,
dimension(SZI_(G),SZJ_(G),SZK_(G)),
intent(inout) :: ea
66 real,
dimension(SZI_(G),SZJ_(G),SZK_(G)),
intent(inout) :: eb
74 integer,
optional,
intent(in) :: halo
76 real,
dimension(SZI_(G)) :: &
82 real,
dimension(2) :: &
87 real :: angstrom, h_neglect
109 real,
dimension(SZI_(G),SZJ_(G),SZK_(G)) :: t_old
112 real,
dimension(SZI_(G),SZJ_(G),SZK_(G)) :: h_old
115 real,
dimension(SZI_(G),SZJ_(G),SZK_(G)) :: work_3d
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
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
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
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
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.")
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
153 compute_t_old = cs%id_internal_heat_heat_tendency > 0 &
154 .or. cs%id_internal_heat_temp_tendency > 0
156 if (cs%id_internal_heat_heat_tendency > 0) work_3d(:,:,:) = 0.0
158 if (compute_h_old .or. compute_t_old)
then ;
do k=1,nz ;
do j=js,je ;
do i=is,ie
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
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
196 if (num_left == 0) cycle
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
204 tv%eqn_of_state, (/isj-(g%isd-1),iej-(g%isd-1)/) )
210 do i=isj,iej ;
if (do_i(i))
then
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)
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
224 if (k<=nkmb .or. nkmb<=0)
then
228 elseif ((k==nkmb+1) .or. (gv%Rlay(k-1) < rcv_bl(i)))
then
235 rcv_tgt = gv%Rlay(k-1)
238 if (k<=nkmb .or. nkmb<=0)
then
239 rcv = 0.0 ; drcv_dt = 0.0
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)
246 tv%eqn_of_state, (/1,2/) )
247 drcv_dt = 0.5*(drcv_dt_(1) + drcv_dt_(2))
250 if ((drcv_dt >= 0.0) .or. (k<=nkmb .or. nkmb<=0))
then
252 heat_in_place = heat_avail
254 elseif (drcv_dt <= cs%dRcv_dT_inplace)
then
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
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
267 if (heat_in_place > 0.0)
then
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
279 if (heat_trans > 0.0)
then
282 drcv = max(rcv - rcv_tgt, 0.0)
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)
292 h_geo_rem(i) = h_geo_rem(i) + h_heated * &
293 ((heat_avail - (heating + heat_in_place)) / heat_avail)
295 h_transfer = (-drcv_dt * heat_trans) / drcv
298 heat_rem(i) = heat_rem(i) - heating
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
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
311 eb(i,j,k2) = eb(i,j,k2) + h_transfer
316 if (heat_rem(i) <= 0.0)
then
317 do_i(i) = .false. ; num_left = num_left-1
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))
334 if (num_left <= 0)
exit
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))
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)
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))
351 call post_data(cs%id_internal_heat_temp_tendency, work_3d, cs%diag, alt_h=h_old)
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)
365 end subroutine geothermal_entraining
370 subroutine geothermal_in_place(h, tv, dt, G, GV, US, CS, halo)
373 real,
dimension(SZI_(G),SZJ_(G),SZK_(G)),
intent(in) :: h
378 real,
intent(in) :: dt
382 integer,
optional,
intent(in) :: halo
385 real,
dimension(SZI_(G)) :: &
389 real :: angstrom, h_neglect
395 real,
dimension(SZI_(G),SZJ_(G),SZK_(G)) :: &
400 logical :: calc_diags
401 integer :: i, j, k, is, ie, js, je, nz, i2, isj, iej
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
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
412 irho_cp = 1.0 / (gv%H_to_RZ * tv%C_p)
413 angstrom = gv%Angstrom_H
414 h_neglect = gv%H_subroundoff
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.")
425 calc_diags = (cs%id_internal_heat_heat_tendency > 0) .or. (cs%id_internal_heat_temp_tendency > 0)
427 if (calc_diags) dtdt_diag(:,:,:) = 0.0
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
442 if (.not.do_any) cycle
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
451 if ((heat_rem(i) > 0.0) .and. (h(i,j,k) > angstrom))
then
454 if ((h(i,j,k)-angstrom) >= h_geo_rem(i))
then
455 heat_here = heat_rem(i)
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
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
469 if (heat_rem(i) > 0.0) do_any= .true.
472 if (.not.do_any)
exit
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))
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)
485 if (cs%id_internal_heat_heat_tendency > 0)
then
486 do k=1,nz ;
do j=js,je ;
do i=is,ie
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)
499 end subroutine geothermal_in_place
502 subroutine geothermal_init(Time, G, GV, US, param_file, diag, CS, useALEalgorithm)
503 type(time_type),
target,
intent(in) :: time
509 type(
diag_ctrl),
target,
intent(inout) :: diag
512 logical,
optional,
intent(in) :: usealealgorithm
515 #include "version_variable.h"
516 character(len=40) :: mdl =
"MOM_geothermal"
517 character(len=48) :: thickness_units
519 character(len=200) :: inputdir, geo_file, filename, geotherm_var
522 integer :: i, j, isd, ied, jsd, jed, id
523 isd = g%isd ; ied = g%ied ; jsd = g%jsd ; jed = g%jed
525 if (
associated(cs))
then
526 call mom_error(warning,
"geothermal_init called with an associated"// &
527 "associated control structure.")
529 else ;
allocate(cs) ;
endif
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
544 call safe_alloc_ptr(cs%geo_heat, isd, ied, jsd, jed) ; cs%geo_heat(:,:) = 0.0
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.")
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)
573 do j=jsd,jed ;
do i=isd,ied
574 cs%geo_heat(i,j) = g%mask2dT(i,j) * geo_scale
577 call pass_var(cs%geo_heat, g%domain)
579 thickness_units = get_thickness_units(gv)
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.)
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
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.)
607 end subroutine geothermal_init
610 subroutine geothermal_end(CS)
614 if (
associated(cs%geo_heat))
deallocate(cs%geo_heat)
615 if (
associated(cs))
deallocate(cs)
616 end subroutine geothermal_end