| 1 | MODULE lmdz_aviation |
|---|
| 2 | !---------------------------------------------------------------- |
|---|
| 3 | ! Module for aviation and contrails |
|---|
| 4 | |
|---|
| 5 | IMPLICIT NONE |
|---|
| 6 | |
|---|
| 7 | ! Arrays for the lecture of aviation files |
|---|
| 8 | ! The allocation is done in the read_aviation module |
|---|
| 9 | ! The size is (klon, nleva, 1) where |
|---|
| 10 | ! nleva is the size of the vertical axis (read from file) |
|---|
| 11 | ! flight_dist_read is the number of km per second |
|---|
| 12 | ! flight_h2o_read is the water content added to the air |
|---|
| 13 | ! aviation_lev is the value of the levels |
|---|
| 14 | INTEGER, SAVE :: nleva ! Size of the vertical axis in the file |
|---|
| 15 | !$OMP THREADPRIVATE(nleva) |
|---|
| 16 | REAL, ALLOCATABLE, DIMENSION(:,:,:), SAVE, PRIVATE :: flight_dist_read ! Aviation distance flown within the mesh [m/s/mesh] |
|---|
| 17 | !$OMP THREADPRIVATE(flight_dist_read) |
|---|
| 18 | REAL, ALLOCATABLE, DIMENSION(:,:,:), SAVE, PRIVATE :: flight_h2o_read ! Aviation H2O emitted within the mesh [kgH2O/s/mesh] |
|---|
| 19 | !$OMP THREADPRIVATE(flight_h2o_read) |
|---|
| 20 | REAL, ALLOCATABLE, DIMENSION(:), SAVE, PRIVATE :: aviation_lev ! Pressure in the middle of the layers [Pa] |
|---|
| 21 | !$OMP THREADPRIVATE(aviation_lev) |
|---|
| 22 | |
|---|
| 23 | CONTAINS |
|---|
| 24 | |
|---|
| 25 | SUBROUTINE aviation_water_emissions( & |
|---|
| 26 | klon, klev, dtime, pplay, temp, qtot, & |
|---|
| 27 | flight_h2o, d_q_avi & |
|---|
| 28 | ) |
|---|
| 29 | |
|---|
| 30 | USE lmdz_lscp_ini, ONLY: RD |
|---|
| 31 | |
|---|
| 32 | IMPLICIT NONE |
|---|
| 33 | |
|---|
| 34 | INTEGER, INTENT(IN) :: klon, klev ! number of horizontal grid points and vertical levels |
|---|
| 35 | REAL, INTENT(IN) :: dtime ! time step [s] |
|---|
| 36 | REAL, DIMENSION(klon,klev), INTENT(IN) :: pplay ! mid-layer pressure [Pa] |
|---|
| 37 | REAL, DIMENSION(klon,klev), INTENT(IN) :: temp ! temperature (K) |
|---|
| 38 | REAL, DIMENSION(klon,klev), INTENT(IN) :: qtot ! total specific humidity (in vapor phase) [kg/kg] |
|---|
| 39 | REAL, DIMENSION(klon,klev), INTENT(IN) :: flight_h2o ! aviation emitted H2O concentration [kgH2O/s/m3] |
|---|
| 40 | REAL, DIMENSION(klon,klev), INTENT(OUT) :: d_q_avi ! water vapor tendency from aviation [kg/kg] |
|---|
| 41 | ! Local |
|---|
| 42 | INTEGER :: i, k |
|---|
| 43 | REAL :: rho |
|---|
| 44 | |
|---|
| 45 | DO i=1, klon |
|---|
| 46 | DO k=1, klev |
|---|
| 47 | !--Dry density [kg/m3] |
|---|
| 48 | rho = pplay(i,k) / temp(i,k) / RD |
|---|
| 49 | |
|---|
| 50 | !--q is the specific humidity (kg/kg humid air) hence the complicated equation to update q |
|---|
| 51 | ! qnew = ( m_humid_air * qold + dm_H2O ) / ( m_humid_air + dm_H2O ) |
|---|
| 52 | ! = ( m_dry_air * qold + dm_h2O * (1-qold) ) / (m_dry_air + dm_H2O * (1-qold) ) |
|---|
| 53 | !--The equation is derived by writing m_humid_air = m_dry_air + m_H2O = m_dry_air / (1-q) |
|---|
| 54 | !--flight_h2O is in kg H2O / s / m3 |
|---|
| 55 | |
|---|
| 56 | !d_q_avi(i,k) = ( M_cell * qtot(i,k) + V_cell * flight_h2o(i,k) * dtime * ( 1. - qtot(i,k) ) ) & |
|---|
| 57 | ! / ( M_cell + V_cell * flight_h2o(i,k) * dtime * ( 1. - qtot(i,k) ) ) & |
|---|
| 58 | ! - qtot(i,k) |
|---|
| 59 | !--NB., M_cell = V_cell * rho |
|---|
| 60 | !d_q_avi(i,k) = ( rho * qtot(i,k) + flight_h2o(i,k) * dtime * ( 1. - qtot(i,k) ) ) & |
|---|
| 61 | ! / ( rho + flight_h2o(i,k) * dtime * ( 1. - qtot(i,k) ) ) & |
|---|
| 62 | ! - qtot(i,k) |
|---|
| 63 | !--Same formula, more computationally effective but less readable |
|---|
| 64 | d_q_avi(i,k) = flight_h2o(i,k) * ( 1. - qtot(i,k) ) & |
|---|
| 65 | / ( rho / dtime / ( 1. - qtot(i,k) ) + flight_h2o(i,k) ) |
|---|
| 66 | ENDDO |
|---|
| 67 | ENDDO |
|---|
| 68 | |
|---|
| 69 | END SUBROUTINE aviation_water_emissions |
|---|
| 70 | |
|---|
| 71 | |
|---|
| 72 | !********************************************************************************** |
|---|
| 73 | SUBROUTINE contrails_formation( & |
|---|
| 74 | klon, dtime, pplay, temp, qsat, qsatl, gamma_cond, flight_dist, & |
|---|
| 75 | clrfra, qclr, pdf_scale, pdf_alpha, pdf_gamma, keepgoing, pt_pron_clds, & |
|---|
| 76 | Tcritcont, qcritcont, potcontfraP, potcontfraNP, & |
|---|
| 77 | dcfa_ini, dqia_ini, dqta_ini) |
|---|
| 78 | |
|---|
| 79 | USE lmdz_lscp_ini, ONLY: RPI, RCPD, EPS_W, RTT |
|---|
| 80 | USE lmdz_lscp_ini, ONLY: EI_H2O_aviation, qheat_fuel_aviation, prop_efficiency_aviation |
|---|
| 81 | USE lmdz_lscp_ini, ONLY: eps, temp_nowater |
|---|
| 82 | |
|---|
| 83 | USE lmdz_lscp_tools, ONLY: GAMMAINC, calc_qsat_ecmwf |
|---|
| 84 | |
|---|
| 85 | IMPLICIT NONE |
|---|
| 86 | |
|---|
| 87 | ! |
|---|
| 88 | ! Input |
|---|
| 89 | ! |
|---|
| 90 | INTEGER, INTENT(IN) :: klon ! number of horizontal grid points |
|---|
| 91 | REAL, INTENT(IN) :: dtime ! time step [s] |
|---|
| 92 | REAL, INTENT(IN) , DIMENSION(klon) :: pplay ! layer pressure [Pa] |
|---|
| 93 | REAL, INTENT(IN) , DIMENSION(klon) :: temp ! temperature [K] |
|---|
| 94 | REAL, INTENT(IN) , DIMENSION(klon) :: qsat ! saturation specific humidity [kg/kg] |
|---|
| 95 | REAL, INTENT(IN) , DIMENSION(klon) :: qsatl ! saturation specific humidity w.r.t. liquid [kg/kg] |
|---|
| 96 | REAL, INTENT(IN) , DIMENSION(klon) :: gamma_cond ! condensation threshold w.r.t. qsat [-] |
|---|
| 97 | REAL, INTENT(IN) , DIMENSION(klon) :: flight_dist ! aviation distance flown concentration [m/s/m3] |
|---|
| 98 | REAL, INTENT(IN) , DIMENSION(klon) :: clrfra ! clear sky fraction [-] |
|---|
| 99 | REAL, INTENT(IN) , DIMENSION(klon) :: qclr ! clear sky specific humidity [kg/kg] |
|---|
| 100 | REAL, INTENT(IN) , DIMENSION(klon) :: pdf_scale ! scale parameter of the clear sky PDF [%] |
|---|
| 101 | REAL, INTENT(IN) , DIMENSION(klon) :: pdf_alpha ! shape parameter of the clear sky PDF [-] |
|---|
| 102 | REAL, INTENT(IN) , DIMENSION(klon) :: pdf_gamma ! tmp parameter of the clear sky PDF [-] |
|---|
| 103 | LOGICAL, INTENT(IN) , DIMENSION(klon) :: keepgoing ! .TRUE. if a new condensation loop should be computed |
|---|
| 104 | LOGICAL, INTENT(IN) , DIMENSION(klon) :: pt_pron_clds ! .TRUE. if clouds are prognostic in this mesh |
|---|
| 105 | ! |
|---|
| 106 | ! Output |
|---|
| 107 | ! |
|---|
| 108 | REAL, INTENT(INOUT), DIMENSION(klon) :: Tcritcont ! critical temperature for contrail formation [K] |
|---|
| 109 | REAL, INTENT(INOUT), DIMENSION(klon) :: qcritcont ! critical specific humidity for contrail formation [kg/kg] |
|---|
| 110 | REAL, INTENT(INOUT), DIMENSION(klon) :: potcontfraP ! potential persistent contrail fraction [-] |
|---|
| 111 | REAL, INTENT(INOUT), DIMENSION(klon) :: potcontfraNP ! potential non-persistent contrail fraction [-] |
|---|
| 112 | REAL, INTENT(INOUT), DIMENSION(klon) :: dcfa_ini ! contrails cloud fraction tendency bec ause of initial formation [s-1] |
|---|
| 113 | REAL, INTENT(INOUT), DIMENSION(klon) :: dqia_ini ! contrails ice specific humidity tende ncy because of initial formation [kg/kg/s] |
|---|
| 114 | REAL, INTENT(INOUT), DIMENSION(klon) :: dqta_ini ! contrails total specific humidity ten dency because of initial formation [kg/kg/s] |
|---|
| 115 | ! |
|---|
| 116 | ! Local |
|---|
| 117 | ! |
|---|
| 118 | INTEGER :: i |
|---|
| 119 | ! for Schmidt-Appleman criteria |
|---|
| 120 | REAL, DIMENSION(klon) :: qzero |
|---|
| 121 | REAL, DIMENSION(klon) :: qsatl_crit, dqsatl_crit |
|---|
| 122 | REAL :: Gcont, psatl_crit, pcrit |
|---|
| 123 | REAL :: rhl_clr, pdf_loc |
|---|
| 124 | REAL :: pdf_x, pdf_y, pdf_e3 |
|---|
| 125 | REAL :: pdf_fra_above_qcritcont, pdf_fra_above_qsat |
|---|
| 126 | REAL :: pdf_q_above_qcritcont, pdf_q_above_qsat |
|---|
| 127 | REAL :: qpotcontP |
|---|
| 128 | ! |
|---|
| 129 | ! for new contrail formation |
|---|
| 130 | REAL :: contrail_cross_section, contfra_new |
|---|
| 131 | |
|---|
| 132 | qzero(:) = 0. |
|---|
| 133 | |
|---|
| 134 | DO i = 1, klon |
|---|
| 135 | IF ( keepgoing(i) ) THEN |
|---|
| 136 | Tcritcont(i) = 0. |
|---|
| 137 | qcritcont(i) = 0. |
|---|
| 138 | potcontfraP(i) = 0. |
|---|
| 139 | potcontfraNP(i) = 0. |
|---|
| 140 | ENDIF |
|---|
| 141 | ENDDO |
|---|
| 142 | |
|---|
| 143 | !--------------------------------- |
|---|
| 144 | !-- SCHMIDT-APPLEMAN CRITERIA -- |
|---|
| 145 | !--------------------------------- |
|---|
| 146 | !--Revised by Schumann (1996) and Rap et al. (2010) |
|---|
| 147 | |
|---|
| 148 | DO i = 1, klon |
|---|
| 149 | !--Gcont is the slope of the mean phase trajectory in the turbulent exhaust field on an absolute |
|---|
| 150 | !--in Pa / K. See Rap et al. (2010) in JGR. |
|---|
| 151 | !--kg H2O/kg fuel * J kg air-1 K-1 * Pa / (kg H2O / kg air * J kg fuel-1) |
|---|
| 152 | Gcont = EI_H2O_aviation * RCPD * pplay(i) & |
|---|
| 153 | / ( EPS_W * qheat_fuel_aviation * ( 1. - prop_efficiency_aviation ) ) |
|---|
| 154 | !--critical temperature below which no liquid contrail can form in exhaust |
|---|
| 155 | !--noted T_LM in Schumann (1996), their Eq. 31 in Appendix 2 |
|---|
| 156 | !--in Kelvins |
|---|
| 157 | Tcritcont(i) = 226.69 + 9.43 * LOG( MAX(Gcont, 0.1) - 0.053 ) & |
|---|
| 158 | + 0.72 * LOG( MAX(Gcont, 0.1) - 0.053 )**2 |
|---|
| 159 | ENDDO |
|---|
| 160 | |
|---|
| 161 | CALL calc_qsat_ecmwf(klon, Tcritcont, qzero, pplay, RTT, 1, .FALSE., qsatl_crit, dqsatl_crit) |
|---|
| 162 | |
|---|
| 163 | DO i = 1, klon |
|---|
| 164 | IF ( keepgoing(i) .AND. pt_pron_clds(i) .AND. ( temp(i) .LE. temp_nowater ) ) THEN |
|---|
| 165 | |
|---|
| 166 | psatl_crit = qsatl_crit(i) / ( EPS_W + ( 1. - EPS_W ) * qsatl_crit(i) ) * pplay(i) |
|---|
| 167 | pcrit = Gcont * ( temp(i) - Tcritcont(i) ) + psatl_crit |
|---|
| 168 | qcritcont(i) = EPS_W * pcrit / ( pplay(i) - ( 1. - EPS_W ) * pcrit ) |
|---|
| 169 | |
|---|
| 170 | |
|---|
| 171 | IF ( ( clrfra(i) .GT. eps ) .AND. ( temp(i) .LT. Tcritcont(i) ) ) THEN |
|---|
| 172 | |
|---|
| 173 | rhl_clr = qclr(i) / clrfra(i) / qsatl(i) * 100. |
|---|
| 174 | pdf_loc = rhl_clr - pdf_scale(i) * pdf_gamma(i) |
|---|
| 175 | pdf_x = qcritcont(i) / qsatl(i) * 100. |
|---|
| 176 | pdf_y = LOG( MAX( pdf_x - pdf_loc, eps ) / pdf_scale(i) ) * pdf_alpha(i) |
|---|
| 177 | IF ( pdf_y .GT. 10. ) THEN !--Avoid overflows |
|---|
| 178 | pdf_fra_above_qcritcont = 0. |
|---|
| 179 | pdf_q_above_qcritcont = 0. |
|---|
| 180 | ELSEIF ( pdf_y .LT. -10. ) THEN |
|---|
| 181 | pdf_fra_above_qcritcont = clrfra(i) |
|---|
| 182 | pdf_q_above_qcritcont = qclr(i) |
|---|
| 183 | ELSE |
|---|
| 184 | pdf_y = EXP( pdf_y ) |
|---|
| 185 | pdf_e3 = GAMMAINC ( 1. + 1. / pdf_alpha(i) , pdf_y ) |
|---|
| 186 | pdf_e3 = pdf_scale(i) * ( 1. - pdf_e3 ) * pdf_gamma(i) |
|---|
| 187 | pdf_fra_above_qcritcont = EXP( - pdf_y ) * clrfra(i) |
|---|
| 188 | pdf_q_above_qcritcont = ( pdf_e3 * clrfra(i) & |
|---|
| 189 | + pdf_loc * pdf_fra_above_qcritcont ) * qsatl(i) / 100. |
|---|
| 190 | ENDIF |
|---|
| 191 | |
|---|
| 192 | pdf_x = qsat(i) / qsatl(i) * 100. |
|---|
| 193 | pdf_y = LOG( MAX( pdf_x - pdf_loc, eps ) / pdf_scale(i) ) * pdf_alpha(i) |
|---|
| 194 | IF ( pdf_y .GT. 10. ) THEN !--Avoid overflows |
|---|
| 195 | pdf_fra_above_qsat = 0. |
|---|
| 196 | pdf_q_above_qsat = 0. |
|---|
| 197 | ELSEIF ( pdf_y .LT. -10. ) THEN |
|---|
| 198 | pdf_fra_above_qsat = clrfra(i) |
|---|
| 199 | pdf_q_above_qsat = qclr(i) |
|---|
| 200 | ELSE |
|---|
| 201 | pdf_y = EXP( pdf_y ) |
|---|
| 202 | pdf_e3 = GAMMAINC ( 1. + 1. / pdf_alpha(i) , pdf_y ) |
|---|
| 203 | pdf_e3 = pdf_scale(i) * ( 1. - pdf_e3 ) * pdf_gamma(i) |
|---|
| 204 | pdf_fra_above_qsat = EXP( - pdf_y ) * clrfra(i) |
|---|
| 205 | pdf_q_above_qsat = ( pdf_e3 * clrfra(i) & |
|---|
| 206 | + pdf_loc * pdf_fra_above_qsat ) * qsatl(i) / 100. |
|---|
| 207 | ENDIF |
|---|
| 208 | |
|---|
| 209 | potcontfraNP(i) = MAX(0., pdf_fra_above_qcritcont - pdf_fra_above_qsat) |
|---|
| 210 | potcontfraP(i) = MIN(pdf_fra_above_qsat, pdf_fra_above_qcritcont) |
|---|
| 211 | qpotcontP = MIN(pdf_q_above_qsat, pdf_q_above_qcritcont) |
|---|
| 212 | |
|---|
| 213 | ENDIF ! temp .LT. Tcritcont |
|---|
| 214 | |
|---|
| 215 | !--Add a source of contrails from aviation |
|---|
| 216 | IF ( potcontfraP(i) .GT. eps ) THEN |
|---|
| 217 | contrail_cross_section = CONTRAIL_CROSS_SECTION_ONERA() |
|---|
| 218 | contfra_new = MIN(1., flight_dist(i) * dtime * contrail_cross_section) |
|---|
| 219 | dcfa_ini(i) = potcontfraP(i) * contfra_new |
|---|
| 220 | dqta_ini(i) = qpotcontP * contfra_new |
|---|
| 221 | dqia_ini(i) = dqta_ini(i) - qsat(i) * dcfa_ini(i) |
|---|
| 222 | ENDIF |
|---|
| 223 | |
|---|
| 224 | ENDIF ! keepgoing |
|---|
| 225 | ENDDO |
|---|
| 226 | |
|---|
| 227 | END SUBROUTINE contrails_formation |
|---|
| 228 | |
|---|
| 229 | |
|---|
| 230 | !********************************************************************************** |
|---|
| 231 | FUNCTION contrail_cross_section_onera() |
|---|
| 232 | |
|---|
| 233 | USE lmdz_lscp_ini, ONLY: initial_width_contrails, initial_height_contrails |
|---|
| 234 | |
|---|
| 235 | IMPLICIT NONE |
|---|
| 236 | |
|---|
| 237 | ! |
|---|
| 238 | ! Output |
|---|
| 239 | ! |
|---|
| 240 | REAL :: contrail_cross_section_onera ! [m2] |
|---|
| 241 | ! |
|---|
| 242 | ! Local |
|---|
| 243 | ! |
|---|
| 244 | |
|---|
| 245 | contrail_cross_section_onera = initial_width_contrails * initial_height_contrails |
|---|
| 246 | |
|---|
| 247 | END FUNCTION contrail_cross_section_onera |
|---|
| 248 | |
|---|
| 249 | |
|---|
| 250 | SUBROUTINE read_aviation_emissions(klon, klev) |
|---|
| 251 | ! This subroutine allows to read the traffic density data read in the file aviation.nc |
|---|
| 252 | ! This file is defined in ./COMP/lmdz.card |
|---|
| 253 | ! Field names in context_input_lmdz.xml should be the same as in the file. |
|---|
| 254 | USE netcdf |
|---|
| 255 | USE mod_phys_lmdz_para |
|---|
| 256 | USE mod_grid_phy_lmdz, ONLY: nbp_lon_=>nbp_lon, nbp_lat_=>nbp_lat, nbp_lev_=>nbp_lev, & |
|---|
| 257 | klon_glo, grid2Dto1D_glo, grid_type, unstructured |
|---|
| 258 | USE iophy, ONLY : io_lon, io_lat |
|---|
| 259 | USE lmdz_xios |
|---|
| 260 | USE print_control_mod, ONLY: lunout |
|---|
| 261 | USE readaerosol_mod, ONLY: check_err |
|---|
| 262 | USE lmdz_lscp_ini, ONLY: EI_H2O_aviation |
|---|
| 263 | IMPLICIT NONE |
|---|
| 264 | |
|---|
| 265 | INTEGER, INTENT(IN) :: klon, klev ! number of horizontal grid points and vertical levels |
|---|
| 266 | |
|---|
| 267 | !---------------------------------------------------- |
|---|
| 268 | ! Local variable |
|---|
| 269 | !---------------------------------------------------- |
|---|
| 270 | REAL, ALLOCATABLE :: flight_dist_mpi(:,:,:), flight_h2o_mpi(:,:,:) |
|---|
| 271 | INTEGER :: ierr |
|---|
| 272 | |
|---|
| 273 | ! FOR REGULAR LON LAT |
|---|
| 274 | CHARACTER(len=30) :: fname |
|---|
| 275 | INTEGER :: nid, dimid, varid |
|---|
| 276 | INTEGER :: imth, i, j, k |
|---|
| 277 | REAL :: npole, spole |
|---|
| 278 | REAL, ALLOCATABLE, DIMENSION(:,:,:,:) :: flight_dist_glo2D |
|---|
| 279 | REAL, ALLOCATABLE, DIMENSION(:,:,:,:) :: flight_h2o_glo2D |
|---|
| 280 | REAL, ALLOCATABLE, DIMENSION(:) :: vartmp_lev |
|---|
| 281 | REAL, ALLOCATABLE, DIMENSION(:,:,:) :: vartmp |
|---|
| 282 | REAL, ALLOCATABLE, DIMENSION(:) :: lon_src ! longitudes in file |
|---|
| 283 | REAL, ALLOCATABLE, DIMENSION(:) :: lat_src, lat_src_inv ! latitudes in file |
|---|
| 284 | LOGICAL :: invert_lat ! true if the field has to be inverted for latitudes |
|---|
| 285 | INTEGER :: nbp_lon, nbp_lat |
|---|
| 286 | |
|---|
| 287 | |
|---|
| 288 | IF (grid_type==unstructured) THEN |
|---|
| 289 | |
|---|
| 290 | IF (is_omp_master) THEN |
|---|
| 291 | ! Activate aviation file |
|---|
| 292 | CALL xios_set_file_attr("aviation", enabled=.TRUE.) |
|---|
| 293 | ! Activate aviation fields |
|---|
| 294 | CALL xios_set_field_attr("KMFLOWN_read", enabled=.TRUE.) |
|---|
| 295 | CALL xios_set_field_attr("KMFLOWN_interp", enabled=.TRUE.) |
|---|
| 296 | |
|---|
| 297 | ! Get number of vertical levels and level values |
|---|
| 298 | CALL xios_get_axis_attr( "aviation_lev", n_glo=nleva ) |
|---|
| 299 | ENDIF |
|---|
| 300 | CALL bcast_omp(nleva) |
|---|
| 301 | |
|---|
| 302 | ! Allocation of arrays |
|---|
| 303 | ALLOCATE(flight_dist_read(klon, nleva,1), STAT=ierr) |
|---|
| 304 | IF (ierr /= 0) CALL abort_physic('lmdz_aviation', 'problem to allocate flight_dist',1) |
|---|
| 305 | ALLOCATE(flight_h2o_read(klon, nleva,1), STAT=ierr) |
|---|
| 306 | IF (ierr /= 0) CALL abort_physic('lmdz_aviation', 'problem to allocate flight_h2o',1) |
|---|
| 307 | ALLOCATE(aviation_lev(nleva), STAT=ierr) |
|---|
| 308 | IF (ierr /= 0) CALL abort_physic('lmdz_aviation', 'problem to allocate aviation_lev',1) |
|---|
| 309 | |
|---|
| 310 | ! Read the data from the file |
|---|
| 311 | ! is_omp_master is necessary to make XIOS works |
|---|
| 312 | IF (is_omp_master) THEN |
|---|
| 313 | ALLOCATE(flight_dist_mpi(klon_mpi, nleva,1), STAT=ierr) |
|---|
| 314 | IF (ierr /= 0) CALL abort_physic('read_aviation_emissions', 'problem to allocate flight_dist_mpi',1) |
|---|
| 315 | ALLOCATE(flight_h2o_mpi(klon_mpi, nleva,1), STAT=ierr) |
|---|
| 316 | IF (ierr /= 0) CALL abort_physic('read_aviation_emissions', 'problem to allocate flight_h2o_mpi',1) |
|---|
| 317 | CALL xios_recv_field("KMFLOWN_interp", flight_dist_mpi(:,:,1)) |
|---|
| 318 | !CALL xios_recv_field("KGH2O_interp", flight_h2o_mpi(:,:,1)) |
|---|
| 319 | flight_h2o_mpi(:,:,:) = 0. |
|---|
| 320 | ! Get number of vertical levels and level values |
|---|
| 321 | CALL xios_get_axis_attr( "aviation_lev", value=aviation_lev(:)) |
|---|
| 322 | ENDIF |
|---|
| 323 | |
|---|
| 324 | ! Propagate to other OMP threads: flight_dist_mpi(klon_mpi,klev) to flight_dist(klon,klev) |
|---|
| 325 | CALL scatter_omp(flight_dist_mpi, flight_dist_read) |
|---|
| 326 | CALL scatter_omp(flight_h2o_mpi, flight_h2o_read) |
|---|
| 327 | CALL bcast_omp(aviation_lev) |
|---|
| 328 | |
|---|
| 329 | ELSE |
|---|
| 330 | |
|---|
| 331 | nbp_lon=nbp_lon_ |
|---|
| 332 | nbp_lat=nbp_lat_ |
|---|
| 333 | |
|---|
| 334 | IF (is_mpi_root) THEN |
|---|
| 335 | ALLOCATE(lon_src(nbp_lon)) |
|---|
| 336 | ALLOCATE(lat_src(nbp_lat)) |
|---|
| 337 | ALLOCATE(lat_src_inv(nbp_lat)) |
|---|
| 338 | ELSE |
|---|
| 339 | ALLOCATE(flight_dist_glo2D(0,0,0,0)) |
|---|
| 340 | ALLOCATE(flight_h2o_glo2D(0,0,0,0)) |
|---|
| 341 | ENDIF |
|---|
| 342 | |
|---|
| 343 | IF (is_mpi_root .AND. is_omp_root) THEN |
|---|
| 344 | |
|---|
| 345 | ! 1) Open file |
|---|
| 346 | !**************************************************************************************** |
|---|
| 347 | fname = 'aviation.nc' |
|---|
| 348 | CALL check_err( nf90_open(TRIM(fname), NF90_NOWRITE, nid), "pb open "//TRIM(fname) ) |
|---|
| 349 | |
|---|
| 350 | |
|---|
| 351 | ! Test for equal longitudes and latitudes in file and model |
|---|
| 352 | !**************************************************************************************** |
|---|
| 353 | ! Read and test longitudes |
|---|
| 354 | CALL check_err( nf90_inq_varid(nid, 'longitude', varid), "pb inq lon" ) |
|---|
| 355 | CALL check_err( nf90_get_var(nid, varid, lon_src(:)), "pb get lon" ) |
|---|
| 356 | |
|---|
| 357 | IF (maxval(ABS(lon_src - io_lon)) > 0.001) THEN |
|---|
| 358 | WRITE(lunout,*) 'Problem in longitudes read from file : ',TRIM(fname) |
|---|
| 359 | WRITE(lunout,*) 'longitudes in file ', TRIM(fname),' : ', lon_src |
|---|
| 360 | WRITE(lunout,*) 'longitudes in model :', io_lon |
|---|
| 361 | |
|---|
| 362 | CALL abort_physic('lmdz_aviation', 'longitudes are not the same in file and model',1) |
|---|
| 363 | END IF |
|---|
| 364 | |
|---|
| 365 | ! Read and test latitudes |
|---|
| 366 | CALL check_err( nf90_inq_varid(nid, 'latitude', varid),"pb inq lat" ) |
|---|
| 367 | CALL check_err( nf90_get_var(nid, varid, lat_src(:)),"pb get lat" ) |
|---|
| 368 | |
|---|
| 369 | ! Invert source latitudes |
|---|
| 370 | DO j = 1, nbp_lat |
|---|
| 371 | lat_src_inv(j) = lat_src(nbp_lat+1-j) |
|---|
| 372 | END DO |
|---|
| 373 | |
|---|
| 374 | IF (maxval(ABS(lat_src - io_lat)) < 0.001) THEN |
|---|
| 375 | ! Latitudes are the same |
|---|
| 376 | invert_lat=.FALSE. |
|---|
| 377 | ELSE IF (maxval(ABS(lat_src_inv - io_lat)) < 0.001) THEN |
|---|
| 378 | ! Inverted source latitudes correspond to model latitudes |
|---|
| 379 | WRITE(lunout,*) 'latitudes will be inverted for file : ',TRIM(fname) |
|---|
| 380 | invert_lat=.TRUE. |
|---|
| 381 | ELSE |
|---|
| 382 | WRITE(lunout,*) 'Problem in latitudes read from file : ',TRIM(fname) |
|---|
| 383 | WRITE(lunout,*) 'latitudes in file ', TRIM(fname),' : ', lat_src |
|---|
| 384 | WRITE(lunout,*) 'latitudes in model :', io_lat |
|---|
| 385 | CALL abort_physic('lmdz_aviation', 'latitudes do not correspond between file and model',1) |
|---|
| 386 | END IF |
|---|
| 387 | |
|---|
| 388 | |
|---|
| 389 | ! 2) Find vertical dimension nleva |
|---|
| 390 | !**************************************************************************************** |
|---|
| 391 | ierr = nf90_inq_dimid(nid, 'pressure_Pa', dimid) |
|---|
| 392 | CALL check_err( nf90_inquire_dimension(nid, dimid, len = nleva), "pb inq dim for PRESNIVS or lev" ) |
|---|
| 393 | |
|---|
| 394 | ! Allocate variables depending on the number of vertical levels |
|---|
| 395 | ALLOCATE(flight_dist_glo2D(nbp_lon, nbp_lat, nleva, 1), flight_h2o_glo2D(nbp_lon, nbp_lat, nleva, 1), stat=ierr) |
|---|
| 396 | IF (ierr /= 0) CALL abort_physic('lmdz_aviation', 'pb in allocation 1',1) |
|---|
| 397 | |
|---|
| 398 | ALLOCATE(aviation_lev(nleva), stat=ierr) |
|---|
| 399 | IF (ierr /= 0) CALL abort_physic('lmdz_aviation', 'pb in allocation 2',1) |
|---|
| 400 | |
|---|
| 401 | ! 3) Read all variables from file |
|---|
| 402 | !**************************************************************************************** |
|---|
| 403 | |
|---|
| 404 | ! Get variable id |
|---|
| 405 | CALL check_err( nf90_inq_varid(nid, 'seg_length_m', varid),"pb inq var seg_length_m" ) |
|---|
| 406 | ! Get the variable |
|---|
| 407 | CALL check_err( nf90_get_var(nid, varid, flight_dist_glo2D),"pb get var seg_length_m" ) |
|---|
| 408 | |
|---|
| 409 | ! ! Get variable id |
|---|
| 410 | ! CALL check_err( nf90_inq_varid(nid, 'fuel_burn', varid),"pb inq var fuel_burn" ) |
|---|
| 411 | ! ! Get the variable |
|---|
| 412 | ! CALL check_err( nf90_get_var(nid, varid, flight_h2o_glo2D),"pb get var fuel_burn" ) |
|---|
| 413 | flight_h2o_glo2D(:,:,:,:) = 0. |
|---|
| 414 | |
|---|
| 415 | ! Get variable id |
|---|
| 416 | CALL check_err( nf90_inq_varid(nid, "pressure_Pa", varid),"pb inq var pressure_Pa" ) |
|---|
| 417 | ! Get the variable |
|---|
| 418 | CALL check_err( nf90_get_var(nid, varid, aviation_lev),"pb get var pressure_Pa" ) |
|---|
| 419 | |
|---|
| 420 | |
|---|
| 421 | ! 4) Close file |
|---|
| 422 | !**************************************************************************************** |
|---|
| 423 | CALL check_err( nf90_close(nid), "pb in close" ) |
|---|
| 424 | |
|---|
| 425 | |
|---|
| 426 | ! 5) Transform the global field from 2D(nbp_lon,nbp_lat) to 1D(klon_glo) |
|---|
| 427 | !**************************************************************************************** |
|---|
| 428 | ! Test if vertical levels have to be inversed |
|---|
| 429 | |
|---|
| 430 | IF (aviation_lev(1) < aviation_lev(nleva)) THEN |
|---|
| 431 | |
|---|
| 432 | ALLOCATE(vartmp(nbp_lon, nbp_lat, nleva), vartmp_lev(nleva)) |
|---|
| 433 | |
|---|
| 434 | ! Inverse vertical levels for flight_dist_glo2D |
|---|
| 435 | vartmp(:,:,:) = flight_dist_glo2D(:,:,:,1) |
|---|
| 436 | DO k=1, nleva |
|---|
| 437 | DO j=1, nbp_lat |
|---|
| 438 | DO i=1, nbp_lon |
|---|
| 439 | flight_dist_glo2D(i,j,k,1) = vartmp(i,j,nleva+1-k) |
|---|
| 440 | END DO |
|---|
| 441 | END DO |
|---|
| 442 | END DO |
|---|
| 443 | |
|---|
| 444 | ! Inverse vertical levels for flight_h2o_glo2D |
|---|
| 445 | vartmp(:,:,:) = flight_h2o_glo2D(:,:,:,1) |
|---|
| 446 | DO k=1, nleva |
|---|
| 447 | DO j=1, nbp_lat |
|---|
| 448 | DO i=1, nbp_lon |
|---|
| 449 | flight_h2o_glo2D(i,j,k,1) = vartmp(i,j,nleva+1-k) |
|---|
| 450 | END DO |
|---|
| 451 | END DO |
|---|
| 452 | END DO |
|---|
| 453 | |
|---|
| 454 | ALLOCATE(vartmp_lev(nleva)) |
|---|
| 455 | ! Inverte vertical axes for aviation_lev |
|---|
| 456 | vartmp_lev(:) = aviation_lev(:) |
|---|
| 457 | DO k=1, nleva |
|---|
| 458 | aviation_lev(k) = vartmp_lev(nleva+1-k) |
|---|
| 459 | END DO |
|---|
| 460 | |
|---|
| 461 | DEALLOCATE(vartmp, vartmp_lev) |
|---|
| 462 | |
|---|
| 463 | ELSE |
|---|
| 464 | WRITE(lunout,*) 'Vertical axis in file ',TRIM(fname), ' is ok, no vertical inversion is done' |
|---|
| 465 | END IF |
|---|
| 466 | |
|---|
| 467 | |
|---|
| 468 | ! - Invert latitudes if necessary |
|---|
| 469 | IF (invert_lat) THEN |
|---|
| 470 | |
|---|
| 471 | ALLOCATE(vartmp(nbp_lon, nbp_lat, nleva)) |
|---|
| 472 | |
|---|
| 473 | ! Invert latitudes for the variable |
|---|
| 474 | vartmp(:,:,:) = flight_dist_glo2D(:,:,:,1) ! use varmth temporarly |
|---|
| 475 | DO k=1,nleva |
|---|
| 476 | DO j=1,nbp_lat |
|---|
| 477 | DO i=1,nbp_lon |
|---|
| 478 | flight_dist_glo2D(i,j,k,1) = vartmp(i,nbp_lat+1-j,k) |
|---|
| 479 | END DO |
|---|
| 480 | END DO |
|---|
| 481 | END DO |
|---|
| 482 | |
|---|
| 483 | ! Invert latitudes for the variable |
|---|
| 484 | vartmp(:,:,:) = flight_h2o_glo2D(:,:,:,1) ! use varmth temporarly |
|---|
| 485 | DO k=1,nleva |
|---|
| 486 | DO j=1,nbp_lat |
|---|
| 487 | DO i=1,nbp_lon |
|---|
| 488 | flight_h2o_glo2D(i,j,k,1) = vartmp(i,nbp_lat+1-j,k) |
|---|
| 489 | END DO |
|---|
| 490 | END DO |
|---|
| 491 | END DO |
|---|
| 492 | |
|---|
| 493 | DEALLOCATE(vartmp) |
|---|
| 494 | END IF ! invert_lat |
|---|
| 495 | |
|---|
| 496 | ! Do zonal mead at poles and distribut at whole first and last latitude |
|---|
| 497 | DO k=1, nleva |
|---|
| 498 | npole=0. ! North pole, j=1 |
|---|
| 499 | spole=0. ! South pole, j=nbp_lat |
|---|
| 500 | DO i=1,nbp_lon |
|---|
| 501 | npole = npole + flight_dist_glo2D(i,1,k,1) |
|---|
| 502 | spole = spole + flight_dist_glo2D(i,nbp_lat,k,1) |
|---|
| 503 | END DO |
|---|
| 504 | npole = npole/REAL(nbp_lon) |
|---|
| 505 | spole = spole/REAL(nbp_lon) |
|---|
| 506 | flight_dist_glo2D(:,1, k,1) = npole |
|---|
| 507 | flight_dist_glo2D(:,nbp_lat,k,1) = spole |
|---|
| 508 | END DO |
|---|
| 509 | |
|---|
| 510 | ! Do zonal mead at poles and distribut at whole first and last latitude |
|---|
| 511 | DO k=1, nleva |
|---|
| 512 | npole=0. ! North pole, j=1 |
|---|
| 513 | spole=0. ! South pole, j=nbp_lat |
|---|
| 514 | DO i=1,nbp_lon |
|---|
| 515 | npole = npole + flight_h2o_glo2D(i,1,k,1) |
|---|
| 516 | spole = spole + flight_h2o_glo2D(i,nbp_lat,k,1) |
|---|
| 517 | END DO |
|---|
| 518 | npole = npole/REAL(nbp_lon) |
|---|
| 519 | spole = spole/REAL(nbp_lon) |
|---|
| 520 | flight_h2o_glo2D(:,1, k,1) = npole |
|---|
| 521 | flight_h2o_glo2D(:,nbp_lat,k,1) = spole |
|---|
| 522 | END DO |
|---|
| 523 | |
|---|
| 524 | ALLOCATE(flight_dist_mpi(klon_glo, nleva, 1), flight_h2o_mpi(klon_glo, nleva, 1), stat=ierr) |
|---|
| 525 | IF (ierr /= 0) CALL abort_physic('lmdz_aviation', 'pb in allocation 3',1) |
|---|
| 526 | |
|---|
| 527 | ! Transform from 2D to 1D field |
|---|
| 528 | CALL grid2Dto1D_glo(flight_dist_glo2D, flight_dist_mpi) |
|---|
| 529 | CALL grid2Dto1D_glo(flight_h2o_glo2D, flight_h2o_mpi) |
|---|
| 530 | |
|---|
| 531 | ELSE |
|---|
| 532 | ALLOCATE(flight_dist_mpi(0,0,0), flight_h2o_mpi(0,0,0)) |
|---|
| 533 | END IF ! is_mpi_root .AND. is_omp_root |
|---|
| 534 | |
|---|
| 535 | !$OMP BARRIER |
|---|
| 536 | |
|---|
| 537 | ! 6) Distribute to all processes |
|---|
| 538 | ! Scatter global field(klon_glo) to local process domain(klon) |
|---|
| 539 | ! and distribute nleva to all processes |
|---|
| 540 | !**************************************************************************************** |
|---|
| 541 | |
|---|
| 542 | ! Distribute nleva |
|---|
| 543 | CALL bcast(nleva) |
|---|
| 544 | |
|---|
| 545 | ! Allocate and distribute pt_ap and pt_b |
|---|
| 546 | IF (.NOT. ALLOCATED(aviation_lev)) THEN ! if pt_ap is allocated also pt_b is allocated |
|---|
| 547 | ALLOCATE(aviation_lev(nleva), stat=ierr) |
|---|
| 548 | IF (ierr /= 0) CALL abort_physic('lmdz_aviation', 'pb in allocation 4',1) |
|---|
| 549 | END IF |
|---|
| 550 | CALL bcast(aviation_lev) |
|---|
| 551 | |
|---|
| 552 | ! Allocate space for output pointer variable at local process |
|---|
| 553 | ALLOCATE(flight_dist_read(klon, nleva, 1), flight_h2o_read(klon, nleva, 1), stat=ierr) |
|---|
| 554 | IF (ierr /= 0) CALL abort_physic('lmdz_aviation', 'pb in allocation 5',1) |
|---|
| 555 | |
|---|
| 556 | ! Scatter global field to local domain at local process |
|---|
| 557 | CALL scatter(flight_dist_mpi, flight_dist_read) |
|---|
| 558 | CALL scatter(flight_h2o_mpi, flight_h2o_read) |
|---|
| 559 | |
|---|
| 560 | ENDIF |
|---|
| 561 | |
|---|
| 562 | END SUBROUTINE read_aviation_emissions |
|---|
| 563 | |
|---|
| 564 | SUBROUTINE vertical_interpolation_aviation(klon, klev, paprs, pplay, temp, flight_dist, flight_h2o) |
|---|
| 565 | ! This subroutine performs the vertical interpolation from the read data in aviation.nc |
|---|
| 566 | ! where there are nleva vertical levels described in aviation_lev to the klev levels or |
|---|
| 567 | ! the model. |
|---|
| 568 | ! flight_dist_read(klon,nleva) -> flight_dist(klon, klev) |
|---|
| 569 | ! flight_h2o_read(klon,nleva) -> flight_h2o(klon, klev) |
|---|
| 570 | |
|---|
| 571 | USE lmdz_lscp_ini, ONLY: RD, RG |
|---|
| 572 | USE lmdz_lscp_ini, ONLY: aviation_coef |
|---|
| 573 | |
|---|
| 574 | IMPLICIT NONE |
|---|
| 575 | |
|---|
| 576 | INTEGER, INTENT(IN) :: klon, klev ! number of horizontal grid points and vertical levels |
|---|
| 577 | REAL, INTENT(IN) :: paprs(klon, klev+1) ! inter-layer pressure [Pa] |
|---|
| 578 | REAL, INTENT(IN) :: pplay(klon, klev) ! mid-layer pressure [Pa] |
|---|
| 579 | REAL, INTENT(IN) :: temp(klon, klev) ! temperature [K] |
|---|
| 580 | REAL, INTENT(OUT) :: flight_dist(klon,klev) ! Aviation distance flown within the mesh [m/s/mesh] |
|---|
| 581 | REAL, INTENT(OUT) :: flight_h2o(klon,klev) ! Aviation H2O emitted within the mesh [kgH2O/s/mesh] |
|---|
| 582 | |
|---|
| 583 | !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! |
|---|
| 584 | ! Local variable |
|---|
| 585 | !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! |
|---|
| 586 | REAL :: aviation_interface(1:nleva+1) ! Pressure of aviation file interfaces [ Pa ] |
|---|
| 587 | INTEGER :: k, kori ! Loop index for vertical layers |
|---|
| 588 | INTEGER :: i ! Loop index for horizontal grid |
|---|
| 589 | REAL :: zfrac ! Fraction of layer kori in layer k |
|---|
| 590 | REAL :: width_read_layer(1:nleva) ! width of a given layer [ Pa ] |
|---|
| 591 | REAL :: rho, rhodz, dz |
|---|
| 592 | |
|---|
| 593 | ! Initialisation |
|---|
| 594 | flight_dist(:,:) = 0. |
|---|
| 595 | flight_h2o(:,:) = 0. |
|---|
| 596 | |
|---|
| 597 | ! Compute the array with the vertical interface |
|---|
| 598 | ! It starts at 1 and has length nleva + 1 |
|---|
| 599 | ! Note that aviation_lev has nleva and gives the altitude in the middle of the layers |
|---|
| 600 | ! Surface pressure in standard atmosphere model [ Pa ] |
|---|
| 601 | aviation_interface(1) = 101325. |
|---|
| 602 | DO kori=2, nleva |
|---|
| 603 | aviation_interface(kori) = (aviation_lev(kori-1)+aviation_lev(kori))/2.0 ! [ Pa ] |
|---|
| 604 | ENDDO |
|---|
| 605 | ! Last interface - we assume the same spacing as the very last one |
|---|
| 606 | aviation_interface(nleva+1) = aviation_interface(nleva) - (aviation_lev(nleva-1) - aviation_lev(nleva)) |
|---|
| 607 | |
|---|
| 608 | ! Vertical width of each layer of the read file |
|---|
| 609 | ! It is positive |
|---|
| 610 | DO kori=1, nleva |
|---|
| 611 | width_read_layer(kori) = aviation_interface(kori) - aviation_interface(kori+1) |
|---|
| 612 | ENDDO |
|---|
| 613 | |
|---|
| 614 | ! Vertical reprojection |
|---|
| 615 | ! The loop over klon is induced since it is done by MPI threads |
|---|
| 616 | ! zfrac is the fraction of layer kori (read file) included in layer k (model) |
|---|
| 617 | DO i=1,klon |
|---|
| 618 | DO k=1, klev |
|---|
| 619 | DO kori=1,nleva |
|---|
| 620 | ! Which of the lower interfaces is the highest (<=> the lowest pressure) ? |
|---|
| 621 | zfrac = min(paprs(i,k), aviation_interface(kori)) |
|---|
| 622 | ! Which of the upper interfaces is the lowest (<=> the greatest pressure) ? |
|---|
| 623 | zfrac = zfrac - max(paprs(i,k+1), aviation_interface(kori+1)) |
|---|
| 624 | ! If zfrac is negative, the layers are not overlapping |
|---|
| 625 | ! Otherwise, we get the fraction of layer kori that overlap with layer k |
|---|
| 626 | ! after normalisation to the total kori layer width |
|---|
| 627 | zfrac = max(0.0, zfrac) / width_read_layer(kori) |
|---|
| 628 | |
|---|
| 629 | ! Vertical reprojection for each desired array |
|---|
| 630 | flight_dist(i,k) = flight_dist(i,k) + zfrac * flight_dist_read(i,kori,1) |
|---|
| 631 | flight_h2o(i,k) = flight_h2o(i,k) + zfrac * flight_h2o_read(i,kori,1) |
|---|
| 632 | ENDDO |
|---|
| 633 | |
|---|
| 634 | !--Dry density [kg/m3] |
|---|
| 635 | rho = pplay(i,k) / temp(i,k) / RD |
|---|
| 636 | !--Dry air mass [kg/m2] |
|---|
| 637 | rhodz = ( paprs(i,k) - paprs(i,k+1) ) / RG |
|---|
| 638 | !--Cell thickness [m] |
|---|
| 639 | dz = rhodz / rho |
|---|
| 640 | |
|---|
| 641 | !--Normalisation with the cell thickness |
|---|
| 642 | flight_dist(i,k) = flight_dist(i,k) / dz |
|---|
| 643 | flight_h2o(i,k) = flight_h2o(i,k) / dz |
|---|
| 644 | |
|---|
| 645 | !--Enhancement factor |
|---|
| 646 | flight_dist(i,k) = flight_dist(i,k) * aviation_coef |
|---|
| 647 | flight_h2o(i,k) = flight_h2o(i,k) * aviation_coef |
|---|
| 648 | ENDDO |
|---|
| 649 | ENDDO |
|---|
| 650 | |
|---|
| 651 | END SUBROUTINE vertical_interpolation_aviation |
|---|
| 652 | |
|---|
| 653 | END MODULE lmdz_aviation |
|---|