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 per m2 |
---|
12 | ! flight_fuel_read is the fuel consumed per second per m2 |
---|
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/m2/vertmesh] |
---|
17 | !$OMP THREADPRIVATE(flight_dist_read) |
---|
18 | REAL, ALLOCATABLE, DIMENSION(:,:,:), SAVE, PRIVATE :: flight_fuel_read ! Aviation fuel consumed within the mesh [kg/s/m2/vertmesh] |
---|
19 | !$OMP THREADPRIVATE(flight_fuel_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_fuel, d_q_avi & |
---|
28 | ) |
---|
29 | |
---|
30 | USE lmdz_lscp_ini, ONLY: RD, EI_H2O_aviation |
---|
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_fuel ! aviation fuel consumption concentration [kg/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, flight_h2o |
---|
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 | flight_h2o = flight_fuel(i,k) * EI_H2O_aviation |
---|
50 | |
---|
51 | !--q is the specific humidity (kg/kg humid air) hence the complicated equation to update q |
---|
52 | ! qnew = ( m_humid_air * qold + dm_H2O ) / ( m_humid_air + dm_H2O ) |
---|
53 | ! = ( m_dry_air * qold + dm_h2O * (1-qold) ) / (m_dry_air + dm_H2O * (1-qold) ) |
---|
54 | !--The equation is derived by writing m_humid_air = m_dry_air + m_H2O = m_dry_air / (1-q) |
---|
55 | !--flight_h2O is in kg H2O / s / m3 |
---|
56 | |
---|
57 | !d_q_avi(i,k) = ( M_cell * qtot(i,k) + V_cell * flight_h2o * dtime * ( 1. - qtot(i,k) ) ) & |
---|
58 | ! / ( M_cell + V_cell * flight_h2o * dtime * ( 1. - qtot(i,k) ) ) & |
---|
59 | ! - qtot(i,k) |
---|
60 | !--NB., M_cell = V_cell * rho |
---|
61 | !d_q_avi(i,k) = ( rho * qtot(i,k) + flight_h2o * dtime * ( 1. - qtot(i,k) ) ) & |
---|
62 | ! / ( rho + flight_h2o * dtime * ( 1. - qtot(i,k) ) ) & |
---|
63 | ! - qtot(i,k) |
---|
64 | !--Same formula, more computationally effective but less readable |
---|
65 | d_q_avi(i,k) = flight_h2o * ( 1. - qtot(i,k) ) & |
---|
66 | / ( rho / dtime / ( 1. - qtot(i,k) ) + flight_h2o ) |
---|
67 | ENDDO |
---|
68 | ENDDO |
---|
69 | |
---|
70 | END SUBROUTINE aviation_water_emissions |
---|
71 | |
---|
72 | |
---|
73 | !********************************************************************************** |
---|
74 | SUBROUTINE contrails_formation( & |
---|
75 | klon, dtime, pplay, temp, rho, qsat, qsatl, gamma_cond, flight_dist, flight_fuel, & |
---|
76 | clrfra, qclr, pdf_scale, pdf_alpha, pdf_gamma, keepgoing, pt_pron_clds, & |
---|
77 | Tcritcont, qcritcont, potcontfraP, potcontfraNP, & |
---|
78 | AEI_contrails, AEI_surv_contrails, fsurv_contrails, section_contrails, & |
---|
79 | dcfc_ini, dqic_ini, dqtc_ini, dnic_ini) |
---|
80 | |
---|
81 | USE lmdz_lscp_ini, ONLY: RPI, RCPD, EPS_W, RTT |
---|
82 | USE lmdz_lscp_ini, ONLY: EI_H2O_aviation, qheat_fuel_aviation, prop_efficiency_aviation |
---|
83 | USE lmdz_lscp_ini, ONLY: eps, temp_nowater |
---|
84 | USE lmdz_lscp_ini, ONLY: Nice_init_contrails |
---|
85 | |
---|
86 | USE lmdz_lscp_tools, ONLY: GAMMAINC, calc_qsat_ecmwf |
---|
87 | |
---|
88 | IMPLICIT NONE |
---|
89 | |
---|
90 | ! |
---|
91 | ! Input |
---|
92 | ! |
---|
93 | INTEGER, INTENT(IN) :: klon ! number of horizontal grid points |
---|
94 | REAL, INTENT(IN) :: dtime ! time step [s] |
---|
95 | REAL, INTENT(IN) , DIMENSION(klon) :: pplay ! layer pressure [Pa] |
---|
96 | REAL, INTENT(IN) , DIMENSION(klon) :: temp ! temperature [K] |
---|
97 | REAL, INTENT(IN) , DIMENSION(klon) :: rho ! air density [kg/m3] |
---|
98 | REAL, INTENT(IN) , DIMENSION(klon) :: qsat ! saturation specific humidity [kg/kg] |
---|
99 | REAL, INTENT(IN) , DIMENSION(klon) :: qsatl ! saturation specific humidity w.r.t. liquid [kg/kg] |
---|
100 | REAL, INTENT(IN) , DIMENSION(klon) :: gamma_cond ! condensation threshold w.r.t. qsat [-] |
---|
101 | REAL, INTENT(IN) , DIMENSION(klon) :: flight_dist ! aviation distance flown concentration [m/s/m3] |
---|
102 | REAL, INTENT(IN) , DIMENSION(klon) :: flight_fuel ! aviation fuel consumption concentration [kg/s/m3] |
---|
103 | REAL, INTENT(IN) , DIMENSION(klon) :: clrfra ! clear sky fraction [-] |
---|
104 | REAL, INTENT(IN) , DIMENSION(klon) :: qclr ! clear sky specific humidity [kg/kg] |
---|
105 | REAL, INTENT(IN) , DIMENSION(klon) :: pdf_scale ! scale parameter of the clear sky PDF [%] |
---|
106 | REAL, INTENT(IN) , DIMENSION(klon) :: pdf_alpha ! shape parameter of the clear sky PDF [-] |
---|
107 | REAL, INTENT(IN) , DIMENSION(klon) :: pdf_gamma ! tmp parameter of the clear sky PDF [-] |
---|
108 | LOGICAL, INTENT(IN) , DIMENSION(klon) :: keepgoing ! .TRUE. if a new condensation loop should be computed |
---|
109 | LOGICAL, INTENT(IN) , DIMENSION(klon) :: pt_pron_clds ! .TRUE. if clouds are prognostic in this mesh |
---|
110 | ! |
---|
111 | ! Output |
---|
112 | ! |
---|
113 | REAL, INTENT(INOUT), DIMENSION(klon) :: Tcritcont ! critical temperature for contrail formation [K] |
---|
114 | REAL, INTENT(INOUT), DIMENSION(klon) :: qcritcont ! critical specific humidity for contrail formation [kg/kg] |
---|
115 | REAL, INTENT(INOUT), DIMENSION(klon) :: potcontfraP ! potential persistent contrail fraction [-] |
---|
116 | REAL, INTENT(INOUT), DIMENSION(klon) :: potcontfraNP ! potential non-persistent contrail fraction [-] |
---|
117 | REAL, INTENT(INOUT), DIMENSION(klon) :: AEI_contrails ! Apparent emission index contrails [#/kg] |
---|
118 | REAL, INTENT(INOUT), DIMENSION(klon) :: AEI_surv_contrails ! Apparent emission index contrails after vortex loss [#/kg] |
---|
119 | REAL, INTENT(INOUT), DIMENSION(klon) :: fsurv_contrails ! Survival fraction after vortex loss [-] |
---|
120 | REAL, INTENT(INOUT), DIMENSION(klon) :: section_contrails ! Cross section of newly formed contrails [m2] |
---|
121 | REAL, INTENT(INOUT), DIMENSION(klon) :: dcfc_ini ! contrails cloud fraction tendency bec ause of initial formation [s-1] |
---|
122 | REAL, INTENT(INOUT), DIMENSION(klon) :: dqic_ini ! contrails ice specific humidity tende ncy because of initial formation [kg/kg/s] |
---|
123 | REAL, INTENT(INOUT), DIMENSION(klon) :: dqtc_ini ! contrails total specific humidity ten dency because of initial formation [kg/kg/s] |
---|
124 | REAL, INTENT(INOUT), DIMENSION(klon) :: dnic_ini ! contrails ice crystals concentration ten dency because of initial formation [#/kg/s] |
---|
125 | ! |
---|
126 | ! Local |
---|
127 | ! |
---|
128 | INTEGER :: i |
---|
129 | ! for Schmidt-Appleman criteria |
---|
130 | REAL, DIMENSION(klon) :: qzero |
---|
131 | REAL, DIMENSION(klon) :: qsatl_crit, dqsatl_crit |
---|
132 | REAL :: Gcont, psatl_crit, pcrit |
---|
133 | REAL :: rhl_clr, pdf_loc |
---|
134 | REAL :: pdf_x, pdf_y, pdf_e3 |
---|
135 | REAL :: pdf_fra_above_qcritcont, pdf_fra_above_qsat |
---|
136 | REAL :: pdf_q_above_qcritcont, pdf_q_above_qsat |
---|
137 | REAL :: qpotcontP |
---|
138 | ! |
---|
139 | ! for new contrail formation |
---|
140 | REAL :: dist_contrails, Nice_per_m_init_contrails |
---|
141 | REAL :: icesat_ratio |
---|
142 | |
---|
143 | qzero(:) = 0. |
---|
144 | |
---|
145 | DO i = 1, klon |
---|
146 | IF ( keepgoing(i) ) THEN |
---|
147 | qcritcont(i) = 0. |
---|
148 | potcontfraP(i) = 0. |
---|
149 | potcontfraNP(i) = 0. |
---|
150 | ENDIF |
---|
151 | ENDDO |
---|
152 | |
---|
153 | !--------------------------------- |
---|
154 | !-- SCHMIDT-APPLEMAN CRITERIA -- |
---|
155 | !--------------------------------- |
---|
156 | !--Revised by Schumann (1996) and Rap et al. (2010) |
---|
157 | |
---|
158 | DO i = 1, klon |
---|
159 | !--Gcont is the slope of the mean phase trajectory in the turbulent exhaust field on an absolute |
---|
160 | !--in Pa / K. See Rap et al. (2010) in JGR. |
---|
161 | !--kg H2O/kg fuel * J kg air-1 K-1 * Pa / (kg H2O / kg air * J kg fuel-1) |
---|
162 | Gcont = EI_H2O_aviation * RCPD * pplay(i) & |
---|
163 | / ( EPS_W * qheat_fuel_aviation * ( 1. - prop_efficiency_aviation ) ) |
---|
164 | !--critical temperature below which no liquid contrail can form in exhaust |
---|
165 | !--noted T_LM in Schumann (1996), their Eq. 31 in Appendix 2 |
---|
166 | !--in Kelvins |
---|
167 | Tcritcont(i) = 226.69 + 9.43 * LOG( MAX(Gcont, 0.1) - 0.053 ) & |
---|
168 | + 0.72 * LOG( MAX(Gcont, 0.1) - 0.053 )**2 |
---|
169 | ENDDO |
---|
170 | |
---|
171 | CALL calc_qsat_ecmwf(klon, Tcritcont, qzero, pplay, RTT, 1, .FALSE., qsatl_crit, dqsatl_crit) |
---|
172 | |
---|
173 | DO i = 1, klon |
---|
174 | IF ( keepgoing(i) .AND. pt_pron_clds(i) ) THEN |
---|
175 | |
---|
176 | psatl_crit = qsatl_crit(i) / ( EPS_W + ( 1. - EPS_W ) * qsatl_crit(i) ) * pplay(i) |
---|
177 | pcrit = Gcont * ( temp(i) - Tcritcont(i) ) + psatl_crit |
---|
178 | qcritcont(i) = EPS_W * pcrit / ( pplay(i) - ( 1. - EPS_W ) * pcrit ) |
---|
179 | |
---|
180 | |
---|
181 | IF ( ( clrfra(i) .GT. eps ) .AND. ( temp(i) .LT. Tcritcont(i) ) ) THEN |
---|
182 | |
---|
183 | rhl_clr = qclr(i) / clrfra(i) / qsatl(i) * 100. |
---|
184 | pdf_loc = rhl_clr - pdf_scale(i) * pdf_gamma(i) |
---|
185 | pdf_x = qcritcont(i) / qsatl(i) * 100. |
---|
186 | pdf_y = LOG( MAX( ( pdf_x - pdf_loc ) / pdf_scale(i), eps) ) * pdf_alpha(i) |
---|
187 | IF ( pdf_y .GT. 10. ) THEN !--Avoid overflows |
---|
188 | pdf_fra_above_qcritcont = 0. |
---|
189 | pdf_q_above_qcritcont = 0. |
---|
190 | ELSEIF ( pdf_y .LT. -10. ) THEN |
---|
191 | pdf_fra_above_qcritcont = clrfra(i) |
---|
192 | pdf_q_above_qcritcont = qclr(i) |
---|
193 | ELSE |
---|
194 | pdf_y = EXP( pdf_y ) |
---|
195 | pdf_e3 = GAMMAINC ( 1. + 1. / pdf_alpha(i) , pdf_y ) |
---|
196 | pdf_e3 = pdf_scale(i) * ( 1. - pdf_e3 ) * pdf_gamma(i) |
---|
197 | pdf_fra_above_qcritcont = EXP( - pdf_y ) * clrfra(i) |
---|
198 | pdf_q_above_qcritcont = ( pdf_e3 * clrfra(i) & |
---|
199 | + pdf_loc * pdf_fra_above_qcritcont ) * qsatl(i) / 100. |
---|
200 | ENDIF |
---|
201 | |
---|
202 | pdf_x = qsat(i) / qsatl(i) * 100. |
---|
203 | pdf_y = LOG( MAX( ( pdf_x - pdf_loc ) / pdf_scale(i), eps) ) * pdf_alpha(i) |
---|
204 | IF ( pdf_y .GT. 10. ) THEN !--Avoid overflows |
---|
205 | pdf_fra_above_qsat = 0. |
---|
206 | pdf_q_above_qsat = 0. |
---|
207 | ELSEIF ( pdf_y .LT. -10. ) THEN |
---|
208 | pdf_fra_above_qsat = clrfra(i) |
---|
209 | pdf_q_above_qsat = qclr(i) |
---|
210 | ELSE |
---|
211 | pdf_y = EXP( pdf_y ) |
---|
212 | pdf_e3 = GAMMAINC ( 1. + 1. / pdf_alpha(i) , pdf_y ) |
---|
213 | pdf_e3 = pdf_scale(i) * ( 1. - pdf_e3 ) * pdf_gamma(i) |
---|
214 | pdf_fra_above_qsat = EXP( - pdf_y ) * clrfra(i) |
---|
215 | pdf_q_above_qsat = ( pdf_e3 * clrfra(i) & |
---|
216 | + pdf_loc * pdf_fra_above_qsat ) * qsatl(i) / 100. |
---|
217 | ENDIF |
---|
218 | |
---|
219 | potcontfraNP(i) = MAX(0., pdf_fra_above_qcritcont - pdf_fra_above_qsat) |
---|
220 | potcontfraP(i) = MIN(pdf_fra_above_qsat, pdf_fra_above_qcritcont) |
---|
221 | qpotcontP = MIN(pdf_q_above_qsat, pdf_q_above_qcritcont) |
---|
222 | |
---|
223 | ENDIF ! temp .LT. Tcritcont |
---|
224 | |
---|
225 | !--Add a source of contrails from aviation |
---|
226 | IF ( ( potcontfraP(i) .GT. eps ) .AND. ( flight_dist(i) .GT. 1e-20 ) ) THEN |
---|
227 | !section_contrails(i) = CONTRAIL_CROSS_SECTION_ONERA() |
---|
228 | section_contrails(i) = CONTRAIL_CROSS_SECTION_SCHUMANN( & |
---|
229 | dtime, rho(i), flight_dist(i), flight_fuel(i)) |
---|
230 | icesat_ratio = qpotcontP / potcontfraP(i) / qsat(i) |
---|
231 | !--If Nice init is fixed in the plume |
---|
232 | !Nice_per_m_init_contrails = Nice_init_contrails * 1e6 * section_contrails(i) |
---|
233 | !--Else if it is parameterized |
---|
234 | CALL CONTRAIL_ICE_NUMBER_CONCENTRATION(temp(i), icesat_ratio, rho(i), & |
---|
235 | flight_dist(i), flight_fuel(i), Nice_per_m_init_contrails, & |
---|
236 | AEI_contrails(i), AEI_surv_contrails(i), fsurv_contrails(i)) |
---|
237 | |
---|
238 | !--If Nice_per_m_init_contrails .EQ. 0., all the crystals were lost in the vortex |
---|
239 | IF ( Nice_per_m_init_contrails .GT. 0. ) THEN |
---|
240 | !--dist_contrails is in meters of contrails per m3 (gridbox-average) |
---|
241 | dist_contrails = flight_dist(i) * dtime * potcontfraP(i) |
---|
242 | dcfc_ini(i) = dist_contrails * section_contrails(i) |
---|
243 | dqtc_ini(i) = icesat_ratio * qsat(i) * dcfc_ini(i) |
---|
244 | dqic_ini(i) = dqtc_ini(i) - qsat(i) * dcfc_ini(i) |
---|
245 | dnic_ini(i) = Nice_per_m_init_contrails * dist_contrails / rho(i) |
---|
246 | ENDIF |
---|
247 | ENDIF |
---|
248 | |
---|
249 | ENDIF ! keepgoing |
---|
250 | ENDDO |
---|
251 | |
---|
252 | END SUBROUTINE contrails_formation |
---|
253 | |
---|
254 | |
---|
255 | !********************************************************************************** |
---|
256 | FUNCTION CONTRAIL_CROSS_SECTION_ONERA() |
---|
257 | |
---|
258 | USE lmdz_lscp_ini, ONLY: initial_width_contrails, initial_height_contrails |
---|
259 | |
---|
260 | IMPLICIT NONE |
---|
261 | |
---|
262 | ! |
---|
263 | ! Output |
---|
264 | ! |
---|
265 | REAL :: contrail_cross_section_onera ! [m2] |
---|
266 | ! |
---|
267 | ! Local |
---|
268 | ! |
---|
269 | |
---|
270 | contrail_cross_section_onera = initial_width_contrails * initial_height_contrails |
---|
271 | |
---|
272 | END FUNCTION CONTRAIL_CROSS_SECTION_ONERA |
---|
273 | |
---|
274 | |
---|
275 | !********************************************************************************** |
---|
276 | !--Based on Schumann (1998) |
---|
277 | FUNCTION CONTRAIL_CROSS_SECTION_SCHUMANN(dtime, rho_air, flight_dist, flight_fuel) |
---|
278 | |
---|
279 | USE lmdz_lscp_ini, ONLY: initial_width_contrails, initial_height_contrails |
---|
280 | |
---|
281 | IMPLICIT NONE |
---|
282 | |
---|
283 | ! |
---|
284 | ! Input |
---|
285 | ! |
---|
286 | REAL :: dtime ! timestep [s] |
---|
287 | REAL :: rho_air ! air density [kg/m3] |
---|
288 | REAL :: flight_dist ! flown distance [m/s/m3] |
---|
289 | REAL :: flight_fuel ! fuel consumed [kg/s/m3] |
---|
290 | ! |
---|
291 | ! Output |
---|
292 | ! |
---|
293 | REAL :: contrail_cross_section_schumann ! [m2] |
---|
294 | ! |
---|
295 | ! Local |
---|
296 | ! |
---|
297 | REAL :: dilution_factor |
---|
298 | |
---|
299 | !--The contrail is formed on average in the middle of the timestep |
---|
300 | dilution_factor = 7000. * (dtime / 2.)**0.8 |
---|
301 | contrail_cross_section_schumann = flight_fuel / flight_dist * dilution_factor / rho_air |
---|
302 | |
---|
303 | END FUNCTION CONTRAIL_CROSS_SECTION_SCHUMANN |
---|
304 | |
---|
305 | |
---|
306 | !********************************************************************************** |
---|
307 | SUBROUTINE CONTRAIL_ICE_NUMBER_CONCENTRATION(temp, icesat_ratio, rho_air, & |
---|
308 | flight_dist, flight_fuel, Nice_per_m_init_contrails, & |
---|
309 | AEI_contrails, AEI_surv_contrails, fsurv_contrails) |
---|
310 | |
---|
311 | USE lmdz_lscp_ini, ONLY: RPI |
---|
312 | USE lmdz_lscp_ini, ONLY: EI_soot_aviation, air_to_fuel_ratio_engine, wingspan |
---|
313 | USE lmdz_lscp_ini, ONLY: Naer_amb, raer_amb_mean, raer_amb_std, r_soot_mean, r_soot_std |
---|
314 | USE lmdz_lscp_ini, ONLY: circ_0_loss, plume_area_loss, nice_init_ref_loss |
---|
315 | USE lmdz_lscp_ini, ONLY: N_Brunt_Vaisala_aviation, EI_H2O_aviation |
---|
316 | |
---|
317 | IMPLICIT NONE |
---|
318 | |
---|
319 | ! |
---|
320 | ! Input |
---|
321 | ! |
---|
322 | REAL, INTENT(IN) :: temp ! air temperature [K] |
---|
323 | REAL, INTENT(IN) :: icesat_ratio ! ice saturation ratio [-] |
---|
324 | REAL, INTENT(IN) :: rho_air ! air density [kg/m3] |
---|
325 | REAL, INTENT(IN) :: flight_dist ! flown distance [m/s/m3] |
---|
326 | REAL, INTENT(IN) :: flight_fuel ! fuel consumed [kg/s/m3] |
---|
327 | ! |
---|
328 | ! Output |
---|
329 | ! |
---|
330 | REAL, INTENT(OUT) :: Nice_per_m_init_contrails ! [#/m] |
---|
331 | REAL, INTENT(OUT) :: AEI_contrails ! [#/kg] |
---|
332 | REAL, INTENT(OUT) :: AEI_surv_contrails ! [#/kg] |
---|
333 | REAL, INTENT(OUT) :: fsurv_contrails ! [-] |
---|
334 | ! |
---|
335 | ! Local |
---|
336 | ! |
---|
337 | ! For initial ice nucleation |
---|
338 | REAL :: log_liqsat_ratio, coef_expo, dil_coef |
---|
339 | REAL :: rd_act_amb, rd_act_soot, phi_amb, phi_soot |
---|
340 | REAL :: AEI_soot, AEI_amb |
---|
341 | ! For ice crystals loss |
---|
342 | REAL :: rho_emit, temp_205, nice_init |
---|
343 | REAL :: z_atm, z_emit, z_desc, z_delta |
---|
344 | REAL :: Nice_per_m, fuel_per_m, frac_surv |
---|
345 | |
---|
346 | !------------------------------ |
---|
347 | !-- INITIAL ICE NUCLEATION -- |
---|
348 | !------------------------------ |
---|
349 | ! |
---|
350 | !--Karcher et al. (2015), Bier and Burkhardt (2019, 2022) |
---|
351 | !log_liqsat_ratio = LOG(liq_satratio) |
---|
352 | |
---|
353 | !! dry core radius in nm |
---|
354 | !! HERE SHOULD IT BE liqsat_ratio OR liqsat_ratio - 1. ? |
---|
355 | !rd_act_amb = (4. / 27. / LOG(liqsat_ratio)**2 / 0.5)**(1./3.) |
---|
356 | !! Integrate lognormal distribution between rd_act_amb and +inf |
---|
357 | !coef_expo = 4. / SQRT(2. * RPI) / LOG(raer_amb_std) |
---|
358 | !phi_amb = 1. / (1. + (rd_act_amb / raer_amb_mean)**coef_expo) |
---|
359 | ! |
---|
360 | !! BU22, Eq. A1, dry core radius in nm |
---|
361 | !rd_act_soot = 0.96453426 + 1.21152973 / log_liqsat_ratio - 0.00520358 / log_liqsat_ratio**2 & |
---|
362 | ! + 2.32286432e-5 / log_liqsat_ratio**3 - 4.36979055e-8 / log_liqsat_ratio**4 |
---|
363 | !rd_act_soot = MIN(150., MAX(1., rd_act_soot)) |
---|
364 | !! Integrate lognormal distribution between rd_act_amb and +inf |
---|
365 | !coef_expo = 4. / SQRT(2. * RPI) / LOG(r_soot_std) |
---|
366 | !phi_amb = 1. / (1. + (rd_act_soot / r_soot_mean)**coef_expo) |
---|
367 | ! |
---|
368 | !dil_coef = (0.01 / t0)**0.9 |
---|
369 | ! |
---|
370 | !! BU22, Eq. 5b |
---|
371 | !AEI_soot = phi_soot * EI_soot_aviation |
---|
372 | !AEI_amb = phi_amb * air_to_fuel_ratio_engine * (1. - dil_coef) / dil_coef & |
---|
373 | ! / rho_air * Naer_amb * 1e6 |
---|
374 | !AEI_contrails = AEI_soot + AEI_amb |
---|
375 | AEI_contrails = EI_soot_aviation |
---|
376 | |
---|
377 | |
---|
378 | !---------------------------- |
---|
379 | !-- ICE CRYSTALS LOSS -- |
---|
380 | !---------------------------- |
---|
381 | ! |
---|
382 | !--Based on Lottermoser and Unterstrasser (2025, LU25 hereinafter) |
---|
383 | !--which is an update of Unterstrasser (2016, U16 hereinafter) |
---|
384 | |
---|
385 | ! fuel consumption in kg/m flown |
---|
386 | fuel_per_m = flight_fuel / flight_dist |
---|
387 | |
---|
388 | ! LU25, Eq. A2 |
---|
389 | z_atm = 607.46 * (icesat_ratio - 1.)**0.897 * (temp / 205.)**2.225 |
---|
390 | |
---|
391 | ! water vapor emissions [kg/m flown], LU25, Eq. 2 |
---|
392 | ! U16, Eq. 6 |
---|
393 | rho_emit = fuel_per_m * EI_H2O_aviation / plume_area_loss |
---|
394 | ! LU25, Eq. A3 |
---|
395 | temp_205 = temp - 205. |
---|
396 | z_emit = 1106.6 * (rho_emit * 1e5)**(0.678 + 0.0116 * temp_205) & |
---|
397 | * EXP(- (0.0807 + 0.000428 * temp_205) * temp_205) |
---|
398 | |
---|
399 | ! U16, Eq. 4 |
---|
400 | z_desc = SQRT(8. * circ_0_loss / RPI / N_Brunt_Vaisala_aviation ) |
---|
401 | |
---|
402 | ! initial number of ice crystals [#/m flown], LU25, Eq. 1 |
---|
403 | Nice_per_m = fuel_per_m * AEI_contrails |
---|
404 | ! ice crystals number concentration [#/m3], LU25, Eq. A1 |
---|
405 | nice_init = Nice_per_m / plume_area_loss |
---|
406 | ! LU25, Eq. 9, 13d, 13e, 13f and 13g |
---|
407 | z_delta = (nice_init / nice_init_ref_loss)**(-0.16) * (1.27 * z_atm + 0.42 * z_emit) - 0.49 * z_desc |
---|
408 | |
---|
409 | ! LU25, Eq. 11, 12, 13a, 13b and 13c |
---|
410 | fsurv_contrails = 0.42 + 1.31 / RPI * ATAN(-1. + z_delta / 100.) |
---|
411 | fsurv_contrails = MIN(1., MAX(0., fsurv_contrails)) |
---|
412 | |
---|
413 | Nice_per_m_init_contrails = Nice_per_m * fsurv_contrails |
---|
414 | AEI_surv_contrails = AEI_contrails * fsurv_contrails |
---|
415 | |
---|
416 | END SUBROUTINE CONTRAIL_ICE_NUMBER_CONCENTRATION |
---|
417 | |
---|
418 | |
---|
419 | SUBROUTINE init_read_aviation_emissions |
---|
420 | |
---|
421 | USE mod_grid_phy_lmdz, ONLY: grid_type, unstructured |
---|
422 | USE mod_phys_lmdz_para, ONLY: is_omp_master |
---|
423 | USE lmdz_xios, ONLY: xios_set_file_attr, xios_set_field_attr |
---|
424 | |
---|
425 | IF (grid_type==unstructured) THEN |
---|
426 | IF (is_omp_master) THEN |
---|
427 | ! Activate aviation file |
---|
428 | CALL xios_set_file_attr("aviation_file", enabled=.TRUE.) |
---|
429 | ! Activate aviation fields |
---|
430 | CALL xios_set_field_attr("DISTFLOWN_read", enabled=.TRUE.) |
---|
431 | CALL xios_set_field_attr("DISTFLOWN_interp", enabled=.TRUE.) |
---|
432 | CALL xios_set_field_attr("FUELBURN_read", enabled=.TRUE.) |
---|
433 | CALL xios_set_field_attr("FUELBURN_interp", enabled=.TRUE.) |
---|
434 | ENDIF |
---|
435 | ENDIF |
---|
436 | |
---|
437 | END SUBROUTINE init_read_aviation_emissions |
---|
438 | |
---|
439 | |
---|
440 | SUBROUTINE read_aviation_emissions(klon, klev) |
---|
441 | ! This subroutine allows to read the traffic density data read in the file aviation.nc |
---|
442 | ! This file is defined in ./COMP/lmdz.card |
---|
443 | ! Field names in context_input_lmdz.xml should be the same as in the file. |
---|
444 | USE netcdf |
---|
445 | USE mod_phys_lmdz_para |
---|
446 | USE mod_grid_phy_lmdz, ONLY: nbp_lon_=>nbp_lon, nbp_lat_=>nbp_lat, nbp_lev_=>nbp_lev, & |
---|
447 | klon_glo, grid2Dto1D_glo, grid_type, unstructured |
---|
448 | USE iophy, ONLY : io_lon, io_lat |
---|
449 | USE lmdz_xios |
---|
450 | USE print_control_mod, ONLY: lunout |
---|
451 | USE readaerosol_mod, ONLY: check_err |
---|
452 | USE lmdz_lscp_ini, ONLY: EI_H2O_aviation |
---|
453 | IMPLICIT NONE |
---|
454 | |
---|
455 | INTEGER, INTENT(IN) :: klon, klev ! number of horizontal grid points and vertical levels |
---|
456 | |
---|
457 | !---------------------------------------------------- |
---|
458 | ! Local variable |
---|
459 | !---------------------------------------------------- |
---|
460 | REAL, ALLOCATABLE :: flight_dist_mpi(:,:,:), flight_fuel_mpi(:,:,:) |
---|
461 | INTEGER :: ierr |
---|
462 | LOGICAL, SAVE :: first = .TRUE. |
---|
463 | !$OMP THREADPRIVATE(first) |
---|
464 | |
---|
465 | ! FOR REGULAR LON LAT |
---|
466 | CHARACTER(len=30) :: fname |
---|
467 | INTEGER :: nid, dimid, varid |
---|
468 | INTEGER :: imth, i, j, k |
---|
469 | REAL :: npole, spole |
---|
470 | REAL, ALLOCATABLE, DIMENSION(:,:,:,:) :: flight_dist_glo2D |
---|
471 | REAL, ALLOCATABLE, DIMENSION(:,:,:,:) :: flight_fuel_glo2D |
---|
472 | REAL, ALLOCATABLE, DIMENSION(:) :: vartmp_lev |
---|
473 | REAL, ALLOCATABLE, DIMENSION(:,:,:) :: vartmp |
---|
474 | REAL, ALLOCATABLE, DIMENSION(:) :: lon_src ! longitudes in file |
---|
475 | REAL, ALLOCATABLE, DIMENSION(:) :: lat_src, lat_src_inv ! latitudes in file |
---|
476 | LOGICAL :: invert_lat ! true if the field has to be inverted for latitudes |
---|
477 | INTEGER :: nbp_lon, nbp_lat |
---|
478 | |
---|
479 | |
---|
480 | IF (grid_type==unstructured) THEN |
---|
481 | |
---|
482 | IF (first) THEN |
---|
483 | IF (is_omp_master) THEN |
---|
484 | ! Get number of vertical levels and level values |
---|
485 | CALL xios_get_axis_attr("aviation_lev", n_glo=nleva) |
---|
486 | ENDIF |
---|
487 | CALL bcast_omp(nleva) |
---|
488 | |
---|
489 | ! Allocation of arrays |
---|
490 | ALLOCATE(flight_dist_read(klon, nleva, 1), STAT=ierr) |
---|
491 | IF (ierr /= 0) CALL abort_physic('lmdz_aviation', 'problem to allocate flight_dist',1) |
---|
492 | ALLOCATE(flight_fuel_read(klon, nleva, 1), STAT=ierr) |
---|
493 | IF (ierr /= 0) CALL abort_physic('lmdz_aviation', 'problem to allocate flight_fuel',1) |
---|
494 | ALLOCATE(aviation_lev(nleva), STAT=ierr) |
---|
495 | IF (ierr /= 0) CALL abort_physic('lmdz_aviation', 'problem to allocate aviation_lev',1) |
---|
496 | |
---|
497 | IF (is_omp_master) THEN |
---|
498 | ! Get number of vertical levels and level values |
---|
499 | CALL xios_get_axis_attr("aviation_lev", value=aviation_lev(:)) |
---|
500 | ENDIF |
---|
501 | CALL bcast_omp(aviation_lev) |
---|
502 | first = .FALSE. |
---|
503 | ENDIF ! first |
---|
504 | |
---|
505 | ! Read the data from the file |
---|
506 | ! is_omp_master is necessary to make XIOS works |
---|
507 | IF (is_omp_master) THEN |
---|
508 | ALLOCATE(flight_dist_mpi(klon_mpi, nleva,1), STAT=ierr) |
---|
509 | IF (ierr /= 0) CALL abort_physic('read_aviation_emissions', 'problem to allocate flight_dist_mpi',1) |
---|
510 | ALLOCATE(flight_fuel_mpi(klon_mpi, nleva,1), STAT=ierr) |
---|
511 | IF (ierr /= 0) CALL abort_physic('read_aviation_emissions', 'problem to allocate flight_fuel_mpi',1) |
---|
512 | CALL xios_recv_field("DISTFLOWN_interp", flight_dist_mpi(:,:,1)) |
---|
513 | CALL xios_recv_field("FUELBURN_interp", flight_fuel_mpi(:,:,1)) |
---|
514 | ENDIF |
---|
515 | |
---|
516 | ! Propagate to other OMP threads: flight_dist_mpi(klon_mpi,klev) to flight_dist(klon,klev) |
---|
517 | CALL scatter_omp(flight_dist_mpi, flight_dist_read) |
---|
518 | CALL scatter_omp(flight_fuel_mpi, flight_fuel_read) |
---|
519 | |
---|
520 | ELSE |
---|
521 | |
---|
522 | nbp_lon=nbp_lon_ |
---|
523 | nbp_lat=nbp_lat_ |
---|
524 | |
---|
525 | IF (is_mpi_root) THEN |
---|
526 | ALLOCATE(lon_src(nbp_lon)) |
---|
527 | ALLOCATE(lat_src(nbp_lat)) |
---|
528 | ALLOCATE(lat_src_inv(nbp_lat)) |
---|
529 | ELSE |
---|
530 | ALLOCATE(flight_dist_glo2D(0,0,0,0)) |
---|
531 | ALLOCATE(flight_fuel_glo2D(0,0,0,0)) |
---|
532 | ENDIF |
---|
533 | |
---|
534 | IF (is_mpi_root .AND. is_omp_root) THEN |
---|
535 | |
---|
536 | ! 1) Open file |
---|
537 | !**************************************************************************************** |
---|
538 | fname = 'aviation.nc' |
---|
539 | CALL check_err( nf90_open(TRIM(fname), NF90_NOWRITE, nid), "pb open "//TRIM(fname) ) |
---|
540 | |
---|
541 | |
---|
542 | ! Test for equal longitudes and latitudes in file and model |
---|
543 | !**************************************************************************************** |
---|
544 | ! Read and test longitudes |
---|
545 | CALL check_err( nf90_inq_varid(nid, 'longitude', varid), "pb inq lon" ) |
---|
546 | CALL check_err( nf90_get_var(nid, varid, lon_src(:)), "pb get lon" ) |
---|
547 | |
---|
548 | IF (maxval(ABS(lon_src - io_lon)) > 0.001) THEN |
---|
549 | WRITE(lunout,*) 'Problem in longitudes read from file : ',TRIM(fname) |
---|
550 | WRITE(lunout,*) 'longitudes in file ', TRIM(fname),' : ', lon_src |
---|
551 | WRITE(lunout,*) 'longitudes in model :', io_lon |
---|
552 | |
---|
553 | CALL abort_physic('lmdz_aviation', 'longitudes are not the same in file and model',1) |
---|
554 | END IF |
---|
555 | |
---|
556 | ! Read and test latitudes |
---|
557 | CALL check_err( nf90_inq_varid(nid, 'latitude', varid),"pb inq lat" ) |
---|
558 | CALL check_err( nf90_get_var(nid, varid, lat_src(:)),"pb get lat" ) |
---|
559 | |
---|
560 | ! Invert source latitudes |
---|
561 | DO j = 1, nbp_lat |
---|
562 | lat_src_inv(j) = lat_src(nbp_lat+1-j) |
---|
563 | END DO |
---|
564 | |
---|
565 | IF (maxval(ABS(lat_src - io_lat)) < 0.001) THEN |
---|
566 | ! Latitudes are the same |
---|
567 | invert_lat=.FALSE. |
---|
568 | ELSE IF (maxval(ABS(lat_src_inv - io_lat)) < 0.001) THEN |
---|
569 | ! Inverted source latitudes correspond to model latitudes |
---|
570 | WRITE(lunout,*) 'latitudes will be inverted for file : ',TRIM(fname) |
---|
571 | invert_lat=.TRUE. |
---|
572 | ELSE |
---|
573 | WRITE(lunout,*) 'Problem in latitudes read from file : ',TRIM(fname) |
---|
574 | WRITE(lunout,*) 'latitudes in file ', TRIM(fname),' : ', lat_src |
---|
575 | WRITE(lunout,*) 'latitudes in model :', io_lat |
---|
576 | CALL abort_physic('lmdz_aviation', 'latitudes do not correspond between file and model',1) |
---|
577 | END IF |
---|
578 | |
---|
579 | |
---|
580 | ! 2) Find vertical dimension nleva |
---|
581 | !**************************************************************************************** |
---|
582 | ierr = nf90_inq_dimid(nid, 'pressure_Pa', dimid) |
---|
583 | CALL check_err( nf90_inquire_dimension(nid, dimid, len = nleva), "pb inq dim for PRESNIVS or lev" ) |
---|
584 | |
---|
585 | ! Allocate variables depending on the number of vertical levels |
---|
586 | ALLOCATE(flight_dist_glo2D(nbp_lon, nbp_lat, nleva, 1), flight_fuel_glo2D(nbp_lon, nbp_lat, nleva, 1), stat=ierr) |
---|
587 | IF (ierr /= 0) CALL abort_physic('lmdz_aviation', 'pb in allocation 1',1) |
---|
588 | |
---|
589 | ALLOCATE(aviation_lev(nleva), stat=ierr) |
---|
590 | IF (ierr /= 0) CALL abort_physic('lmdz_aviation', 'pb in allocation 2',1) |
---|
591 | |
---|
592 | ! 3) Read all variables from file |
---|
593 | !**************************************************************************************** |
---|
594 | |
---|
595 | ! Get variable id |
---|
596 | CALL check_err( nf90_inq_varid(nid, 'seg_length', varid),"pb inq var seg_length_m" ) |
---|
597 | ! Get the variable |
---|
598 | CALL check_err( nf90_get_var(nid, varid, flight_dist_glo2D),"pb get var seg_length_m" ) |
---|
599 | |
---|
600 | ! Get variable id |
---|
601 | CALL check_err( nf90_inq_varid(nid, 'fuel_burn', varid),"pb inq var fuel_burn" ) |
---|
602 | ! Get the variable |
---|
603 | CALL check_err( nf90_get_var(nid, varid, flight_fuel_glo2D),"pb get var fuel_burn" ) |
---|
604 | |
---|
605 | ! Get variable id |
---|
606 | CALL check_err( nf90_inq_varid(nid, "pressure_Pa", varid),"pb inq var pressure_Pa" ) |
---|
607 | ! Get the variable |
---|
608 | CALL check_err( nf90_get_var(nid, varid, aviation_lev),"pb get var pressure_Pa" ) |
---|
609 | |
---|
610 | |
---|
611 | ! 4) Close file |
---|
612 | !**************************************************************************************** |
---|
613 | CALL check_err( nf90_close(nid), "pb in close" ) |
---|
614 | |
---|
615 | |
---|
616 | ! 5) Transform the global field from 2D(nbp_lon,nbp_lat) to 1D(klon_glo) |
---|
617 | !**************************************************************************************** |
---|
618 | ! Test if vertical levels have to be inversed |
---|
619 | |
---|
620 | IF (aviation_lev(1) < aviation_lev(nleva)) THEN |
---|
621 | |
---|
622 | ALLOCATE(vartmp(nbp_lon, nbp_lat, nleva), vartmp_lev(nleva)) |
---|
623 | |
---|
624 | ! Inverse vertical levels for flight_dist_glo2D |
---|
625 | vartmp(:,:,:) = flight_dist_glo2D(:,:,:,1) |
---|
626 | DO k=1, nleva |
---|
627 | DO j=1, nbp_lat |
---|
628 | DO i=1, nbp_lon |
---|
629 | flight_dist_glo2D(i,j,k,1) = vartmp(i,j,nleva+1-k) |
---|
630 | END DO |
---|
631 | END DO |
---|
632 | END DO |
---|
633 | |
---|
634 | ! Inverse vertical levels for flight_fuel_glo2D |
---|
635 | vartmp(:,:,:) = flight_fuel_glo2D(:,:,:,1) |
---|
636 | DO k=1, nleva |
---|
637 | DO j=1, nbp_lat |
---|
638 | DO i=1, nbp_lon |
---|
639 | flight_fuel_glo2D(i,j,k,1) = vartmp(i,j,nleva+1-k) |
---|
640 | END DO |
---|
641 | END DO |
---|
642 | END DO |
---|
643 | |
---|
644 | ALLOCATE(vartmp_lev(nleva)) |
---|
645 | ! Inverte vertical axes for aviation_lev |
---|
646 | vartmp_lev(:) = aviation_lev(:) |
---|
647 | DO k=1, nleva |
---|
648 | aviation_lev(k) = vartmp_lev(nleva+1-k) |
---|
649 | END DO |
---|
650 | |
---|
651 | DEALLOCATE(vartmp, vartmp_lev) |
---|
652 | |
---|
653 | ELSE |
---|
654 | WRITE(lunout,*) 'Vertical axis in file ',TRIM(fname), ' is ok, no vertical inversion is done' |
---|
655 | END IF |
---|
656 | |
---|
657 | |
---|
658 | ! - Invert latitudes if necessary |
---|
659 | IF (invert_lat) THEN |
---|
660 | |
---|
661 | ALLOCATE(vartmp(nbp_lon, nbp_lat, nleva)) |
---|
662 | |
---|
663 | ! Invert latitudes for the variable |
---|
664 | vartmp(:,:,:) = flight_dist_glo2D(:,:,:,1) ! use varmth temporarly |
---|
665 | DO k=1,nleva |
---|
666 | DO j=1,nbp_lat |
---|
667 | DO i=1,nbp_lon |
---|
668 | flight_dist_glo2D(i,j,k,1) = vartmp(i,nbp_lat+1-j,k) |
---|
669 | END DO |
---|
670 | END DO |
---|
671 | END DO |
---|
672 | |
---|
673 | ! Invert latitudes for the variable |
---|
674 | vartmp(:,:,:) = flight_fuel_glo2D(:,:,:,1) ! use varmth temporarly |
---|
675 | DO k=1,nleva |
---|
676 | DO j=1,nbp_lat |
---|
677 | DO i=1,nbp_lon |
---|
678 | flight_fuel_glo2D(i,j,k,1) = vartmp(i,nbp_lat+1-j,k) |
---|
679 | END DO |
---|
680 | END DO |
---|
681 | END DO |
---|
682 | |
---|
683 | DEALLOCATE(vartmp) |
---|
684 | END IF ! invert_lat |
---|
685 | |
---|
686 | ! Do zonal mead at poles and distribut at whole first and last latitude |
---|
687 | DO k=1, nleva |
---|
688 | npole=0. ! North pole, j=1 |
---|
689 | spole=0. ! South pole, j=nbp_lat |
---|
690 | DO i=1,nbp_lon |
---|
691 | npole = npole + flight_dist_glo2D(i,1,k,1) |
---|
692 | spole = spole + flight_dist_glo2D(i,nbp_lat,k,1) |
---|
693 | END DO |
---|
694 | npole = npole/REAL(nbp_lon) |
---|
695 | spole = spole/REAL(nbp_lon) |
---|
696 | flight_dist_glo2D(:,1, k,1) = npole |
---|
697 | flight_dist_glo2D(:,nbp_lat,k,1) = spole |
---|
698 | END DO |
---|
699 | |
---|
700 | ! Do zonal mead at poles and distribut at whole first and last latitude |
---|
701 | DO k=1, nleva |
---|
702 | npole=0. ! North pole, j=1 |
---|
703 | spole=0. ! South pole, j=nbp_lat |
---|
704 | DO i=1,nbp_lon |
---|
705 | npole = npole + flight_fuel_glo2D(i,1,k,1) |
---|
706 | spole = spole + flight_fuel_glo2D(i,nbp_lat,k,1) |
---|
707 | END DO |
---|
708 | npole = npole/REAL(nbp_lon) |
---|
709 | spole = spole/REAL(nbp_lon) |
---|
710 | flight_fuel_glo2D(:,1, k,1) = npole |
---|
711 | flight_fuel_glo2D(:,nbp_lat,k,1) = spole |
---|
712 | END DO |
---|
713 | |
---|
714 | ALLOCATE(flight_dist_mpi(klon_glo, nleva, 1), flight_fuel_mpi(klon_glo, nleva, 1), stat=ierr) |
---|
715 | IF (ierr /= 0) CALL abort_physic('lmdz_aviation', 'pb in allocation 3',1) |
---|
716 | |
---|
717 | ! Transform from 2D to 1D field |
---|
718 | CALL grid2Dto1D_glo(flight_dist_glo2D, flight_dist_mpi) |
---|
719 | CALL grid2Dto1D_glo(flight_fuel_glo2D, flight_fuel_mpi) |
---|
720 | |
---|
721 | ELSE |
---|
722 | ALLOCATE(flight_dist_mpi(0,0,0), flight_fuel_mpi(0,0,0)) |
---|
723 | END IF ! is_mpi_root .AND. is_omp_root |
---|
724 | |
---|
725 | !$OMP BARRIER |
---|
726 | |
---|
727 | ! 6) Distribute to all processes |
---|
728 | ! Scatter global field(klon_glo) to local process domain(klon) |
---|
729 | ! and distribute nleva to all processes |
---|
730 | !**************************************************************************************** |
---|
731 | |
---|
732 | ! Distribute nleva |
---|
733 | CALL bcast(nleva) |
---|
734 | |
---|
735 | ! Allocate and distribute pt_ap and pt_b |
---|
736 | IF (.NOT. ALLOCATED(aviation_lev)) THEN ! if pt_ap is allocated also pt_b is allocated |
---|
737 | ALLOCATE(aviation_lev(nleva), stat=ierr) |
---|
738 | IF (ierr /= 0) CALL abort_physic('lmdz_aviation', 'pb in allocation 4',1) |
---|
739 | END IF |
---|
740 | CALL bcast(aviation_lev) |
---|
741 | |
---|
742 | ! Allocate space for output pointer variable at local process |
---|
743 | ALLOCATE(flight_dist_read(klon, nleva, 1), flight_fuel_read(klon, nleva, 1), stat=ierr) |
---|
744 | IF (ierr /= 0) CALL abort_physic('lmdz_aviation', 'pb in allocation 5',1) |
---|
745 | |
---|
746 | ! Scatter global field to local domain at local process |
---|
747 | CALL scatter(flight_dist_mpi, flight_dist_read) |
---|
748 | CALL scatter(flight_fuel_mpi, flight_fuel_read) |
---|
749 | |
---|
750 | ENDIF |
---|
751 | |
---|
752 | END SUBROUTINE read_aviation_emissions |
---|
753 | |
---|
754 | SUBROUTINE vertical_interpolation_aviation(klon, klev, paprs, pplay, temp, flight_dist, flight_fuel) |
---|
755 | ! This subroutine performs the vertical interpolation from the read data in aviation.nc |
---|
756 | ! where there are nleva vertical levels described in aviation_lev to the klev levels or |
---|
757 | ! the model. |
---|
758 | ! flight_dist_read(klon,nleva) -> flight_dist(klon, klev) |
---|
759 | ! flight_fuel_read(klon,nleva) -> flight_fuel(klon, klev) |
---|
760 | |
---|
761 | USE lmdz_lscp_ini, ONLY: RD, RG |
---|
762 | USE lmdz_lscp_ini, ONLY: aviation_coef |
---|
763 | |
---|
764 | IMPLICIT NONE |
---|
765 | |
---|
766 | INTEGER, INTENT(IN) :: klon, klev ! number of horizontal grid points and vertical levels |
---|
767 | REAL, INTENT(IN) :: paprs(klon, klev+1) ! inter-layer pressure [Pa] |
---|
768 | REAL, INTENT(IN) :: pplay(klon, klev) ! mid-layer pressure [Pa] |
---|
769 | REAL, INTENT(IN) :: temp(klon, klev) ! temperature [K] |
---|
770 | REAL, INTENT(OUT) :: flight_dist(klon,klev) ! Aviation distance flown concentration [m/s/m3] |
---|
771 | REAL, INTENT(OUT) :: flight_fuel(klon,klev) ! Aviation fuel burn concentration [kg/s/m3] |
---|
772 | |
---|
773 | !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! |
---|
774 | ! Local variable |
---|
775 | !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! |
---|
776 | REAL :: aviation_interface(1:nleva+1) ! Pressure of aviation file interfaces [ Pa ] |
---|
777 | INTEGER :: k, kori ! Loop index for vertical layers |
---|
778 | INTEGER :: i ! Loop index for horizontal grid |
---|
779 | REAL :: zfrac ! Fraction of layer kori in layer k |
---|
780 | REAL :: width_read_layer(1:nleva) ! width of a given layer [ Pa ] |
---|
781 | REAL :: rho, rhodz, dz |
---|
782 | |
---|
783 | ! Initialisation |
---|
784 | flight_dist(:,:) = 0. |
---|
785 | flight_fuel(:,:) = 0. |
---|
786 | |
---|
787 | ! Compute the array with the vertical interface |
---|
788 | ! It starts at 1 and has length nleva + 1 |
---|
789 | ! Note that aviation_lev has nleva and gives the altitude in the middle of the layers |
---|
790 | ! Surface pressure in standard atmosphere model [ Pa ] |
---|
791 | aviation_interface(1) = 101325. |
---|
792 | DO kori=2, nleva |
---|
793 | aviation_interface(kori) = (aviation_lev(kori-1)+aviation_lev(kori))/2.0 ! [ Pa ] |
---|
794 | ENDDO |
---|
795 | ! Last interface - we assume the same spacing as the very last one |
---|
796 | aviation_interface(nleva+1) = aviation_interface(nleva) - (aviation_lev(nleva-1) - aviation_lev(nleva)) |
---|
797 | |
---|
798 | ! Vertical width of each layer of the read file |
---|
799 | ! It is positive |
---|
800 | DO kori=1, nleva |
---|
801 | width_read_layer(kori) = aviation_interface(kori) - aviation_interface(kori+1) |
---|
802 | ENDDO |
---|
803 | |
---|
804 | ! Vertical reprojection |
---|
805 | ! The loop over klon is induced since it is done by MPI threads |
---|
806 | ! zfrac is the fraction of layer kori (read file) included in layer k (model) |
---|
807 | DO i=1,klon |
---|
808 | DO k=1, klev |
---|
809 | DO kori=1,nleva |
---|
810 | ! Which of the lower interfaces is the highest (<=> the lowest pressure) ? |
---|
811 | zfrac = min(paprs(i,k), aviation_interface(kori)) |
---|
812 | ! Which of the upper interfaces is the lowest (<=> the greatest pressure) ? |
---|
813 | zfrac = zfrac - max(paprs(i,k+1), aviation_interface(kori+1)) |
---|
814 | ! If zfrac is negative, the layers are not overlapping |
---|
815 | ! Otherwise, we get the fraction of layer kori that overlap with layer k |
---|
816 | ! after normalisation to the total kori layer width |
---|
817 | zfrac = max(0.0, zfrac) / width_read_layer(kori) |
---|
818 | |
---|
819 | ! Vertical reprojection for each desired array |
---|
820 | flight_dist(i,k) = flight_dist(i,k) + zfrac * flight_dist_read(i,kori,1) |
---|
821 | flight_fuel(i,k) = flight_fuel(i,k) + zfrac * flight_fuel_read(i,kori,1) |
---|
822 | ENDDO |
---|
823 | |
---|
824 | !--Dry density [kg/m3] |
---|
825 | rho = pplay(i,k) / temp(i,k) / RD |
---|
826 | !--Dry air mass [kg/m2] |
---|
827 | rhodz = ( paprs(i,k) - paprs(i,k+1) ) / RG |
---|
828 | !--Cell thickness [m] |
---|
829 | dz = rhodz / rho |
---|
830 | |
---|
831 | !--Normalisation with the cell thickness |
---|
832 | flight_dist(i,k) = flight_dist(i,k) / dz |
---|
833 | flight_fuel(i,k) = flight_fuel(i,k) / dz |
---|
834 | |
---|
835 | !--Enhancement factor |
---|
836 | flight_dist(i,k) = flight_dist(i,k) * aviation_coef |
---|
837 | flight_fuel(i,k) = flight_fuel(i,k) * aviation_coef |
---|
838 | ENDDO |
---|
839 | ENDDO |
---|
840 | |
---|
841 | END SUBROUTINE vertical_interpolation_aviation |
---|
842 | |
---|
843 | END MODULE lmdz_aviation |
---|