67 use time_interp_external_mod
, only : init_external_field, time_interp_external
68 use time_interp_external_mod
, only : time_interp_external_init
69 implicit none ;
private 71 #include <MOM_memory.h> 80 integer :: opacity_scheme
87 real :: pen_sw_scale_2nd
89 real :: sw_1st_exp_ratio
94 real :: opacity_land_value
98 character(len=128) :: chl_file
100 logical :: chl_from_file
101 type(time_type),
pointer :: time
107 integer :: id_sw_pen = -1, id_sw_vis_pen = -1, id_chl = -1
108 integer,
pointer :: id_opacity(:) => null()
122 type(optics_type),
intent(inout) :: optics
123 type(
forcing),
intent(in) :: fluxes
140 integer :: i, j, k, n, is, ie, js, je, nz
141 real :: inv_sw_pen_scale
144 logical :: call_for_surface
145 real :: tmp(szi_(g),szj_(g),szk_(g))
146 real :: chl(szi_(g),szj_(g),szk_(g))
148 real :: Pen_SW_tot(szi_(g),szj_(g))
150 is = g%isc ; ie = g%iec ; js = g%jsc ; je = g%jec ; nz = g%ke
152 if (.not.
associated(cs))
call mom_error(fatal,
"set_opacity: "// &
153 "Module must be initialized via opacity_init before it is used.")
155 if (cs%var_pen_sw)
then 156 if (cs%chl_from_file)
then 163 if (optics%nbands <= 1)
then ; inv_nbands = 1.0
164 else ; inv_nbands = 1.0 /
real(optics%nbands) ; endif
167 inv_sw_pen_scale = 1.0 / max(cs%pen_sw_scale, 0.1*gv%Angstrom_z, &
168 gv%H_to_m*gv%H_subroundoff)
172 do k=1,nz ;
do j=js,je ;
do i=is,ie
173 optics%opacity_band(1,i,j,k) = inv_sw_pen_scale
174 optics%opacity_band(2,i,j,k) = 1.0 / max(cs%pen_sw_scale_2nd, &
175 0.1*gv%Angstrom_z,gv%H_to_m*gv%H_subroundoff)
176 enddo ;
enddo ;
enddo 177 if (.not.
associated(fluxes%sw) .or. (cs%pen_SW_scale <= 0.0))
then 179 do j=js,je ;
do i=is,ie ;
do n=1,optics%nbands
180 optics%sw_pen_band(n,i,j) = 0.0
181 enddo ;
enddo ;
enddo 184 do j=js,je ;
do i=is,ie ;
185 optics%sw_pen_band(1,i,j) = (cs%SW_1st_EXP_RATIO) * fluxes%sw(i,j)
186 optics%sw_pen_band(2,i,j) = (1.-cs%SW_1st_EXP_RATIO) * fluxes%sw(i,j)
190 do k=1,nz ;
do j=js,je ;
do i=is,ie ;
do n=1,optics%nbands
191 optics%opacity_band(n,i,j,k) = inv_sw_pen_scale
192 enddo ;
enddo ;
enddo ;
enddo 193 if (.not.
associated(fluxes%sw) .or. (cs%pen_SW_scale <= 0.0))
then 195 do j=js,je ;
do i=is,ie ;
do n=1,optics%nbands
196 optics%sw_pen_band(n,i,j) = 0.0
197 enddo ;
enddo ;
enddo 200 do j=js,je ;
do i=is,ie ;
do n=1,optics%nbands
201 optics%sw_pen_band(n,i,j) = cs%pen_SW_frac * inv_nbands * fluxes%sw(i,j)
202 enddo ;
enddo ;
enddo 208 if (cs%id_sw_pen > 0)
then 210 do j=js,je ;
do i=is,ie
211 pen_sw_tot(i,j) = 0.0
213 pen_sw_tot(i,j) = pen_sw_tot(i,j) + optics%sw_pen_band(n,i,j)
216 call post_data(cs%id_sw_pen, pen_sw_tot, cs%diag)
218 if (cs%id_sw_vis_pen > 0)
then 221 do j=js,je ;
do i=is,ie
222 pen_sw_tot(i,j) = 0.0
223 do n=1,min(optics%nbands,2)
224 pen_sw_tot(i,j) = pen_sw_tot(i,j) + optics%sw_pen_band(n,i,j)
229 do j=js,je ;
do i=is,ie
230 pen_sw_tot(i,j) = 0.0
232 pen_sw_tot(i,j) = pen_sw_tot(i,j) + optics%sw_pen_band(n,i,j)
236 call post_data(cs%id_sw_vis_pen, pen_sw_tot, cs%diag)
238 do n=1,optics%nbands ;
if (cs%id_opacity(n) > 0)
then 240 do k=1,nz ;
do j=js,je ;
do i=is,ie
241 tmp(i,j,k) = optics%opacity_band(n,i,j,k)
242 enddo ;
enddo ;
enddo 243 call post_data(cs%id_opacity(n), tmp, cs%diag)
251 type(optics_type),
intent(inout) :: optics
252 type(
forcing),
intent(in) :: fluxes
257 real,
dimension(SZI_(G),SZJ_(G),SZK_(G)), &
258 intent(in),
optional :: chl_in
267 real :: chl_data(szi_(g),szj_(g))
271 real :: Inv_nbands_nir
279 type(time_type) :: day
280 character(len=128) :: mesg
281 integer :: days, seconds
282 integer :: i, j, k, n, is, ie, js, je, nz, nbands
283 logical :: multiband_vis_input, multiband_nir_input
285 is = g%isc ; ie = g%iec ; js = g%jsc ; je = g%jec ; nz = g%ke
300 nbands = optics%nbands
302 if (nbands <= 1)
then ; inv_nbands = 1.0
303 else ; inv_nbands = 1.0 /
real(nbands) ; endif
305 if (nbands <= 2)
then ; inv_nbands_nir = 0.0
306 else ; inv_nbands_nir = 1.0 /
real(nbands - 2.0) ; endif
308 multiband_vis_input = (
associated(fluxes%sw_vis_dir) .and. &
309 associated(fluxes%sw_vis_dif))
310 multiband_nir_input = (
associated(fluxes%sw_nir_dir) .and. &
311 associated(fluxes%sw_nir_dif))
314 if(
present(chl_in))
then 315 do j=js,je ;
do i=is,ie ; chl_data(i,j) = chl_in(i,j,1) ;
enddo ;
enddo 316 do k=1,nz;
do j=js,je ;
do i=is,ie
317 if ((g%mask2dT(i,j) > 0.5) .and. (chl_in(i,j,k) < 0.0))
then 318 write(mesg,
'(" Negative chl_in of ",(1pe12.4)," found at i,j,k = ", & 319 & 3(1x,i3), " lon/lat = ",(1pe12.4)," E ", (1pe12.4), " N.")') &
320 chl_in(i,j,k), i, j, k, g%geoLonT(i,j), g%geoLatT(i,j)
321 call mom_error(fatal,
"MOM_opacity opacity_from_chl: "//trim(mesg))
327 call get_time(cs%Time,seconds,days)
328 call time_interp_external(cs%sbc_chl, cs%Time, chl_data)
329 do j=js,je ;
do i=is,ie
330 if ((g%mask2dT(i,j) > 0.5) .and. (chl_data(i,j) < 0.0))
then 331 write(mesg,
'(" Time_interp negative chl of ",(1pe12.4)," at i,j = ",& 332 & 2(i3), "lon/lat = ",(1pe12.4)," E ", (1pe12.4), " N.")') &
333 chl_data(i,j), i, j, g%geoLonT(i,j), g%geoLatT(i,j)
334 call mom_error(fatal,
"MOM_opacity opacity_from_chl: "//trim(mesg))
339 if (cs%id_chl > 0)
then 340 if(
present(chl_in))
then 341 call post_data(cs%id_chl, chl_in(:,:,1), cs%diag)
343 call post_data(cs%id_chl, chl_data, cs%diag)
347 select case (cs%opacity_scheme)
352 do j=js,je ;
do i=is,ie
353 sw_vis_tot = 0.0 ; sw_nir_tot = 0.0
354 if (g%mask2dT(i,j) > 0.5)
then 355 if (multiband_vis_input)
then 356 sw_vis_tot = fluxes%sw_vis_dir(i,j) + fluxes%sw_vis_dif(i,j)
358 sw_vis_tot = 0.42 * fluxes%sw(i,j)
360 if (multiband_nir_input)
then 361 sw_nir_tot = fluxes%sw_nir_dir(i,j) + fluxes%sw_nir_dif(i,j)
363 sw_nir_tot = fluxes%sw(i,j) - sw_vis_tot
368 optics%sw_pen_band(1,i,j) = cs%blue_frac*sw_vis_tot
371 optics%sw_pen_band(2,i,j) = (1.0-cs%blue_frac)*sw_vis_tot
374 optics%sw_pen_band(n,i,j) = inv_nbands_nir * sw_nir_tot
381 do j=js,je ;
do i=is,ie
383 if (g%mask2dT(i,j) > 0.5)
then ;
if (multiband_vis_input)
then 385 (fluxes%sw_vis_dir(i,j) + fluxes%sw_vis_dif(i,j))
392 optics%sw_pen_band(n,i,j) = inv_nbands*sw_pen_tot
396 call mom_error(fatal,
"opacity_from_chl: CS%opacity_scheme is not valid.")
402 if (
present(chl_in))
then 403 do j=js,je ;
do i=is,ie ; chl_data(i,j) = chl_in(i,j,k) ;
enddo ;
enddo 406 select case (cs%opacity_scheme)
408 do j=js,je ;
do i=is,ie
409 if (g%mask2dT(i,j) <= 0.5)
then 411 optics%opacity_band(n,i,j,k) = cs%opacity_land_value
415 optics%opacity_band(1,i,j,k) = 0.0232 + 0.074*chl_data(i,j)**0.674
417 optics%opacity_band(2,i,j,k) = 0.225 + 0.037*chl_data(i,j)**0.629
419 do n=3,nbands ; optics%opacity_band(n,i,j,k) = 2.86 ;
enddo 423 do j=js,je ;
do i=is,ie
424 optics%opacity_band(1,i,j,k) = cs%opacity_land_value
425 if (g%mask2dT(i,j) > 0.5) &
429 optics%opacity_band(n,i,j,k) = optics%opacity_band(1,i,j,k)
434 call mom_error(fatal,
"opacity_from_chl: CS%opacity_scheme is not valid.")
442 real,
intent(in) :: chl_data
443 real :: opacity_morel
450 real,
dimension(6),
parameter :: &
451 Z2_coef=(/7.925, -6.644, 3.662, -1.815, -0.218, 0.502/)
454 chl = log10(min(max(chl_data,0.02),60.0)) ; chl2 = chl*chl
455 opacity_morel = 1.0 / ( (z2_coef(1) + z2_coef(2)*chl) + chl2 * &
456 ((z2_coef(3) + chl*z2_coef(4)) + chl2*(z2_coef(5) + chl*z2_coef(6))) )
460 real,
intent(in) :: chl_data
461 real :: SW_pen_frac_morel
469 real,
dimension(6),
parameter :: &
470 V1_coef=(/0.321, 0.008, 0.132, 0.038, -0.017, -0.007/)
472 chl = log10(min(max(chl_data,0.02),60.0)) ; chl2 = chl*chl
473 sw_pen_frac_morel = 1.0 - ( (v1_coef(1) + v1_coef(2)*chl) + chl2 * &
474 ((v1_coef(3) + chl*v1_coef(4)) + chl2*(v1_coef(5) + chl*v1_coef(6))) )
478 real,
intent(in) :: chl_data
479 real :: opacity_manizza
484 opacity_manizza = 0.0232 + 0.074*chl_data**0.674
487 subroutine opacity_init(Time, G, param_file, diag, tracer_flow, CS, optics)
488 type(time_type),
target,
intent(in) :: Time
489 type(ocean_grid_type),
intent(in) :: G
490 type(param_file_type),
intent(in) :: param_file
492 type(diag_ctrl),
target,
intent(inout) :: diag
494 type(tracer_flow_control_cs), &
495 target,
intent(in) :: tracer_flow
498 type(optics_type),
pointer :: optics
508 #include "version_variable.h" 509 character(len=200) :: inputdir
510 character(len=240) :: filename
511 character(len=200) :: tmpstr
512 character(len=40) :: mdl =
"MOM_opacity" 513 character(len=40) :: bandnum, shortname
514 character(len=200) :: longname
515 character(len=40) :: scheme_string
516 logical :: use_scheme
517 integer :: isd, ied, jsd, jed, nz, n
518 isd = g%isd ; ied = g%ied ; jsd = g%jsd ; jed = g%jed ; nz = g%ke
520 if (
associated(cs))
then 521 call mom_error(warning,
"opacity_init called with an associated"// &
522 "associated control structure.")
524 else ;
allocate(cs) ;
endif 528 cs%tracer_flow_CSp => tracer_flow
531 call log_version(param_file, mdl, version,
"")
534 call get_param(param_file, mdl,
"VAR_PEN_SW", cs%var_pen_sw, &
535 "If true, use one of the CHL_A schemes specified by \n"//&
536 "OPACITY_SCHEME to determine the e-folding depth of \n"//&
537 "incoming short wave radiation.", default=.false.)
539 cs%opacity_scheme =
no_scheme ; scheme_string =
"" 540 if (cs%var_pen_sw)
then 541 call get_param(param_file, mdl,
"OPACITY_SCHEME", tmpstr, &
542 "This character string specifies how chlorophyll \n"//&
543 "concentrations are translated into opacities. Currently \n"//&
544 "valid options include:\n"//&
545 " \t\t MANIZZA_05 - Use Manizza et al., GRL, 2005. \n"//&
546 " \t\t MOREL_88 - Use Morel, JGR, 1988.", &
548 if (len_trim(tmpstr) > 0)
then 549 tmpstr = uppercase(tmpstr)
556 call mom_error(fatal,
"opacity_init: #DEFINE OPACITY_SCHEME "//&
557 trim(tmpstr) //
"in input file is invalid.")
559 call mom_mesg(
'opacity_init: opacity scheme set to "'//trim(tmpstr)//
'".', 5)
562 call mom_error(warning,
"opacity_init: No scheme has successfully "//&
563 "been specified for the opacity. Using the default MANIZZA_05.")
567 call get_param(param_file, mdl,
"CHL_FROM_FILE", cs%chl_from_file, &
568 "If true, chl_a is read from a file.", default=.true.)
569 if (cs%chl_from_file)
then 570 call time_interp_external_init()
572 call get_param(param_file, mdl,
"INPUTDIR", inputdir, default=
".")
573 call get_param(param_file, mdl,
"CHL_FILE", cs%chl_file, &
574 "CHL_FILE is the file containing chl_a concentrations in \n"//&
575 "the variable CHL_A. It is used when VAR_PEN_SW and \n"//&
576 "CHL_FROM_FILE are true.", fail_if_missing=.true.)
578 filename = trim(slasher(inputdir))//trim(cs%chl_file)
579 call log_param(param_file, mdl,
"INPUTDIR/CHL_FILE", filename)
580 cs%sbc_chl = init_external_field(filename,
'CHL_A',domain=g%Domain%mpp_domain)
583 call get_param(param_file, mdl,
"BLUE_FRAC_SW", cs%blue_frac, &
584 "The fraction of the penetrating shortwave radiation \n"//&
585 "that is in the blue band.", default=0.5, units=
"nondim")
587 call get_param(param_file, mdl,
"EXP_OPACITY_SCHEME", tmpstr, &
588 "This character string specifies which exponential \n"//&
589 "opacity scheme to utilize. Currently \n"//&
590 "valid options include:\n"//&
591 " \t\t SINGLE_EXP - Single Exponent decay. \n"//&
592 " \t\t DOUBLE_EXP - Double Exponent decay.", &
594 if (len_trim(tmpstr) > 0)
then 595 tmpstr = uppercase(tmpstr)
602 call mom_mesg(
'opacity_init: opacity scheme set to "'//trim(tmpstr)//
'".', 5)
604 call get_param(param_file, mdl,
"PEN_SW_SCALE", cs%pen_sw_scale, &
605 "The vertical absorption e-folding depth of the \n"//&
606 "penetrating shortwave radiation.", units=
"m", default=0.0)
609 call get_param(param_file, mdl,
"PEN_SW_SCALE_2ND", cs%pen_sw_scale_2nd, &
610 "The (2nd) vertical absorption e-folding depth of the \n"//&
611 "penetrating shortwave radiation \n"//&
612 "(use if SW_EXP_MODE==double.)",&
613 units=
"m", default=0.0)
614 call get_param(param_file, mdl,
"SW_1ST_EXP_RATIO", cs%sw_1st_exp_ratio, &
615 "The fraction of 1st vertical absorption e-folding depth \n"//&
616 "penetrating shortwave radiation if SW_EXP_MODE==double.",&
617 units=
"m", default=0.0)
620 cs%pen_sw_scale_2nd = 0.0
621 cs%sw_1st_exp_ratio = 1.0
623 call get_param(param_file, mdl,
"PEN_SW_FRAC", cs%pen_sw_frac, &
624 "The fraction of the shortwave radiation that penetrates \n"//&
625 "below the surface.", units=
"nondim", default=0.0)
628 call get_param(param_file, mdl,
"PEN_SW_NBANDS", optics%nbands, &
629 "The number of bands of penetrating shortwave radiation.", &
632 if (optics%nbands.ne.2)
then 633 call mom_error(fatal,
"set_opacity: "// &
634 "Cannot use a double_exp opacity scheme with nbands!=2.")
636 elseif (cs%Opacity_scheme ==
single_exp )
then 637 if (optics%nbands.ne.1)
then 638 call mom_error(fatal,
"set_opacity: "// &
639 "Cannot use a single_exp opacity scheme with nbands!=1.")
642 if (.not.
ASSOCIATED(optics%min_wavelength_band)) &
643 allocate(optics%min_wavelength_band(optics%nbands))
644 if (.not.
ASSOCIATED(optics%max_wavelength_band)) &
645 allocate(optics%max_wavelength_band(optics%nbands))
648 optics%min_wavelength_band(1) =0
649 optics%max_wavelength_band(1) =550
650 if (optics%nbands >= 2)
then 651 optics%min_wavelength_band(2)=550
652 optics%max_wavelength_band(2)=700
654 if (optics%nbands > 2)
then 656 optics%min_wavelength_band(n) =700
657 optics%max_wavelength_band(n) =2800
662 call get_param(param_file, mdl,
"OPACITY_LAND_VALUE", cs%opacity_land_value, &
663 "The value to use for opacity over land. The default is \n"//&
664 "10 m-1 - a value for muddy water.", units=
"m-1", default=10.0)
666 if (.not.
ASSOCIATED(optics%opacity_band)) &
667 allocate(optics%opacity_band(optics%nbands,isd:ied,jsd:jed,nz))
668 if (.not.
ASSOCIATED(optics%sw_pen_band)) &
669 allocate(optics%sw_pen_band(optics%nbands,isd:ied,jsd:jed))
670 allocate(cs%id_opacity(optics%nbands)) ; cs%id_opacity(:) = -1
672 cs%id_sw_pen = register_diag_field(
'ocean_model',
'SW_pen', diag%axesT1, time, &
673 'Penetrating shortwave radiation flux into ocean',
'Watt meter-2')
674 cs%id_sw_vis_pen = register_diag_field(
'ocean_model',
'SW_vis_pen', diag%axesT1, time, &
675 'Visible penetrating shortwave radiation flux into ocean',
'Watt meter-2')
677 write(bandnum,
'(i3)') n
678 shortname =
'opac_'//trim(adjustl(bandnum))
679 longname =
'Opacity for shortwave radiation in band '//trim(adjustl(bandnum))
680 cs%id_opacity(n) = register_diag_field(
'ocean_model', shortname, diag%axesTL, time, &
684 cs%id_chl = register_diag_field(
'ocean_model',
'Chl_opac', diag%axesT1, time, &
685 'Surface chlorophyll A concentration used to find opacity',
'mg meter-3')
693 type(optics_type),
pointer,
optional :: optics
695 if (
associated(cs%id_opacity))
deallocate(cs%id_opacity)
696 if (
associated(cs))
deallocate(cs)
698 if (
present(optics))
then ;
if (
associated(optics))
then 699 if (
ASSOCIATED(optics%opacity_band))
deallocate(optics%opacity_band)
700 if (
ASSOCIATED(optics%sw_pen_band))
deallocate(optics%sw_pen_band)
character *(10), parameter single_exp_string
subroutine, public opacity_init(Time, G, param_file, diag, tracer_flow, CS, optics)
integer, parameter no_scheme
integer, parameter double_exp
subroutine, public opacity_end(CS, optics)
This module implements boundary forcing for MOM6.
subroutine, public set_opacity(optics, fluxes, G, GV, CS)
Ocean grid type. See mom_grid for details.
integer, parameter morel_88
Provides the ocean grid type.
This module contains I/O framework code.
subroutine opacity_from_chl(optics, fluxes, G, CS, chl_in)
integer, parameter single_exp
real function, public opacity_morel(chl_data)
character(len=len(input_string)) function, public uppercase(input_string)
integer, parameter manizza_05
Structure that contains pointers to the boundary forcing used to drive the liquid ocean simulated by ...
subroutine, public mom_mesg(message, verb, all_print)
character *(10), parameter double_exp_string
character *(10), parameter manizza_05_string
character *(10), parameter morel_88_string
The thermo_var_ptrs structure contains pointers to an assortment of thermodynamic fields that may be ...
subroutine, public get_chl_from_model(Chl_array, G, CS)
real function sw_pen_frac_morel(chl_data)
subroutine, public mom_error(level, message, all_print)
real function, public opacity_manizza(chl_data)