diff --git a/config/namelist.config b/config/namelist.config index 16402a01a..b9376ed5a 100644 --- a/config/namelist.config +++ b/config/namelist.config @@ -59,6 +59,7 @@ use_cavity_partial_cell=.false. use_floatice = .false. use_sw_pene=.true. flag_debug=.false. +use_transit=.false. / &machine diff --git a/config/namelist.transit b/config/namelist.transit new file mode 100644 index 000000000..3ca5c87cf --- /dev/null +++ b/config/namelist.transit @@ -0,0 +1,17 @@ +! The namelist file for transient tracers + +&transit_param +anthro_transit=.false. +paleo_transit=.false. +length_transit=1 ! 166 for anthro_transit=.true. +ti_start_transit=1 ! 1 for D14C, 80 for CFC-12 +ifile_transit='/work/ollie/mbutzin/fesom2/input/trace_gases/Table_CO2_isoC_CFC12_SF6.txt' +r14c_a = 1.0000 ! atm. 14C/C ratio, global mean +r39ar_a = 1.0000 ! atm. 39Ar/Ar ratio, global mean +xarg_a = 9.34e-3 ! atm. Argon concn. (mole fraction), global mean +xco2_a = 284.32e-6 ! atm. CO2 concn. (mole fraction), global mean +dic_0 = 2.00 ! mixed layer DIC concn. (mol / m**3), global mean +arg_0 = 0.01 ! mixed layer Argon concn. (mol / m**3), global mean +decay14 = 3.8561e-12 ! decay constant of 14C (1 / s), 1 a = 365.0 d +decay39 = 8.1708e-11 ! decay constant of 39Ar (1 / s), 1 a = 365.0 d +/ diff --git a/src/gen_model_setup.F90 b/src/gen_model_setup.F90 index 133aad01b..faac60fe7 100755 --- a/src/gen_model_setup.F90 +++ b/src/gen_model_setup.F90 @@ -9,7 +9,8 @@ subroutine setup_model(partit) use diagnostics, only: ldiag_solver,lcurt_stress_surf,lcurt_stress_surf, ldiag_Ri, ldiag_TurbFlux, ldiag_trflx, & ldiag_dMOC, ldiag_DVD, diag_list use g_clock, only: timenew, daynew, yearnew - use g_ic3d + use g_ic3d + use mod_transit implicit none type(t_partit), intent(inout), target :: partit character(len=MAX_PATH) :: nmlfile @@ -80,6 +81,21 @@ subroutine setup_model(partit) read (fileunit, NML=diag_list) close (fileunit) + if (use_transit) then +! Transient tracer input, input file names have to be specified in +! namelist.config, nml=run_config + if(partit%mype==0) print *, "Transient tracers are ON. Tracer input file: ", ifile_transit + open (20,file=ifile_transit) + if (anthro_transit .or. paleo_transit) then + call read_transit_input + else +! Spinup / equilibrium runs with constant tracer input, +! read parameter values from namelist.oce + read (20,nml=transit_param) + end if + close (20) + end if + if(partit%mype==0) write(*,*) 'Namelist files are read in' !_____________________________________________________________________________ diff --git a/src/gen_modules_clock.F90 b/src/gen_modules_clock.F90 index cf4173f2b..e3abda4d3 100755 --- a/src/gen_modules_clock.F90 +++ b/src/gen_modules_clock.F90 @@ -71,6 +71,7 @@ subroutine clock_init(partit) USE MOD_PARTIT USE MOD_PARSUP use g_config + use mod_transit, only: ti_transit, ti_start_transit implicit none type(t_partit), intent(in), target :: partit integer :: i, daystart, yearstart @@ -95,6 +96,9 @@ subroutine clock_init(partit) else r_restart=.true. end if +! For simulations with transient tracer input data + if (use_transit) ti_transit = yearnew - yearstart + ti_start_transit + ! year as character string write(cyearold,'(i4)') yearold diff --git a/src/gen_modules_config.F90 b/src/gen_modules_config.F90 index a23f0c1c5..bd21a57d1 100755 --- a/src/gen_modules_config.F90 +++ b/src/gen_modules_config.F90 @@ -145,9 +145,11 @@ module g_config character(100) :: which_toy="soufflet" logical :: flag_debug=.false. ! prints name of actual subroutine he is in logical :: flag_warn_cflz=.true. ! switches off cflz warning + logical :: use_transit=.false. ! switches off transient tracers namelist /run_config/ use_ice,use_floatice, use_sw_pene, use_cavity, & use_cavity_partial_cell, cavity_partial_cell_thresh, & - use_cavity_fw2press, toy_ocean, which_toy, flag_debug, flag_warn_cflz, lwiso !---wiso-code: add lwiso + use_cavity_fw2press, toy_ocean, which_toy, flag_debug, flag_warn_cflz, lwiso, & + use_transit !_____________________________________________________________________________ ! *** others *** diff --git a/src/io_meandata.F90 b/src/io_meandata.F90 index 57fcda543..31dec2c66 100644 --- a/src/io_meandata.F90 +++ b/src/io_meandata.F90 @@ -130,6 +130,7 @@ subroutine ini_mean_io(ice, dynamics, tracers, partit, mesh) use g_config, only: use_cavity use g_forcing_param, only: use_virt_salt, use_landice_water, use_age_tracer !---fwf-code, age-code use g_config, only : lwiso !---wiso-code + use mod_transit, only : index_transit implicit none integer :: i, j integer, save :: nm_io_unit = 103 ! unit to open namelist file, skip 100-102 for cray @@ -469,11 +470,16 @@ subroutine ini_mean_io(ice, dynamics, tracers, partit, mesh) end if !---wiso-code-end -CASE ('otracers ') - do j=3, tracers%num_tracers - write (id_string, "(I3.3)") tracers%data(j)%ID - call def_stream((/nl-1, nod2D/), (/nl-1, myDim_nod2D/), 'tra_'//id_string, 'pasive tracer ID='//id_string, 'n/a', tracers%data(j)%values(:,:), io_list(i)%freq, io_list(i)%unit, io_list(i)%precision, partit, mesh) - end do +! Transient tracers +CASE('SF6 ') + if (use_transit) call def_stream((/nl-1, nod2D/), (/nl-1, myDim_nod2D/), 'sf6', 'sulfur hexafluoride', 'mol / m**3', tracers%data(index_transit(1))%values(:,:), io_list(i)%freq, io_list(i)%unit, io_list(i)%precision, partit, mesh) +CASE('CFC-12 ') + if (use_transit) call def_stream((/nl-1, nod2D/), (/nl-1, myDim_nod2D/), 'cfc12', 'chlorofluorocarbon CFC-12', 'mol / m**3', tracers%data(index_transit(2))%values(:,:), io_list(i)%freq, io_list(i)%unit, io_list(i)%precision, partit, mesh) +CASE('R14C ') + if (use_transit) call def_stream((/nl-1, nod2D/), (/nl-1, myDim_nod2D/), 'r14c', '14C / C ratio of DIC', 'none', tracers%data(index_transit(3))%values(:,:), io_list(i)%freq, io_list(i)%unit, io_list(i)%precision, partit, mesh) +CASE('R39Ar ') + if (use_transit) call def_stream((/nl-1, nod2D/), (/nl-1, myDim_nod2D/), 'r39ar', '39Ar / Ar ratio', 'none', tracers%data(index_transit(4))%values(:,:), io_list(i)%freq, io_list(i)%unit, io_list(i)%precision, partit, mesh) +! Transient tracers end CASE ('slope_x ') call def_stream((/nl-1, nod2D/), (/nl-1, myDim_nod2D/), 'slope_x', 'neutral slope X', 'none', slope_tapered(1,:,:), io_list(i)%freq, io_list(i)%unit, io_list(i)%precision, partit, mesh) CASE ('slope_y ') diff --git a/src/io_restart.F90 b/src/io_restart.F90 index 6ba528f03..18451d89a 100644 --- a/src/io_restart.F90 +++ b/src/io_restart.F90 @@ -107,7 +107,24 @@ subroutine ini_ocean_io(year, dynamics, tracers, partit, mesh) trname='salt' longname='salinity' units='psu' + CASE(6) + trname='sf6' + longname='sulfur hexafluoride' + units='mol / m**3' + CASE(12) + trname='cfc12' + longname='chlorofluorocarbon CFC-12' + units='mol / m**3' + CASE(14) + trname='r14c' + longname='14C / C ratio of DIC' + units='none' + CASE(39) + trname='r39ar' + longname='39Ar / Ar ratio' + units='none' CASE DEFAULT +! other passive tracers write(trname,'(A3,i1)') 'tra_', j write(longname,'(A15,i1)') 'passive tracer ', j units='none' diff --git a/src/mod_transit.F90 b/src/mod_transit.F90 new file mode 100644 index 000000000..6713032eb --- /dev/null +++ b/src/mod_transit.F90 @@ -0,0 +1,315 @@ +!========================================================== +MODULE mod_transit +! Parameters, variables and functions for transient tracer simulations. +! By mbutzin, 2019-2021 + + implicit none + save + +! Atmospheric pressure, local (dummy) variable, global-mean SLP, and local wind speed at 10 m + real(kind=8) :: press_a, mean_slp = 1.01325e5, wind_2 + +! Atmospheric trace gas (dummy) values used in air-sea flux calculations +! Isotopic ratios are normalized and fractionation-corrected, +! volume mixing ratios are mole fractions in dry air. + real(kind=8) :: r14c_a = 1.0, & ! 14CO2 / 12CO2 (may vary with latitude in transient runs) + r39ar_a = 1.0, & ! 39Ar / 40 Ar (homogeneous) + xarg_a = 9.34e-3, & ! Argon (homogeneous) + xCO2_a = 284.32e-6, & ! CO2 (CMIP6 & OMIP-BGC: 284.32e-6 for 1700-1850, PMIP4: 190.00e-6 for 21 ka BP) + xf12_a = 0.0, & ! CFC-12 (latitude dependent) + xsf6_a = 0.0 ! SF6 (latitude dependent) + +! Transient values of atmospheric trace gases (1d-arrays of variable length to be specified in namelist.config -> length_transit) + real(kind=8), allocatable, dimension(:) :: r14c_nh, r14c_tz, r14c_sh, & ! 14CO2 / 12CO2, latitude-dependent (e.g., bomb 14C) + r14c_ti, & ! 14CO2 / 12CO2, homogenous (e.g., IntCal) + xCO2_ti, & ! CO2 + xf12_nh, xf12_sh, & ! CFC-12, latitude-dependent + xsf6_nh, xsf6_sh ! SF6, latitude-dependent + integer, allocatable, dimension(:) :: year_ce ! current year in anthropenic runs (control output) + integer :: length_transit = 1, & ! length (years) of transient tracer input + ti_start_transit = 1 ! index of the first tracer input year in ifile_transit + logical :: anthro_transit = .false., & + paleo_transit = .false. ! specify tracer input scenario + character(300) :: ifile_transit ='Table_CO2_isoC_CFC12_SF6.txt'! tracer input file; not neccessary for steady state simulations + + +! Parameters which can be changed via namelist.oce (-> transit_param) +! Global-mean concentrations of DIC and Argon in the mixed layer (mol / m**3) + real(kind=8) :: dic_0 = 2.00, & ! GLODAPv2, 0-50 m: TCO2 ~ 2050 umol / kg + arg_0 = 0.01 ! Hamme et al. 2019, doi:10.1146/annurev-marine-121916-063604 +! Radioactive decay constants (1 / s; default values assume that 1 year = 365.00 days) + real(kind=8) :: decay14 = 3.8561e-12 , & ! 14C; t1/2 = 5700 a following OMIP-BGC + decay39 = 8.1708e-11 ! 39Ar; t1/2 = 269 a + +! Further internal parameters +! Latitude of atmospheric boundary conditions and latitudinal interpolation weight + real(kind=8) :: y_abc, yy_nh +! Tracer indices of transient tracers + integer :: id_r14c, id_r39ar, id_f12, id_sf6 + integer, dimension(4) :: index_transit = (/-1, -1, -1, -1/) +! Time index (=year) in transient simulations + integer :: ti_transit + +! Namelist to modify default parameter settings + namelist / transit_param / & + anthro_transit, & ! anthoropogenic transient tracers + paleo_transit, & ! paleo transient tracers + length_transit, & ! 166 for anthro_transit=.true. + ti_start_transit, & ! 1 for D14C, 80 for CFC-12 + ifile_transit, & ! forcing file + r14c_a, & ! atm. 14C/C ratio, global mean + r39ar_a, & ! atmospheric 39Ar/Ar ratio + xarg_a, & ! atmospheric mole fraction of Argon + xco2_a, & ! atmospheric mole fraction of CO2 + dic_0, arg_0, & ! mixed layer values of DIC and Argon + decay14, decay39 ! decay constants of 14C and 39Ar + + contains + + function iso_flux(which_gas, temp_c, sal, wind_2, f_ice, p_atm, x_gas, r_air, r_sea, c_surf) +! Calculate isotopic air-sea exchange fluxes in 1 / (m**2 * s) assuming local solubility equilibrium +! for the abundant isotopologue. Positive values mean oceanic uptake. + implicit none + + real(kind=8) :: iso_flux +! Input parameters + character(len=3), intent(in) :: which_gas ! trace gas name + + real(kind=8), intent(in) :: temp_c, sal, & ! SST (deg C) and SSS ("PSU" or permil) + wind_2, & ! wind speed at 10 m heigth squared + f_ice, & ! sea-ice fractional coverage + p_atm, & ! total atmospheric pressure (Pa) + x_gas, & ! atmospheric mole fraction of the abundant isotope + r_air, r_sea, & ! isotopic ratios in atmosphere and ocean + c_surf ! surface water concentration of the abundant isotope (mol / m**3) + + iso_flux = transfer_vel(which_gas, temp_c, wind_2) * & + solub(which_gas, temp_c, sal) * p_atm / 1.01325e5 * x_gas * & + (r_air - r_sea) * (1. - f_ice) / c_surf + return + end function iso_flux + + + function gas_flux(which_gas, temp_c, sal, wind_2, f_ice, p_atm, x_gas, c_surf) +! Computes air-sea exchange gas fluxes in mol / (m**2 * s) , positive values mean oceanic uptake. + implicit none + + real(kind=8) :: gas_flux +! Input parameters + character(len=3), intent(in) :: which_gas ! trace gas name + real(kind=8), intent(in) :: temp_c, sal, & ! SST (deg C) and SSS ("PSU" or permil) + wind_2, & ! wind speed at 10 m heigth squared + f_ice, & ! sea-ice fractional coverage + p_atm, & ! total atmospheric pressure (Pa) + x_gas, & ! atmospheric mole fraction + c_surf ! marine surface water concentration (mol / m**3) +! Internal variables + real(kind=8) :: c_sat ! marine saturation concentration (mol / m**3) + c_sat = solub(which_gas, temp_c, sal) * p_atm / 1.01325e5 * x_gas + gas_flux = transfer_vel(which_gas, temp_c, wind_2) * (c_sat - c_surf) * (1. - f_ice) + + return + end function gas_flux + + + function solub(which_gas, temp_c, sal) +! Computes the solubility of trace gases in seawater. +! This parametrization includes the effect of water vapor. + implicit none + real(kind=8) :: solub ! solubility ((p)mol / (m**3 * atm)) +! Input parameters + character(len=3), intent(in) :: which_gas ! tracer name + real(kind=8), intent(in) :: temp_c, & ! temperature (deg C) + sal ! salinity ("PSU" or permil) + real(kind=8) :: a1, a2, a3, a4, & ! polynomial coefficients of the + b1, b2, b3, b4, c1, & ! solubility function + temp_k100, & ! water temperature in K / 100 + con2con ! concentration units conversion factor + integer :: pow ! power in solubility function + + temp_k100 = (temp_c + 273.15) * 0.01 + + select case (which_gas) + case ("co2") +! CO2 in mol / (L * atm) (Weiss & Price 1985, doi:10.1016/0304-4203(80)90024-9, Table VI) + a1 = -160.7333; a2 = 215.4152; a3 = 89.8920; a4 = -1.47759; pow = 2 + b1 = 0.029941; b2 = -0.027455; b3 = 0.0053407; c1 = 0. + con2con = 1000. ! convert to mol / (m**3 * atm) + case ("f12") +! CFC-12 in mol / (L * atm) (Warner & Weiss 1985, doi:10.1016/0198-0149(85)90099-8, Table 5) + a1 = -218.0971; a2 = 298.9702; a3 = 113.8049; a4 = -1.39165; pow = 2 + b1 = -0.143566; b2 = 0.091015; b3 = -0.0153924; c1 = 0. + con2con = 1000. ! convert to mol / (m**3 * atm) + case ("sf6") +! SF6 in mol / (L * atm) (Bullister et al. 2002, doi:10.1016/S0967-0637(01)00051-6, Table 3) + a1 = -80.0343; a2 = 117.232; a3 = 29.5817; a4 = 0.; pow = 2 + b1 = 0.0335183; b2 = -0.0373942; b3 = 0.00774862; c1 = 0. + con2con = 1000. ! convert to mol / (m**3 * atm) + case("arg") +! Argon in mol / kg (Jenkins et al. 2019, doi:10.1016/j.marchem.2019.03.007, Table 4) + a1 = -227.4607; a2 = 305.4347; a3 = 180.5278; a4 = -27.99450; pow = 1 + b1 = -0.066942; b2 = 0.037201; b3 = -0.0056364; c1 = -5.30e-6 + con2con = 1024.5 ! convert to mol / m**3 assuming homogeneous density of surface water + end select + + solub = exp( a1 + a2 / temp_k100 + a3 * log(temp_k100) + a4 * temp_k100 **pow + & + sal * (b1 + b2 * temp_k100 + b3 * temp_k100**2 + c1 * sal)) + solub = solub * con2con + + return + end function solub + + + function sc_660(which_gas, temp_c) +! Schmidt numbers of trace gases in sea water with S = 35 +! normalized to 20 degC (Sc(CO2) ~660; Wanninkhof 2014, tab. 1)). + implicit none +! Result + real(kind=8) :: sc_660 ! Schmidt number +! Input parameters + character(len=3), intent(in) :: which_gas ! tracer name + real(kind=8), intent(in) :: temp_c ! temperature (deg C) +! Internal parameters and/or variables + real(kind=8) :: as, bs, cs, ds, es ! polynomial coefficients + + select case (which_gas) + case ("co2") ! CO2 + as = 2116.8; bs = -136.25; cs = 4.7353; ds = -0.092307; es = 0.0007555 + case ("f12") ! CFC-12 + as = 3828.1; bs = -249.86; cs = 8.7603; ds = -0.171600; es = 0.0014080 + case ("sf6") ! SF6 + as = 3177.5; bs = -200.57; cs = 6.8865; ds = -0.133350; es = 0.0010877 + case ("arg") ! Ar-39 + as = 2078.1; bs = -146.74; cs = 5.6403; ds = -0.118380; es = 0.0010148 + end select + + sc_660 = (as + bs *temp_c + cs * temp_c**2 + ds * temp_c**3 + es * temp_c**4) / 660. + + return + end function sc_660 + + + function transfer_vel(which_gas, temp_c, wind_2) +! Compute gas transfer velocities of / for tracers + implicit none +! Result + real(kind=8) :: transfer_vel ! transfer velocity (m / s) +! Input parameters + character(len=3), intent(in) :: which_gas ! tracer name + real(kind=8), intent(in) :: temp_c, & ! temperature (deg C) + wind_2 ! wind speed squared at 10 m height (m / s) + +! Wanninkhof (2014), eq. (4) with a = 0.251 (cm / h) / (m / s)**2 -> 6.9722e-7 s / m +! to obtain the gas transfer velocity in m / s + transfer_vel = 6.9722e-7 * sc_660(which_gas, temp_c)**(-0.5) * wind_2 + + return + end function transfer_vel + + + function speed_2(windstr_x, windstr_y) +! Computes the square of wind speed at 10 m height from wind stress fields +! in coupled simulations as long as it is not provided by the AGCM / OASIS. +! We follow Peixoto & Oort (1992, Eq. (10.28), (10,29)) and Charnock (1955); +! also see MPI report 349 (2003), Eq. (5.7). + implicit none + real(kind=8) :: speed_2 + +! Input + real(kind=8), intent(in) :: windstr_x, windstr_y + +! Internal variables and parameters +! Zonal and meridional velocities at 10 m height + real(kind=8) :: u_10, v_10 +! Zonal and meridional friction velocities + real(kind=8) :: u_fric, v_fric +! Zonal and meridional roughness lengths + real(kind=8) :: l_rough_x, l_rough_y +! Inverse von-Karman constant (0.4), Charnock constant (0.018) divided by g, inverse density of air (1.3), log(10) + real(kind=8), parameter :: inv_karm = 2.5, charn_g = 0.00173, inv_dens_air = 0.76923, log_10 = 2.30258 + +! Calculate friction velocities (Peixoto & Oort, 1992, Eq. (10.28)) + u_fric = sqrt(abs(windstr_x) * inv_dens_air) + v_fric = sqrt(abs(windstr_y) * inv_dens_air) + +! Calculate roughness lengths (MPI report 349, 2003, Eq. (5.7), quoting Charnock, 1955) + l_rough_x = max((charn_g * u_fric**2), 1.5e-5) + l_rough_y = max((charn_g * v_fric**2), 1.5e-5) + +! Calculate wind speed at 10 m (Peixoto & Oort, 1992, Eq. (10.29)) + u_10 = inv_karm * u_fric * (log_10 - log(l_rough_x)) + v_10 = inv_karm * v_fric * (log_10 - log(l_rough_y)) + + speed_2 = u_10**2 + v_10**2 + + return + end function speed_2 + + + subroutine read_transit_input +! Read atmospheric input of isoCO2 and / or other tracers + implicit none + +! Internal variables + integer :: jj + real(kind=8), allocatable, dimension(:) :: d14c_nh, d14c_tz, d14c_sh, d14c_ti, d13c_dummy + + if (anthro_transit) then +! Anthropogenic input for 1850 - 2015 CE + allocate(d14c_nh(length_transit)) + allocate(d14c_tz(length_transit)) + allocate(d14c_sh(length_transit)) + allocate(r14c_nh(length_transit)) + allocate(r14c_tz(length_transit)) + allocate(r14c_sh(length_transit)) + allocate(xCO2_ti(length_transit)) + allocate(xf12_nh(length_transit)) + allocate(xf12_sh(length_transit)) + allocate(xsf6_nh(length_transit)) + allocate(xsf6_sh(length_transit)) + allocate(year_ce(length_transit)) + allocate(d13c_dummy(length_transit)) + +! Skip header lines + do jj = 1,15 + read (20, fmt=*) + end do +! Read input values + do jj = 1, length_transit + read (20, fmt=*) year_ce(jj), & + xCO2_ti(jj), & + d14c_nh(jj), d14c_tz(jj), d14c_sh(jj), & + d13c_dummy(jj), & + xf12_nh(jj), xf12_sh(jj), & + xsf6_nh(jj), xsf6_sh(jj) + end do + +! Convert Delta14C to F14C + r14c_nh = 1. + 0.001 * d14c_nh + r14c_tz = 1. + 0.001 * d14c_tz + r14c_sh = 1. + 0.001 * d14c_sh +! Convert volume mixing ratios + xCO2_ti = xCO2_ti * 1.e-6 + xf12_nh = xf12_nh * 1.e-12 + xf12_sh = xf12_sh * 1.e-12 + xsf6_nh = xsf6_nh * 1.e-12 + xsf6_sh = xsf6_sh * 1.e-12 + elseif (paleo_transit) then +! UNDER CONSTRUCTION + allocate(d14c_ti(length_transit)) + allocate(r14c_ti(length_transit)) + allocate(xCO2_ti(length_transit)) +! + else +! Read constant parameter values from namelist.oce. +! This is done in subroutine gen_model_setup. + allocate(d14c_ti(1)) + allocate(r14c_ti(1)) + allocate(xCO2_ti(1)) + end if + + return + end subroutine read_transit_input + +END MODULE mod_transit +!========================================================== diff --git a/src/oce_ale_tracer.F90 b/src/oce_ale_tracer.F90 index 511c4fa0e..53d883f41 100644 --- a/src/oce_ale_tracer.F90 +++ b/src/oce_ale_tracer.F90 @@ -43,32 +43,36 @@ subroutine diff_ver_part_redi_expl(tracer, partit, mesh) module diff_ver_part_impl_ale_interface interface - subroutine diff_ver_part_impl_ale(tr_num, dynamics, tracer, partit, mesh) + subroutine diff_ver_part_impl_ale(tr_num, dynamics, tracer, ice, partit, mesh) use mod_mesh USE MOD_PARTIT USE MOD_PARSUP use mod_tracer use MOD_DYN + use mod_ice integer , intent(in) , target :: tr_num type(t_dyn) , intent(inout), target :: dynamics type(t_tracer), intent(inout), target :: tracer type(t_partit), intent(inout), target :: partit type(t_mesh) , intent(in) , target :: mesh + type(t_ice) , intent(in) , target :: ice end subroutine end interface end module module diff_tracers_ale_interface interface - subroutine diff_tracers_ale(tr_num, dynamics, tracer, partit, mesh) + subroutine diff_tracers_ale(tr_num, dynamics, tracer, ice, partit, mesh) use mod_mesh USE MOD_PARTIT USE MOD_PARSUP use mod_tracer + use mod_ice use MOD_DYN integer , intent(in), target :: tr_num type(t_dyn) , intent(inout), target :: dynamics type(t_tracer), intent(inout), target :: tracer + type(t_ice), intent(in), target :: ice type(t_partit), intent(inout), target :: partit type(t_mesh) , intent(in) , target :: mesh end subroutine @@ -89,6 +93,20 @@ function bc_surface(n, id, sval, nzmin, partit) end interface end module +module transit_bc_surface_interface + interface + function transit_bc_surface(n, id, sst, sss, a_ice, sval, nzmin, partit) + use mod_mesh + USE MOD_PARTIT + USE MOD_PARSUP + integer , intent(in) :: n, id, nzmin + type(t_partit), intent(inout), target :: partit + real(kind=WP) :: transit_bc_surface + real(kind=WP), intent(in) :: sst, sss, a_ice, sval + end function + end interface +end module + module diff_part_bh_interface interface subroutine diff_part_bh(tr_num, dynamics, tracer, partit, mesh) @@ -142,6 +160,7 @@ subroutine solve_tracers_ale(ice, dynamics, tracers, partit, mesh) use diff_tracers_ale_interface use oce_adv_tra_driver_interfaces use g_forcing_param, only: use_age_tracer !---age-code + use mod_transit, only: decay14, decay39 implicit none type(t_ice) , intent(in) , target :: ice type(t_dyn) , intent(inout), target :: dynamics @@ -231,7 +250,11 @@ subroutine solve_tracers_ale(ice, dynamics, tracers, partit, mesh) !$OMP END PARALLEL DO ! diffuse tracers if (flag_debug .and. mype==0) print *, achar(27)//'[37m'//' --> call diff_tracers_ale'//achar(27)//'[0m' - call diff_tracers_ale(tr_num, dynamics, tracers, partit, mesh) + call diff_tracers_ale(tr_num, dynamics, tracers, ice, partit, mesh) + +! Radioactive decay of 14C and 39Ar + if (tracers%data(tr_num)%ID == 14) tracers%data(tr_num)%values(:,:) = tracers%data(tr_num)%values(:,:) * exp(-decay14 * dt) + if (tracers%data(tr_num)%ID == 39) tracers%data(tr_num)%values(:,:) = tracers%data(tr_num)%values(:,:) * exp(-decay39 * dt) ! relax to salt and temp climatology if (flag_debug .and. mype==0) print *, achar(27)//'[37m'//' --> call relax_to_clim'//achar(27)//'[0m' @@ -305,7 +328,7 @@ end subroutine solve_tracers_ale ! ! !=============================================================================== -subroutine diff_tracers_ale(tr_num, dynamics, tracers, partit, mesh) +subroutine diff_tracers_ale(tr_num, dynamics, tracers, ice, partit, mesh) use mod_mesh USE MOD_PARTIT USE MOD_PARSUP @@ -318,10 +341,12 @@ subroutine diff_tracers_ale(tr_num, dynamics, tracers, partit, mesh) use diff_ver_part_redi_expl_interface use diff_ver_part_impl_ale_interface use diff_part_bh_interface + use mod_ice implicit none integer , intent(in) , target :: tr_num type(t_dyn) , intent(inout), target :: dynamics type(t_tracer), intent(inout), target :: tracers + type(t_ice) , intent(in) , target :: ice type(t_partit), intent(inout), target :: partit type(t_mesh) , intent(in) , target :: mesh !___________________________________________________________________________ @@ -369,7 +394,7 @@ subroutine diff_tracers_ale(tr_num, dynamics, tracers, partit, mesh) !___________________________________________________________________________ if (tracers%data(tr_num)%i_vert_diff) then ! do vertical diffusion: implicite - call diff_ver_part_impl_ale(tr_num, dynamics, tracers, partit, mesh) + call diff_ver_part_impl_ale(tr_num, dynamics, tracers, ice, partit, mesh) end if !We DO not set del_ttf to zero because it will not be used in this timestep anymore !init_tracers_AB will set it to zero for the next timestep @@ -447,7 +472,7 @@ end subroutine diff_ver_part_expl_ale ! !=============================================================================== ! vertical diffusivity augmented with Redi contribution [vertical flux of K(3,3)*d_zT] -subroutine diff_ver_part_impl_ale(tr_num, dynamics, tracers, partit, mesh) +subroutine diff_ver_part_impl_ale(tr_num, dynamics, tracers, ice, partit, mesh) use MOD_MESH USE MOD_PARTIT USE MOD_PARSUP @@ -462,12 +487,17 @@ subroutine diff_ver_part_impl_ale(tr_num, dynamics, tracers, partit, mesh) use o_mixing_KPP_mod !for ghats _GO_ use g_cvmix_kpp, only: kpp_nonlcltranspT, kpp_nonlcltranspS, kpp_oblmixc use bc_surface_interface + use transit_bc_surface_interface + use mod_ice implicit none integer , intent(in) , target :: tr_num type(t_dyn) , intent(inout), target :: dynamics type(t_tracer), intent(inout), target :: tracers type(t_partit), intent(inout), target :: partit type(t_mesh) , intent(in) , target :: mesh + + type(t_ice) , intent(in) , target :: ice + !___________________________________________________________________________ real(kind=WP) :: a(mesh%nl), b(mesh%nl), c(mesh%nl), tr(mesh%nl) real(kind=WP) :: cp(mesh%nl), tp(mesh%nl) @@ -481,6 +511,8 @@ subroutine diff_ver_part_impl_ale(tr_num, dynamics, tracers, partit, mesh) ! pointer on necessary derived types real(kind=WP), dimension(:,:), pointer :: trarr real(kind=WP), dimension(:,:), pointer :: Wvel_i + real(kind=WP), dimension(:,:), pointer :: sst, sss ! auxiliary variables needed for transient tracers + real(kind=WP), dimension(:), pointer :: a_ice #include "associate_part_def.h" #include "associate_mesh_def.h" #include "associate_part_ass.h" @@ -488,6 +520,9 @@ subroutine diff_ver_part_impl_ale(tr_num, dynamics, tracers, partit, mesh) trarr => tracers%data(tr_num)%values(:,:) Wvel_i => dynamics%w_i(:,:) + sst => tracers%data(1)%values(:,:) + sss => tracers%data(2)%values(:,:) + a_ice => ice%data(1)%values(:) !___________________________________________________________________________ if ((trim(tracers%data(tr_num)%tra_adv_lim)=='FCT') .OR. (.not. dynamics%use_wsplit)) do_wimpl=.false. if (Redi) isredi=1._WP @@ -859,7 +894,9 @@ subroutine diff_ver_part_impl_ale(tr_num, dynamics, tracers, partit, mesh) ! v (+) v (+) ! tr(nzmin)= tr(nzmin)+bc_surface(n, tracers%data(tr_num)%ID, trarr(nzmin,n), nzmin, partit) - + if ((tracers%data(tr_num)%ID .ge. 6) .and.(tracers%data(tr_num)%ID .le. 40)) then + tr(nzmin)= tr(nzmin)+transit_bc_surface(n, tracers%data(tr_num)%ID, sst(nzmin,n), sss(nzmin,n), a_ice(n), trarr(nzmin,n), nzmin, partit) + end if !_______________________________________________________________________ ! The forward sweep algorithm to solve the three-diagonal matrix ! problem @@ -1309,6 +1346,7 @@ FUNCTION bc_surface(n, id, sval, nzmin, partit) USE o_ARRAYS USE g_forcing_arrays USE g_config + use mod_transit implicit none integer, intent(in) :: n, id, nzmin @@ -1316,9 +1354,8 @@ FUNCTION bc_surface(n, id, sval, nzmin, partit) type(t_partit),intent(inout), target :: partit REAL(kind=WP) :: bc_surface character(len=10) :: id_string + real(kind=WP), dimension(:), pointer :: a_ice - ! --> is_nonlinfs=1.0 for zelvel,zstar .... - ! --> is_nonlinfs=0.0 for linfs SELECT CASE (id) CASE (1) bc_surface=-dt*(heat_flux(n)/vcpw + sval*water_flux(n)*is_nonlinfs) @@ -1327,6 +1364,8 @@ FUNCTION bc_surface(n, id, sval, nzmin, partit) ! by forming/melting of sea ice bc_surface= dt*(virtual_salt(n) & !--> is zeros for zlevel/zstar + relax_salt(n) - real_salt_flux(n)*is_nonlinfs) +!---Transient tracers (case ##6,12,14,39) need additional input parameters +! and are considered in the separate function transit_bc_surface !---wiso-code CASE (101) ! apply boundary conditions to tracer ID=101 (H218O) bc_surface = dt*wiso_flux_oce(n,1) @@ -1363,3 +1402,132 @@ FUNCTION bc_surface(n, id, sval, nzmin, partit) END SELECT RETURN END FUNCTION + + +!=============================================================================== +! This function returns a boundary conditions for a specified transient tracer ID and surface node. +! Different to function bc_surface, SST, SSS, and sea ice concentrations are always needed as +! auxiliary variable +FUNCTION transit_bc_surface(n, id, sst, sss, aice, sval, nzmin, partit) + use MOD_MESH + USE MOD_PARTIT + USE MOD_PARSUP + USE o_ARRAYS + USE g_forcing_arrays + USE g_config + use g_clock + use mod_transit + implicit none + + integer, intent(in) :: n, id, nzmin + real(kind=WP), intent(in) :: sst, sss, aice, sval + type(t_partit),intent(inout), target :: partit + REAL(kind=WP) :: transit_bc_surface + character(len=10) :: id_string + + + ! --> is_nonlinfs=1.0 for zelvel,zstar .... + ! --> is_nonlinfs=0.0 for linfs + +#if defined (__oasis) +! SLP and wind speed in coupled setups. This is a makeshift solution +! as long as the true values are not provided by the AGCM / OASIS. + press_a = mean_slp + wind_2 = speed_2(stress_atmoce_x(n), stress_atmoce_y(n)) +#else + press_a = press_air(n) + wind_2 = u_wind(n)**2 + v_wind(n)**2 +#endif + + SELECT CASE (id) + +! Boundary conditions for additional (transient) tracers (14C, 39Ar, CFC-12, and SF6) + CASE (14) ! Radiocarbon (more precisely, fractionation-corrected 14C/C): + if (anthro_transit) then +! Select atmospheric input values corresponding to the latitude + if (y_abc > 30.) then +! Northern Hemisphere + r14c_a = r14c_nh(ti_transit) + else if (y_abc <- 30.) then +! Southern Hemisphere + r14c_a = r14c_sh(ti_transit) + else +! Tropical zone + r14c_a = r14c_tz(ti_transit) + end if + xCO2_a = xCO2_ti(ti_transit) + else if (paleo_transit) then + r14c_a = r14c_ti(ti_transit) + xCO2_a = xCO2_ti(ti_transit) + else +! Constant (global-mean) namelist values are taken + end if +! Local isotopic 14CO2/CO2 air-sea exchange flux (in m / s), +! since F14C is normalized to atmospheric (water) values the isotopic flux has to be +! corrected for precipitation or evaporation fluxes with different isotopic signatures. + transit_bc_surface = dt * (iso_flux("co2", sst, sss, wind_2, aice, press_a, xco2_a, r14c_a, sval, dic_0) & + - sval * water_flux(n) * is_nonlinfs) + + CASE (39) ! Argon-39 (fractionationation-corrected 39Ar/Ar) +! Local isotopic 39Ar/Ar air-sea exchange flux (in m / s), +! since F39Ar is normalized to atmospheric (water) values the isotopic flux has to be +! corrected for precipitation or evaporation fluxes with different isotopic signatures. + transit_bc_surface = dt * (iso_flux("arg", sst, sss, wind_2, aice, press_a, xarg_a, r39ar_a, sval, arg_0) & + - sval * water_flux(n) * is_nonlinfs) + + CASE (12) ! CFC-12 + if (anthro_transit) then +! Select atmospheric input values corresponding to the latitude +! Annual values are interpolated to monthly values, this is omitted in the last simulation year + if (y_abc > 10.) then ! Northern Hemisphere +! Northern Hemisphere + xf12_a = xf12_nh(ti_transit) + if (ti_transit < length_transit) xf12_a = xf12_a + month * (xf12_nh(ti_transit + 1) - xf12_a) / 12. + else if (y_abc <- 10.) then +! Southern Hemisphere + xf12_a = xf12_sh(ti_transit) + if (ti_transit < length_transit) xf12_a = xf12_a + month * (xf12_sh(ti_transit + 1) - xf12_a) / 12. + else +! Tropical zone, interpolate between NH and SH + xf12_a = (1 - yy_nh) * xf12_nh(ti_transit) + yy_nh * xf12_sh(ti_transit) + if (ti_transit < length_transit) & + xf12_a = xf12_a + month * ((1 - yy_nh) * xf12_nh(ti_transit + 1) + yy_nh * xf12_sh(ti_transit + 1) - xf12_a) / 12. + end if + else +! Constant (global-mean) namelist values are taken + end if + +! Local air-sea exchange gas flux of CFC-12 (in m / s): + transit_bc_surface = dt * (gas_flux("f12", sst, sss, wind_2, aice, press_a, xf12_a, sval) & + - sval * water_flux(n) * is_nonlinfs) + + CASE (6) ! SF6 + if (anthro_transit) then +! Select atmospheric input values corresponding to the latitude +! Annual values are interpolated to monthly values, this is omitted in the last simulation year + if (y_abc > 10.) then ! Northern Hemisphere +! Northern Hemisphere + xsf6_a = xsf6_nh(ti_transit) + if (ti_transit < length_transit) xsf6_a = xsf6_a + month * (xsf6_nh(ti_transit + 1) - xsf6_a) / 12. + else if (y_abc <- 10.) then +! Southern Hemisphere + xsf6_a = xsf6_sh(ti_transit) + if (ti_transit < length_transit) xsf6_a = xsf6_a + month * (xsf6_sh(ti_transit + 1) - xsf6_a) / 12. + else +! Tropical zone, interpolate between NH and SH + xsf6_a = (1 - yy_nh) * xsf6_nh(ti_transit) + yy_nh * xsf6_sh(ti_transit) + if (ti_transit < length_transit) & + xsf6_a = xsf6_a + month * ((1 - yy_nh) * xsf6_nh(ti_transit + 1) + yy_nh * xsf6_sh(ti_transit + 1) - xsf6_a) / 12. + end if + else +! Constant (global-mean) namelist values are taken + end if + +! Local air-sea exchange gas flux of SF6 (in m / s): + transit_bc_surface = dt * (gas_flux("sf6", sst, sss, wind_2, aice, press_a, xsf6_a, sval) & + - sval * water_flux(n) * is_nonlinfs) + +! Done with boundary conditions for (transient) tracers. + END SELECT + RETURN +END FUNCTION diff --git a/src/oce_setup_step.F90 b/src/oce_setup_step.F90 index 2b2f59f0d..9ec0bb0dc 100755 --- a/src/oce_setup_step.F90 +++ b/src/oce_setup_step.F90 @@ -276,7 +276,8 @@ SUBROUTINE tracer_init(tracers, partit, mesh) USE DIAGNOSTICS, only: ldiag_DVD USE g_ic3d use g_forcing_param, only: use_age_tracer !---age-code - use g_config, only : lwiso ! add lwiso switch + use g_config, only : lwiso, use_transit ! add lwiso switch and switch for transient tracers + use mod_transit, only : index_transit IMPLICIT NONE type(t_tracer), intent(inout), target :: tracers type(t_partit), intent(inout), target :: partit @@ -373,6 +374,36 @@ SUBROUTINE tracer_init(tracers, partit, mesh) endif !---age-code-end + ! Transient tracers +!! UNDER CONSTRUCTION - Actually we do not want to hardwire the number of transient tracers + if (use_transit) then + ! add transient tracers to the model + nml_tracer_list(num_tracers+1) = nml_tracer_list(1) + nml_tracer_list(num_tracers+2) = nml_tracer_list(1) + nml_tracer_list(num_tracers+3) = nml_tracer_list(1) + nml_tracer_list(num_tracers+4) = nml_tracer_list(1) + nml_tracer_list(num_tracers+1)%id = 6 + nml_tracer_list(num_tracers+1)%id = 12 + nml_tracer_list(num_tracers+1)%id = 14 + nml_tracer_list(num_tracers+1)%id = 39 + + index_transit(1) = num_tracers+1 + index_transit(2) = num_tracers+2 + index_transit(3) = num_tracers+3 + index_transit(4) = num_tracers+4 + + num_tracers = num_tracers + 4 + + ! tracers initialised from file + idlist((n_ic3d+1):(n_ic3d+1)) = (/14/) + filelist((n_ic3d+1):(n_ic3d+1)) = (/'R14C.nc'/) + varlist((n_ic3d+1):(n_ic3d+1)) = (/'R14C'/) + + if (mype==0) write(*,*) 'XXX Transient tracers will be used in FESOM' + endif + ! 'use_transit' end + + if (mype==0) write(*,*) 'total number of tracers is: ', num_tracers !___________________________________________________________________________ @@ -850,6 +881,8 @@ SUBROUTINE oce_initial_state(tracers, partit, mesh) USE o_ARRAYS USE g_config USE g_ic3d + ! for additional (transient) tracers: + use mod_transit, only: id_r14c, id_r39ar, id_f12, id_sf6 implicit none type(t_tracer), intent(inout), target :: tracers type(t_partit), intent(inout), target :: partit @@ -873,6 +906,7 @@ SUBROUTINE oce_initial_state(tracers, partit, mesh) ! this must be always done! First two tracers with IDs 0 and 1 are the temperature and salinity. if(mype==0) write(*,*) 'read Temperature climatology from:', trim(filelist(1)) if(mype==0) write(*,*) 'read Salinity climatology from:', trim(filelist(2)) + if(any(idlist == 14) .and. mype==0) write(*,*) 'read radiocarbon climatology from:', trim(filelist(3)) call do_ic3d(tracers, partit, mesh) Tclim=tracers%data(1)%values @@ -943,6 +977,44 @@ SUBROUTINE oce_initial_state(tracers, partit, mesh) end if !---wiso-code-end +! Transient tracers + CASE (14) ! initialize tracer ID=14, fractionation-corrected 14C/C +! this initialization can be overwritten by calling do_ic3d +!! if (.not. any(idlist == 14)) then ! CHECK IF THIS LINE IS STILL NECESSARY + tracers%data(i)%values(:,:) = 0.85 + if (mype==0) then + write (i_string, "(I3)") i + write (id_string, "(I3)") id + write(*,*) 'initializing '//trim(i_string)//'th tracer with ID='//trim(id_string) + write (*,*) tracers%data(i)%values(1,1) + end if +!! end if + CASE (39) ! initialize tracer ID=39, fractionation-corrected 39Ar/Ar + tracers%data(i)%values(:,:) = 0.85 + if (mype==0) then + write (i_string, "(I3)") i + write (id_string, "(I3)") id + write(*,*) 'initializing '//trim(i_string)//'th tracer with ID='//trim(id_string) + write (*,*) tracers%data(i)%values(1,1) + end if + CASE (12) ! initialize tracer ID=12, CFC-12 + tracers%data(i)%values(:,:) = 0. + if (mype==0) then + write (i_string, "(I3)") i + write (id_string, "(I3)") id + write(*,*) 'initializing '//trim(i_string)//'th tracer with ID='//trim(id_string) + write (*,*) tracers%data(i)%values(1,1) + end if + CASE (6) ! initialize tracer ID=6, SF6 + tracers%data(i)%values(:,:) = 0. + if (mype==0) then + write (i_string, "(I3)") i + write (id_string, "(I3)") id + write(*,*) 'initializing '//trim(i_string)//'th tracer with ID='//trim(id_string) + write (*,*) tracers%data(i)%values(1,1) + end if +! Transient tracers end + !_______________________________________________________________________ CASE (301) !Fram Strait 3d restored passive tracer tracers%data(i)%values(:,:)=0.0_WP