PartMC  2.3.0
mosaic.F90
Go to the documentation of this file.
1 ! Copyright (C) 2007-2011 Matthew West
2 ! Licensed under the GNU General Public License version 2 or (at your
3 ! option) any later version. See the file COPYING for details.
4 
5 !> \file
6 !> The pmc_mosaic module.
7 
8 !> Interface to the MOSAIC aerosol and gas phase chemistry code.
9 module pmc_mosaic
10 
11  use pmc_aero_data
12  use pmc_aero_state
13  use pmc_constants
14  use pmc_env_state
15  use pmc_gas_data
16  use pmc_gas_state
17  use pmc_util
18 
19 contains
20 
21 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
22 
23  !> Whether MOSAIC support is compiled in.
24  logical function mosaic_support()
25 
26 #ifdef PMC_USE_MOSAIC
27  mosaic_support = .true.
28 #else
29  mosaic_support = .false.
30 #endif
31 
32  end function mosaic_support
33 
34 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
35 
36  !> Initialize all MOSAIC data-structures.
37  subroutine mosaic_init(env_state, aero_data, del_t, do_optical)
38 
39 #ifdef PMC_USE_MOSAIC
40  use module_data_mosaic_aero, only: alpha_astem, rtol_eqb_astem, &
41  ptol_mol_astem, mgas_aer_xfer, mdynamic_solver
42 
43  use module_data_mosaic_main, only: tbeg_sec, dt_sec, rlon, rlat, &
44  zalt_m, rh, te, pr_atm, cair_mlc, cair_molm3, ppb, avogad, &
45  mmode, mgas, maer, mcld, maeroptic, mshellcore, &
46  msolar, mphoto, lun_aeroptic, naerbin
47 #endif
48 
49  !> Environment state.
50  type(env_state_t), intent(inout) :: env_state
51  !> Aerosol data.
52  type(aero_data_t), intent(in) :: aero_data
53  !> Timestep for coagulation.
54  real(kind=dp), intent(in) :: del_t
55  !> Whether to compute optical properties.
56  logical, intent(in) :: do_optical
57 
58 #ifdef PMC_USE_MOSAIC
59  ! MOSAIC function interfaces
60  interface
61  subroutine loadperoxyparameters()
62  end subroutine loadperoxyparameters
63  subroutine init_data_modules()
64  end subroutine init_data_modules
65  subroutine allocatememory()
66  end subroutine allocatememory
67  end interface
68 
69  call init_data_modules ! initialize indices and vars
70 
71  ! allocate one aerosol bin
72  naerbin = 1
73  call allocatememory()
74 
75  ! parameters
76  mmode = 1 ! 1 = time integration, 2 = parametric analysis
77  mgas = 1 ! 1 = gas chem on, 0 = gas chem off
78  maer = 1 ! 1 = aer chem on, 0 = aer chem off
79  mcld = 0 ! 1 = cld chem on, 0 = cld chem off
80  if (do_optical) then
81  maeroptic = 1 ! 1 = aer_optical on, 0 = aer_optical off
82  else
83  maeroptic = 0
84  end if
85  mshellcore = 1 ! 0 = no shellcore, 1 = core is BC only
86  ! 2 = core is BC and DUST
87  msolar = 1 ! 1 = diurnally varying phot, 2 = fixed phot
88  mphoto = 2 ! 1 = Rick's param, 2 = Yang's param
89  mgas_aer_xfer = 1 ! 1 = gas-aerosol partitioning, 0 = no partition
90  mdynamic_solver = 1 ! 1 = astem, 2 = lsodes
91  alpha_astem = 0.5d0 ! solver parameter. range: 0.01 - 1.0
92  rtol_eqb_astem = 0.01d0 ! relative eqb tolerance. range: 0.01 - 0.03
93  ptol_mol_astem = 0.01d0 ! percent mol tolerance. range: 0.01 - 1.0
94 
95  ! time variables
96  dt_sec = del_t ! time-step (s)
97  tbeg_sec = env_state%start_day*24*3600 + & ! time since the beg of
98  nint(env_state%start_time) ! year 00:00, UTC (s)
99 
100  ! geographic location
101  rlon = deg2rad(env_state%longitude) ! longitude
102  rlat = deg2rad(env_state%latitude) ! latitude
103  zalt_m = env_state%altitude ! altitude (m)
104 
105  ! environmental parameters: map PartMC -> MOSAIC
106  rh = env_state%rel_humid * 100.d0 ! relative humidity (%)
107  te = env_state%temp ! temperature (K)
108  pr_atm = env_state%pressure / const%air_std_press ! pressure (atm)
109  cair_mlc = avogad*pr_atm/(82.056d0*te) ! air conc [molec/cc]
110  cair_molm3 = 1d6*pr_atm/(82.056d0*te) ! air conc [mol/m^3]
111  ppb = 1d9
112 
113  call loadperoxyparameters ! Aperox and Bperox only once
114 
115  ! get unit for aerosol optical output
116  if (lun_aeroptic <= 0 ) lun_aeroptic = get_unit()
117 
118  ! ensure H2O is a valid species
119  call assert_msg(111041803, aero_data%i_water > 0, &
120  "MOSAIC requires H2O as an aerosol species")
121 
122 #endif
123 
124  end subroutine mosaic_init
125 
126 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
127 
128  !> Clean-up after running MOSAIC, deallocating memory.
129  subroutine mosaic_cleanup()
130 
131 #ifdef PMC_USE_MOSAIC
132  ! MOSAIC function interfaces
133  interface
134  subroutine deallocatememory()
135  end subroutine deallocatememory
136  end interface
137 
138  call deallocatememory()
139 #endif
140 
141  end subroutine mosaic_cleanup
142 
143 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
144 
145  !> Map all data PartMC -> MOSAIC.
146  subroutine mosaic_from_partmc(env_state, aero_data, &
147  aero_state, gas_data, gas_state)
148 
149 #ifdef PMC_USE_MOSAIC
150  use module_data_mosaic_aero, only: nbin_a, aer, num_a, jhyst_leg, &
151  jtotal, water_a
152 
153  use module_data_mosaic_main, only: tbeg_sec, tcur_sec, tmid_sec, &
154  dt_sec, dt_min, dt_aeroptic_min, rh, te, pr_atm, cnn, cair_mlc, &
155  cair_molm3, ppb, avogad, msolar, naerbin
156 #endif
157 
158  !> Environment state.
159  type(env_state_t), intent(in) :: env_state
160  !> Aerosol data.
161  type(aero_data_t), intent(in) :: aero_data
162  !> Aerosol state.
163  type(aero_state_t), intent(in) :: aero_state
164  !> Gas data.
165  type(gas_data_t), intent(in) :: gas_data
166  !> Gas state.
167  type(gas_state_t), intent(in) :: gas_state
168 
169 #ifdef PMC_USE_MOSAIC
170  ! local variables
171  real(kind=dp) :: time_utc ! 24-hr UTC clock time (hr).
172  real(kind=dp) :: tmar21_sec ! Time at noon, march 21, UTC (s).
173  real(kind=dp) :: conv_fac(aero_data%n_spec), dum_var
174  integer :: i_part, i_spec, i_spec_mosaic
175  type(aero_particle_t), pointer :: particle
176  real(kind=dp) :: num_conc
177 
178  ! MOSAIC function interfaces
179  interface
180  subroutine allocatememory()
181  end subroutine allocatememory
182  subroutine deallocatememory()
183  end subroutine deallocatememory
184  end interface
185 
186  ! update time variables
187  tmar21_sec = real((79*24 + 12)*3600, kind=dp) ! noon, mar 21, UTC
188  tcur_sec = real(tbeg_sec, kind=dp) + env_state%elapsed_time
189  ! current (old) time since the beg of year 00:00, UTC (s)
190 
191  time_utc = env_state%start_time/3600d0 ! 24-hr UTC clock time (hr)
192  time_utc = time_utc + dt_sec/3600d0
193 
194  do while (time_utc >= 24d0)
195  time_utc = time_utc - 24d0
196  end do
197 
198  tmid_sec = tcur_sec + 0.5d0*dt_sec
199  if(tmid_sec .ge. tmar21_sec)then
200  tmid_sec = tmid_sec - tmar21_sec ! seconds since noon, march 21
201  else
202  tmid_sec = tmid_sec &
203  + dble(((365-79)*24 - 12)*3600) ! seconds since noon, march 21
204  endif
205 
206  ! transport timestep (min)
207  dt_min = dt_sec/60d0
208  ! aerosol optics timestep (min)
209  dt_aeroptic_min = 0d0
210 
211  ! compute aerosol conversion factors
212  do i_spec = 1,aero_data%n_spec
213  ! converts m^3(species) to nmol(species)/m^3(air)
214  conv_fac(i_spec) = 1.d9 * aero_data%density(i_spec) &
215  / aero_data%molec_weight(i_spec)
216  enddo
217 
218  ! environmental parameters: map PartMC -> MOSAIC
219  rh = env_state%rel_humid * 100.d0 ! relative humidity (%)
220  te = env_state%temp ! temperature (K)
221  pr_atm = env_state%pressure / const%air_std_press ! pressure (atm)
222  cair_mlc = avogad*pr_atm/(82.056d0*te) ! air conc [molec/cc]
223  cair_molm3 = 1d6*pr_atm/(82.056d0*te) ! air conc [mol/m^3]
224  ppb = 1d9
225 
226  ! aerosol data: map PartMC -> MOSAIC
227  nbin_a = aero_state_total_particles(aero_state)
228  if (nbin_a > naerbin) then
229  call deallocatememory()
230  naerbin = nbin_a
231  call allocatememory()
232  end if
233  aer = 0d0 ! initialize to zero
234  ! work backwards for consistency with mosaic_to_partmc(), which
235  ! has specific ordering requirements
236  do i_part = aero_state%apa%n_part,1,-1
237  particle => aero_state%apa%particle(i_part)
238  num_conc = aero_weight_array_num_conc(aero_state%awa, particle)
239  do i_spec = 1,aero_data%n_spec
240  i_spec_mosaic = aero_data%mosaic_index(i_spec)
241  if (i_spec_mosaic > 0) then
242  ! convert m^3(species) to nmol(species)/m^3(air)
243  aer(i_spec_mosaic, 3, i_part) & ! nmol/m^3(air)
244  = particle%vol(i_spec) * conv_fac(i_spec) * num_conc
245  end if
246  end do
247  ! handle water specially
248  ! convert m^3(water) to kg(water)/m^3(air)
249  water_a(i_part) = particle%vol(aero_data%i_water) &
250  * aero_data%density(aero_data%i_water) * num_conc
251  num_a(i_part) = 1d-6 * num_conc ! num conc (#/cc(air))
252  jhyst_leg(i_part) = particle%water_hyst_leg
253  end do
254 
255  ! gas chemistry: map PartMC -> MOSAIC
256  cnn = 0d0
257  do i_spec = 1,gas_data%n_spec
258  i_spec_mosaic = gas_data%mosaic_index(i_spec)
259  if (i_spec_mosaic > 0) then
260  ! convert ppbv to molec/cc
261  cnn(i_spec_mosaic) = gas_state%mix_rat(i_spec) * cair_mlc / ppb
262  end if
263  end do
264 #endif
265 
266  end subroutine mosaic_from_partmc
267 
268 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
269 
270  !> Map all data MOSAIC -> PartMC.
271  subroutine mosaic_to_partmc(env_state, aero_data, aero_state, gas_data, &
272  gas_state)
273 
274 #ifdef PMC_USE_MOSAIC
275  use module_data_mosaic_aero, only: nbin_a, aer, num_a, jhyst_leg, &
276  jtotal, water_a
277 
278  use module_data_mosaic_main, only: tbeg_sec, tcur_sec, tmid_sec, &
279  dt_sec, dt_min, dt_aeroptic_min, rh, te, pr_atm, cnn, cair_mlc, &
280  cair_molm3, ppb, avogad, msolar, cos_sza
281 #endif
282 
283  !> Environment state.
284  type(env_state_t), intent(inout) :: env_state
285  !> Aerosol data.
286  type(aero_data_t), intent(in) :: aero_data
287  !> Aerosol state.
288  type(aero_state_t), intent(inout) :: aero_state
289  !> Gas data.
290  type(gas_data_t), intent(in) :: gas_data
291  !> Gas state.
292  type(gas_state_t), intent(inout) :: gas_state
293 
294 #ifdef PMC_USE_MOSAIC
295  ! local variables
296  real(kind=dp) :: conv_fac(aero_data%n_spec), dum_var, num_conc
297  integer :: i_part, i_spec, i_spec_mosaic
298  type(aero_particle_t), pointer :: particle
299  real(kind=dp) :: reweight_num_conc(aero_state%apa%n_part)
300 
301  ! compute aerosol conversion factors
302  do i_spec = 1,aero_data%n_spec
303  ! converts m^3(species) to nmol(species)/m^3(air)
304  conv_fac(i_spec) = 1d9 * aero_data%density(i_spec) &
305  / aero_data%molec_weight(i_spec)
306  enddo
307 
308  ! environmental parameters: map MOSAIC -> PartMC
309  env_state%rel_humid = rh / 100d0
310  env_state%temp = te
311  env_state%pressure = pr_atm * const%air_std_press
312  if (msolar == 1) then
313  env_state%solar_zenith_angle = acos(cos_sza)
314  end if
315  cair_mlc = avogad*pr_atm/(82.056d0*te) ! air conc [molec/cc]
316  cair_molm3 = 1d6*pr_atm/(82.056d0*te) ! air conc [mol/m^3]
317  ppb = 1d9
318 
319  ! We're modifying particle diameters, so the bin sort is now invalid
320  aero_state%valid_sort = .false.
321 
322  ! aerosol data: map MOSAIC -> PartMC
323  call aero_state_num_conc_for_reweight(aero_state, reweight_num_conc)
324  do i_part = 1,aero_state%apa%n_part,1
325  particle => aero_state%apa%particle(i_part)
326  num_conc = aero_weight_array_num_conc(aero_state%awa, particle)
327  do i_spec = 1,aero_data%n_spec
328  i_spec_mosaic = aero_data%mosaic_index(i_spec)
329  if (i_spec_mosaic > 0) then
330  particle%vol(i_spec) = &
331  ! convert nmol(species)/m^3(air) to m^3(species)
332  aer(i_spec_mosaic, 3, i_part) &
333  / (conv_fac(i_spec) * num_conc)
334  end if
335  end do
336  particle%water_hyst_leg = jhyst_leg(i_part)
337  ! handle water specially
338  ! convert kg(water)/m^3(air) to m^3(water)
339  particle%vol(aero_data%i_water) = water_a(i_part) &
340  / aero_data%density(aero_data%i_water) / num_conc
341  end do
342  ! adjust particles to account for weight changes
343  call aero_state_reweight(aero_state, reweight_num_conc)
344 
345  ! gas chemistry: map MOSAIC -> PartMC
346  do i_spec = 1,gas_data%n_spec
347  i_spec_mosaic = gas_data%mosaic_index(i_spec)
348  if (i_spec_mosaic > 0) then
349  ! convert molec/cc to ppbv
350  gas_state%mix_rat(i_spec) = cnn(i_spec_mosaic) / cair_mlc * ppb
351  end if
352  end do
353 #endif
354 
355  end subroutine mosaic_to_partmc
356 
357 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
358 
359  !> Do one timestep with MOSAIC.
360  !!
361  !! We currently also compute aerosol optical properties within this
362  !! subroutine. In principle this could be done at data analysis
363  !! time, rather than inside the timestepper. It's not clear if this
364  !! really matters, however. Because of this mosaic_aero_optical() is
365  !! currently disabled.
366  subroutine mosaic_timestep(env_state, aero_data, aero_state, gas_data, &
367  gas_state, do_optical)
368 
369 #ifdef PMC_USE_MOSAIC
370  use module_data_mosaic_main, only: msolar
371 #endif
372 
373  !> Environment state.
374  type(env_state_t), intent(inout) :: env_state
375  !> Aerosol data.
376  type(aero_data_t), intent(in) :: aero_data
377  !> Aerosol state.
378  type(aero_state_t), intent(inout) :: aero_state
379  !> Gas data.
380  type(gas_data_t), intent(in) :: gas_data
381  !> Gas state.
382  type(gas_state_t), intent(inout) :: gas_state
383  !> Whether to compute optical properties.
384  logical, intent(in) :: do_optical
385 
386 #ifdef PMC_USE_MOSAIC
387  ! MOSAIC function interfaces
388  interface
389  subroutine solarzenithangle()
390  end subroutine solarzenithangle
391  subroutine integratechemistry()
392  end subroutine integratechemistry
393  subroutine aerosol_optical()
394  end subroutine aerosol_optical
395  end interface
396 
397  ! map PartMC -> MOSAIC
398  call mosaic_from_partmc(env_state, aero_data, aero_state, gas_data, &
399  gas_state)
400 
401  if (msolar == 1) then
402  call solarzenithangle
403  end if
404 
405  call integratechemistry
406 
407  ! map MOSAIC -> PartMC
408  if (do_optical) then
409  ! must do optical properties first, as mosaic_to_partmc() may
410  ! change the number of particles
411  call aerosol_optical
412  call mosaic_aero_optical(env_state, aero_data, &
413  aero_state, gas_data, gas_state)
414  end if
415 
416  call mosaic_to_partmc(env_state, aero_data, aero_state, gas_data, &
417  gas_state)
418 #endif
419 
420  end subroutine mosaic_timestep
421 
422 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
423 
424  !> Compute the optical properties of each aerosol particle.
425  !> FIXME: currently disabled.
426  !!
427  !! At the moment we are computing the aerosol optical properties
428  !! every timestep from withing mosaic_timestep. This decision should
429  !! be re-evaluated at some point in the future.
430  subroutine mosaic_aero_optical(env_state, aero_data, &
431  aero_state, gas_data, gas_state)
432 
433 #ifdef PMC_USE_MOSAIC
434  use module_data_mosaic_aero, only: ri_shell_a, ri_core_a, &
435  ext_cross, scat_cross, asym_particle
436 #endif
437 
438  !> Environment state.
439  type(env_state_t), intent(in) :: env_state
440  !> Aerosol data.
441  type(aero_data_t), intent(in) :: aero_data
442  !> Aerosol state.
443  type(aero_state_t), intent(inout) :: aero_state
444  !> Gas data.
445  type(gas_data_t), intent(in) :: gas_data
446  !> Gas state.
447  type(gas_state_t), intent(in) :: gas_state
448 
449 #ifdef PMC_USE_MOSAIC
450  ! MOSAIC function interfaces
451  interface
452  subroutine aerosol_optical()
453  end subroutine aerosol_optical
454  end interface
455 
456  integer :: i_part
457  type(aero_particle_t), pointer :: particle
458 
459  ! map PartMC -> MOSAIC
460 ! call mosaic_from_partmc(env_state, aero_data, aero_state, &
461 ! gas_data, gas_state)
462 
463 ! call aerosol_optical
464 
465  ! map MOSAIC -> PartMC
466  ! work backwards for consistency with mosaic_to_partmc(), which
467  ! has specific ordering requirements
468  do i_part = aero_state%apa%n_part,1,-1
469  particle => aero_state%apa%particle(i_part)
470  particle%absorb_cross_sect = (ext_cross(i_part) &
471  - scat_cross(i_part)) / 1d4 ! (m^2)
472  particle%scatter_cross_sect = scat_cross(i_part) / 1d4 ! (m^2)
473  particle%asymmetry = asym_particle(i_part) ! (1)
474  particle%refract_shell = cmplx(ri_shell_a(i_part), kind=dc) ! (1)
475  particle%refract_core = cmplx(ri_core_a(i_part), kind=dc) ! (1)
476  ! FIXME: how do we get core_vol?
477  !particle%core_vol = diam2vol(dp_core_a(i_part)) ! (m^3)
478  particle%core_vol = 0d0
479  end do
480 #endif
481 
482  end subroutine mosaic_aero_optical
483 
484 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
485 
486 end module pmc_mosaic
The aero_data_t structure and associated subroutines.
Definition: aero_data.F90:9
Physical constants.
Definition: constants.F90:9
The env_state_t structure and associated subroutines.
Definition: env_state.F90:9
subroutine aero_state_num_conc_for_reweight(aero_state, reweight_num_conc)
Save the correct number concentrations for later use by aero_state_reweight().
Definition: aero_state.F90:522
subroutine assert_msg(code, condition_ok, error_msg)
Errors unless condition_ok is true.
Definition: util.F90:76
subroutine mosaic_to_partmc(env_state, aero_data, aero_state, gas_data, gas_state)
Map all data MOSAIC -> PartMC.
Definition: mosaic.F90:271
real(kind=dp) function aero_weight_array_num_conc(aero_weight_array, aero_particle)
Compute the number concentration for a particle (m^{-3}).
The gas_data_t structure and associated subroutines.
Definition: gas_data.F90:9
Interface to the MOSAIC aerosol and gas phase chemistry code.
Definition: mosaic.F90:9
Common utility subroutines.
Definition: util.F90:9
Current environment state.
Definition: env_state.F90:26
logical function mosaic_support()
Whether MOSAIC support is compiled in.
Definition: mosaic.F90:24
The aero_state_t structure and assocated subroutines.
Definition: aero_state.F90:9
subroutine mosaic_timestep(env_state, aero_data, aero_state, gas_data, gas_state, do_optical)
Do one timestep with MOSAIC.
Definition: mosaic.F90:366
real(kind=dp) function deg2rad(deg)
Convert degrees to radians.
Definition: util.F90:1226
The gas_state_t structure and associated subroutines.
Definition: gas_state.F90:9
integer function aero_state_total_particles(aero_state, i_group, i_class)
Returns the total number of particles in an aerosol distribution.
Definition: aero_state.F90:255
subroutine aero_state_reweight(aero_state, reweight_num_conc)
Reweight all particles after their constituent volumes have been altered.
Definition: aero_state.F90:550
The current collection of aerosol particles.
Definition: aero_state.F90:63
Single aerosol particle data structure.
subroutine mosaic_aero_optical(env_state, aero_data, aero_state, gas_data, gas_state)
Compute the optical properties of each aerosol particle. FIXME: currently disabled.
Definition: mosaic.F90:430
integer function get_unit()
Returns an available unit number. This should be freed by free_unit().
Definition: util.F90:147
subroutine mosaic_from_partmc(env_state, aero_data, aero_state, gas_data, gas_state)
Map all data PartMC -> MOSAIC.
Definition: mosaic.F90:146
subroutine mosaic_init(env_state, aero_data, del_t, do_optical)
Initialize all MOSAIC data-structures.
Definition: mosaic.F90:37
Current state of the gas mixing ratios in the system.
Definition: gas_state.F90:26
subroutine mosaic_cleanup()
Clean-up after running MOSAIC, deallocating memory.
Definition: mosaic.F90:129
Constant gas data.
Definition: gas_data.F90:29
Aerosol material properties and associated data.
Definition: aero_data.F90:40