PartMC  2.9.0
scenario.F90
Go to the documentation of this file.
1 ! Copyright (C) 2005-2016 Nicole Riemer and 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_scenario module.
7 
8 !> The scenario_t structure and associated subroutines.
10 
11  use pmc_gas_state
12  use pmc_aero_dist
13  use pmc_util
14  use pmc_env_state
15  use pmc_aero_state
16  use pmc_spec_file
17  use pmc_aero_data
18  use pmc_gas_data
19  use pmc_chamber
20  use pmc_mpi
21 #ifdef PMC_USE_MPI
22  use mpi
23 #endif
24 #ifdef PMC_USE_QUADPACK
25  use quadpack_double, only: dqagse
26 #endif
27 
28  !> Type code for an undefined or invalid loss function.
29  integer, parameter :: scenario_loss_function_invalid = 0
30  !> Type code for a zero loss function.
31  integer, parameter :: scenario_loss_function_none = 1
32  !> Type code for a constant loss function.
33  integer, parameter :: scenario_loss_function_constant = 2
34  !> Type code for a loss rate function proportional to particle volume.
35  integer, parameter :: scenario_loss_function_volume = 3
36  !> Type code for a loss rate function based on dry deposition
37  integer, parameter :: scenario_loss_function_drydep = 4
38  !> Type code for a loss rate function for chamber experiments.
39  integer, parameter :: scenario_loss_function_chamber = 5
40 
41  !> Parameter to switch between algorithms for particle loss.
42  !! A value of 0 will always use the naive algorithm, and
43  !! a value of 1 will always use the accept-reject algorithm.
44  real(kind=dp), parameter :: scenario_loss_alg_threshold = 1.0d0
45 
46  !> Parameters for simulating dry deposition
48  !> Reference height
49  real(kind=dp) :: z_ref = 20.0d0
50  !> Mean wind speed at reference height
51  real(kind=dp) :: u_mean = 5.0d0
52  !> Roughness length (land-use category dependent)
53  ! crops, mixed farming (LUC 7 from Zhang et al., 2001)
54  real(kind=dp) :: z_rough = 0.8d0
55  !> Characteristic radius of collectors (land-use category dependent)
56  real(kind=dp) :: a = 2.0d0 / 1000.0d0
57  !> Impaction parameter
58  real(kind=dp) :: alpha = 1.0d0
59  !> Surface resistance parameter
60  real(kind=dp) :: eps_0 = 3.0d0
61  !> Diffusivity paramter
62  real(kind=dp) :: gamma = 0.56d0 ! LUC-dependent for Zhang et al., 2001
63  !> Brownian diffusion coefficient
64  real(kind=dp) :: c_b = 1.0d0
65  !> Interception coefficient
66  real(kind=dp) :: c_in = 0.5d0
67  !> Impaction coefficient
68  real(kind=dp) :: c_im = 1.0d0
69  !> Interception exponent
70  real(kind=dp) :: nu = 2.0d0
71  !> Impaction exponent
72  real(kind=dp) :: beta = 2.0d0
73  end type drydep_params_t
74 
75  !> Scenario data.
76  !!
77  !! This is everything needed to drive the scenario being simulated.
78  !!
79  !! The temperature, pressure, emissions and background states are profiles
80  !! prescribed as functions of time by giving a number of times and
81  !! the corresponding data. Simple data such as temperature and pressure is
82  !! linearly interpolated between times, with constant interpolation
83  !! outside of the range of times. Gases and aerosols are
84  !! interpolated with gas_state_interp_1d() and
85  !! aero_dist_interp_1d(), respectively.
87  !> Temperature set-point times (s).
88  real(kind=dp), allocatable :: temp_time(:)
89  !> Temperatures at set-points (K).
90  real(kind=dp), allocatable :: temp(:)
91 
92  !> Pressure set-point times (s).
93  real(kind=dp), allocatable :: pressure_time(:)
94  !> Pressures at set-points (Pa).
95  real(kind=dp), allocatable :: pressure(:)
96 
97  !> Height set-point times (s).
98  real(kind=dp), allocatable :: height_time(:)
99  !> Heights at set-points (m).
100  real(kind=dp), allocatable :: height(:)
101 
102  !> Gas emission set-point times (s).
103  real(kind=dp), allocatable :: gas_emission_time(:)
104  !> Gas emisssion rate scales at set-points (1).
105  real(kind=dp), allocatable :: gas_emission_rate_scale(:)
106  !> Gas emission rates at set-points (mol m^{-2} s^{-1}).
107  type(gas_state_t), allocatable :: gas_emission(:)
108 
109  !> Gas-background dilution set-point times (s).
110  real(kind=dp), allocatable :: gas_dilution_time(:)
111  !> Gas-background dilution rates at set-points (s^{-1}).
112  real(kind=dp), allocatable :: gas_dilution_rate(:)
113  !> Background gas mixing ratios at set-points (ppb).
114  type(gas_state_t), allocatable :: gas_background(:)
115 
116  !> Aerosol emission set-points times (s).
117  real(kind=dp), allocatable :: aero_emission_time(:)
118  !> Aerosol emission rate scales at set-points (1).
119  real(kind=dp), allocatable :: aero_emission_rate_scale(:)
120  !> Aerosol emissions at set-points (# m^{-2} s^{-1}).
121  type(aero_dist_t), allocatable :: aero_emission(:)
122 
123  !> Aerosol-background dilution set-point times (s).
124  real(kind=dp), allocatable :: aero_dilution_time(:)
125  !> Aerosol-background dilution rates at set-points (s^{-1}).
126  real(kind=dp), allocatable :: aero_dilution_rate(:)
127  !> Aerosol background at set-points (# m^{-3}).
128  type(aero_dist_t), allocatable :: aero_background(:)
129 
130  !> Type of loss rate function.
131  integer :: loss_function_type
132  !> Dry deposition parameters
133  type(drydep_params_t) :: drydep
134  !> Chamber parameters for wall loss and sedimentation.
135  type(chamber_t) :: chamber
136  end type scenario_t
137 
138 contains
139 
140 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
141 
142  !> Initialize the time-dependent contents of the
143  !> environment. Thereafter scenario_update_env_state() should be used.
144  subroutine scenario_init_env_state(scenario, env_state, time)
145 
146  !> Scenario data.
147  type(scenario_t), intent(in) :: scenario
148  !> Environment state to update.
149  type(env_state_t), intent(inout) :: env_state
150  !> Current time (s).
151  real(kind=dp), intent(in) :: time
152 
153  env_state%temp = interp_1d(scenario%temp_time, scenario%temp, time)
154  env_state%pressure = interp_1d(scenario%pressure_time, &
155  scenario%pressure, time)
156  env_state%height = interp_1d(scenario%height_time, scenario%height, time)
157  env_state%elapsed_time = time
158  ! FIXME: should compute this at some point
159  env_state%solar_zenith_angle = 0d0
160 
161  end subroutine scenario_init_env_state
162 
163 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
164 
165  !> Update time-dependent contents of the environment.
166  !> scenario_init_env_state() should have been called at the start.
167  subroutine scenario_update_env_state(scenario, env_state, time)
168 
169  !> Scenario data.
170  type(scenario_t), intent(in) :: scenario
171  !> Environment state to update.
172  type(env_state_t), intent(inout) :: env_state
173  !> Current time (s).
174  real(kind=dp), intent(in) :: time
175 
176  !> Ambient water vapor pressure (Pa).
177  real(kind=dp) :: pmv_old, pmv_new
178  !> Ambient pressure (Pa)
179  real(kind=dp) :: pressure_old
180  !> Ambient temperature (K)
181  real(kind=dp) :: temp_old
182 
183  ! Update temperature and pressure and adjust relative humidity to maintain
184  ! water mixing ratio.
185 
186  pmv_old = env_state_sat_vapor_pressure(env_state) * env_state%rel_humid
187  pressure_old = env_state%pressure
188  temp_old = env_state%temp
189 
190  env_state%temp = interp_1d(scenario%temp_time, scenario%temp, time)
191  env_state%pressure = interp_1d(scenario%pressure_time, &
192  scenario%pressure, time)
193 
194  pmv_new = pmv_old * env_state%pressure / pressure_old
195  env_state%rel_humid = pmv_new / env_state_sat_vapor_pressure(env_state)
196 
197  env_state%height = interp_1d(scenario%height_time, scenario%height, time)
198  env_state%elapsed_time = time
199 
200  end subroutine scenario_update_env_state
201 
202 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
203 
204  !> Do gas emissions and background dilution.
205  !!
206  !! Emissions are given as an areal rate \f$e(t)\f$, and then divided
207  !! by the current box height \f$h(t)\f$ to obtain a volume
208  !! rate. There is also a dimensionless rate scaling \f$r(t)\f$. All
209  !! input functions are asusumed constant over the timestep, so the
210  !! concentration \f$c(t)\f$ change is given by
211  !! \f[
212  !! c(t) = c(0) + \frac{r t}{h} e.
213  !! \f]
214  !!
215  !! We model dilution by considering a gas concentration \f$c(t)\f$
216  !! in a box of height \f$h(t)\f$, subject to first-order dilution
217  !! with a rate \f$\lambda(t)\f$. Then the effective dilution rate is
218  !! \f[
219  !! \lambda_{\rm eff}(t) = \lambda(t) + \frac{\dot{h}(t)}{h(t)}
220  !! \f]
221  !! and the evolution of \f$c(t)\f$ is given by
222  !! \f[
223  !! \dot{c}(t) = - \lambda_{\rm eff}(t) c(t).
224  !! \f]
225  !! Solving this with separation of variables gives
226  !! \f[
227  !! \frac{c(t)}{c(0)} = \frac{h(0)}{h(t)}
228  !! \exp\left( - \int_0^t \lambda(t)\,dt\right).
229  !! \f]
230  !! If we define \f$p = c(t)/c(0)\f$ to be the remaining proportion
231  !! of the initial concentration, and \f$b\f$ to be the constant
232  !! background concentration, then we have
233  !! \f[
234  !! c(t) = p(t) c(0) + (1 - p(t)) b.
235  !! \f]
236  !! We assume constant \f$\lambda\f$ and we only do entrainment with
237  !! increasing height \f$h(t)\f$, so we have
238  !! \f[
239  !! p(t) = \min\left(1, \frac{h(0)}{h(t)}\right) \exp(-\lambda t).
240  !! \f]
241  subroutine scenario_update_gas_state(scenario, delta_t, env_state, &
242  old_env_state, gas_data, gas_state)
243 
244  !> Scenario data.
245  type(scenario_t), intent(in) :: scenario
246  !> Time increment to update over.
247  real(kind=dp), intent(in) :: delta_t
248  !> Current environment.
249  type(env_state_t), intent(in) :: env_state
250  !> Previous environment.
251  type(env_state_t), intent(in) :: old_env_state
252  !> Gas data values.
253  type(gas_data_t), intent(in) :: gas_data
254  !> Gas state to update.
255  type(gas_state_t), intent(inout) :: gas_state
256 
257  real(kind=dp) :: emission_rate_scale, dilution_rate, p
258  type(gas_state_t) :: emissions, background
259 
260  ! emissions
261  if (size(scenario%gas_emission) > 0) then
262  call gas_state_interp_1d(scenario%gas_emission, &
263  scenario%gas_emission_time, scenario%gas_emission_rate_scale, &
264  env_state%elapsed_time, emissions, emission_rate_scale)
265  call gas_state_molar_conc_to_ppb(emissions, env_state)
266  p = emission_rate_scale * delta_t / env_state%height
267  call gas_state_add_scaled(gas_state, emissions, p)
268  end if
269 #ifndef PMC_USE_WRF
270  ! dilution
271  call gas_state_interp_1d(scenario%gas_background, &
272  scenario%gas_dilution_time, scenario%gas_dilution_rate, &
273  env_state%elapsed_time, background, dilution_rate)
274  p = exp(- dilution_rate * delta_t)
275  if (env_state%height > old_env_state%height) then
276  p = p * old_env_state%height / env_state%height
277  end if
278  call gas_state_scale(gas_state, p)
279  call gas_state_add_scaled(gas_state, background, 1d0 - p)
280 #endif
281  call gas_state_ensure_nonnegative(gas_state)
282 
283  end subroutine scenario_update_gas_state
284 
285 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
286 
287  !> Do emissions and background dilution for a particle aerosol
288  !> distribution.
289  !!
290  !! See scenario_update_gas_state() for a description of the
291  !! model. We additionally scale the number concentration to account
292  !! for temperature changes.
293  subroutine scenario_update_aero_state(scenario, delta_t, env_state, &
294  old_env_state, aero_data, aero_state, n_emit, n_dil_in, n_dil_out, &
295  allow_doubling, allow_halving)
296 
297  !> Scenario data.
298  type(scenario_t), intent(in) :: scenario
299  !> Time increment to update over.
300  real(kind=dp), intent(in) :: delta_t
301  !> Current environment.
302  type(env_state_t), intent(in) :: env_state
303  !> Previous environment.
304  type(env_state_t), intent(in) :: old_env_state
305  !> Aero data values.
306  type(aero_data_t), intent(in) :: aero_data
307  !> Aero state to update.
308  type(aero_state_t), intent(inout) :: aero_state
309  !> Number of emitted particles.
310  integer, intent(out) :: n_emit
311  !> Number of diluted-in particles.
312  integer, intent(out) :: n_dil_in
313  !> Number of diluted-out particles.
314  integer, intent(out) :: n_dil_out
315  !> Whether to allow doubling of the population.
316  logical, intent(in) :: allow_doubling
317  !> Whether to allow halving of the population.
318  logical, intent(in) :: allow_halving
319 
320  real(kind=dp), parameter :: sample_timescale = 3600.0d0
321  real(kind=dp) :: characteristic_factor, sample_timescale_effective
322  real(kind=dp) :: emission_rate_scale, dilution_rate, p
323  type(aero_dist_t) :: emissions, background
324  type(aero_state_t) :: aero_state_delta
325 
326  sample_timescale_effective = max(1.0, min(sample_timescale, &
327  env_state%elapsed_time))
328  characteristic_factor = sample_timescale_effective / delta_t
329 
330  ! emissions
331  if (size(scenario%aero_emission) > 0) then
332  call aero_dist_interp_1d(scenario%aero_emission, &
333  scenario%aero_emission_time, scenario%aero_emission_rate_scale, &
334  env_state%elapsed_time, emissions, emission_rate_scale)
335  p = emission_rate_scale * delta_t / env_state%height
336  call aero_state_add_aero_dist_sample(aero_state, aero_data, &
337  emissions, p, characteristic_factor, env_state%elapsed_time, &
338  allow_doubling, allow_halving, n_emit)
339  end if
340 #ifndef PMC_USE_WRF
341  ! dilution
342  call aero_dist_interp_1d(scenario%aero_background, &
343  scenario%aero_dilution_time, scenario%aero_dilution_rate, &
344  env_state%elapsed_time, background, dilution_rate)
345  p = exp(- dilution_rate * delta_t)
346  if (env_state%height > old_env_state%height) then
347  p = p * old_env_state%height / env_state%height
348  end if
349  ! loss to background
350  call aero_state_zero(aero_state_delta)
351  call aero_state_copy_weight(aero_state, aero_state_delta)
352  call aero_state_sample_particles(aero_state, aero_state_delta, &
353  aero_data, 1d0 - p, aero_info_dilution)
354  n_dil_out = aero_state_total_particles(aero_state_delta)
355  ! addition from background
356  call aero_state_add_aero_dist_sample(aero_state, aero_data, &
357  background, 1d0 - p, characteristic_factor, env_state%elapsed_time, &
358  allow_doubling, allow_halving, n_dil_in)
359 
360  ! particle loss function
361  call scenario_particle_loss(scenario, delta_t, aero_data, aero_state, &
362  env_state)
363 #endif
364 
365 #ifdef PMC_USE_WRF
366  call aero_weight_array_scale(aero_state%awa, &
367  old_env_state%inverse_density * (1.0d0 / env_state%inverse_density))
368 #else
369  ! update computational volume
370  call aero_weight_array_scale(aero_state%awa, &
371  old_env_state%temp * env_state%pressure &
372  / (env_state%temp * old_env_state%pressure))
373 #endif
374 
375  end subroutine scenario_update_aero_state
376 
377 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
378 
379  !> Do emissions and background dilution from the environment for a
380  !> binned aerosol distribution.
381  !!
382  !! See scenario_update_gas_state() for a description of the model.
383  subroutine scenario_update_aero_binned(scenario, delta_t, env_state, &
384  old_env_state, bin_grid, aero_data, aero_binned)
385 
386  !> Scenario data.
387  type(scenario_t), intent(in) :: scenario
388  !> Time increment to update over.
389  real(kind=dp), intent(in) :: delta_t
390  !> Current environment.
391  type(env_state_t), intent(in) :: env_state
392  !> Previous environment.
393  type(env_state_t), intent(in) :: old_env_state
394  !> Bin grid.
395  type(bin_grid_t), intent(in) :: bin_grid
396  !> Aero data values.
397  type(aero_data_t), intent(in) :: aero_data
398  !> Aero binned to update.
399  type(aero_binned_t), intent(inout) :: aero_binned
400 
401  real(kind=dp) :: emission_rate_scale, dilution_rate, p
402  type(aero_dist_t) :: emissions, background
403  type(aero_binned_t) :: emissions_binned, background_binned
404 
405  ! emissions
406  call aero_dist_interp_1d(scenario%aero_emission, &
407  scenario%aero_emission_time, scenario%aero_emission_rate_scale, &
408  env_state%elapsed_time, emissions, emission_rate_scale)
409  call aero_binned_add_aero_dist(emissions_binned, bin_grid, aero_data, &
410  emissions)
411  p = emission_rate_scale * delta_t / env_state%height
412  call aero_binned_add_scaled(aero_binned, emissions_binned, p)
413 
414  ! dilution
415  call aero_dist_interp_1d(scenario%aero_background, &
416  scenario%aero_dilution_time, scenario%aero_dilution_rate, &
417  env_state%elapsed_time, background, dilution_rate)
418  call aero_binned_add_aero_dist(background_binned, bin_grid, aero_data, &
419  background)
420  p = exp(- dilution_rate * delta_t)
421  if (env_state%height > old_env_state%height) then
422  p = p * old_env_state%height / env_state%height
423  end if
424  call aero_binned_scale(aero_binned, p)
425  call aero_binned_add_scaled(aero_binned, background_binned, 1d0 - p)
426 
427  end subroutine scenario_update_aero_binned
428 
429 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
430 
431  !> Update the modal aerosol distribution to account for particle loss.
432  subroutine scenario_update_aero_modes(aero_dist, del_t, env_state, &
433  density, scenario)
434 
435  !> Aerosol distribution.
436  type(aero_dist_t), intent(inout) :: aero_dist
437  !> Timestep.
438  real(kind=dp), intent(in) :: del_t
439  !> Environment state.
440  type(env_state_t), intent(in) :: env_state
441  !> Particle density (kg m^-3), assumed uniform across all modes.
442  real(kind=dp), intent(in) :: density
443  !> Scenario
444  type(scenario_t), intent(in) :: scenario
445 
446  real(kind=dp) :: n, d_pg, ln_sigma_g
447  real(kind=dp) :: m_0_rate, m_3_rate
448  real(kind=dp) :: m, new_m
449  real(kind=dp) :: new_n, new_d_pg
450  integer :: i_mode
451 
452  if (scenario%loss_function_type == scenario_loss_function_invalid) then
453  return
454  else if (scenario%loss_function_type == scenario_loss_function_none) then
455  return
456  else if (scenario%loss_function_type == &
458  return
459  else if (scenario%loss_function_type == scenario_loss_function_drydep) then
460  do i_mode = 1,aero_dist_n_mode(aero_dist)
461  n = aero_dist%mode(i_mode)%num_conc
462 
463  if (n == 0d0) cycle
464 
465  d_pg = aero_dist%mode(i_mode)%char_radius * 2.0d0
466  ln_sigma_g = aero_dist%mode(i_mode)%log10_std_dev_radius &
467  / log10(exp(1.0d0))
468 
469  ! Integrated deposition rate for the 0-th moment (num. conc.)
470  m_0_rate = -1.0d0 * scenario_integrated_loss_rate_drydep(scenario, &
471  aero_dist%mode(i_mode), 0.0d0, density, env_state)
472  new_n = n * exp(m_0_rate * del_t)
473 
474  aero_dist%mode(i_mode)%num_conc = new_n
475 
476  ! Integrated deposition rate for the 3-rd moment
477  ! (proportional to volume conc.)
478  m_3_rate = -1.0d0 * scenario_integrated_loss_rate_drydep(scenario, &
479  aero_dist%mode(i_mode), 3.0d0, density, env_state)
480  m = n * d_pg**3.0d0 * exp((3.0d0**2.0d0) / 2.0d0 &
481  * (ln_sigma_g**2.0d0))
482  new_m = m * exp(m_3_rate * del_t)
483 
484  ! New geometric mean diameter
485  new_d_pg = (new_m / new_n * exp(-(3.0d0**2.0d0) / 2.0d0 &
486  * (ln_sigma_g**2.0d0)))**(1.0d0/3.0d0)
487  aero_dist%mode(i_mode)%char_radius = new_d_pg / 2.0d0
488  end do
489  end if
490 
491  end subroutine scenario_update_aero_modes
492 
493 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
494 
495  !> Evaluate a loss rate function.
496  real(kind=dp) function scenario_loss_rate(scenario, vol, density, &
497  aero_data, env_state)
498 
499  !> Scenario data.
500  type(scenario_t), intent(in) :: scenario
501  !> Volume of particle (m^3).
502  real(kind=dp), intent(in) :: vol
503  !> Density of particle (kg/m^3).
504  real(kind=dp), intent(in) :: density
505  !> Aerosol data.
506  type(aero_data_t), intent(in) :: aero_data
507  !> Environment state.
508  type(env_state_t), intent(in) :: env_state
509 
510  scenario_loss_rate = 0d0
511  if (scenario%loss_function_type == scenario_loss_function_invalid) then
512  scenario_loss_rate = 0d0
513  else if (scenario%loss_function_type == scenario_loss_function_none) then
514  scenario_loss_rate = 0d0
515  else if (scenario%loss_function_type == scenario_loss_function_constant) &
516  then
517  scenario_loss_rate = 1d-3
518  else if (scenario%loss_function_type == scenario_loss_function_volume) then
519  scenario_loss_rate = 1d15*vol
520  else if (scenario%loss_function_type == scenario_loss_function_drydep) &
521  then
523  aero_data, env_state, scenario)
524  else if (scenario%loss_function_type == scenario_loss_function_chamber) &
525  then
526  scenario_loss_rate = chamber_loss_rate_wall(scenario%chamber, vol, &
527  aero_data, env_state) &
528  + chamber_loss_rate_sedi(scenario%chamber, vol, density, &
529  aero_data, env_state)
530  else
531  call die_msg(201594391, "Unknown loss function id: " &
532  // trim(integer_to_string(scenario%loss_function_type)))
533  end if
534 
535  end function scenario_loss_rate
536 
537 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
538 
539  !> Compute and return the dry deposition rate for a given particle.
540  !! All equations used here are written in detail in the file
541  !! \c doc/deposition/deposition.tex.
542  real(kind=dp) function scenario_loss_rate_drydep(vol, density, aero_data, &
543  env_state, scenario)
544 
545  !> Particle volume (m^3).
546  real(kind=dp), intent(in) :: vol
547  !> Particle density (kg m^-3).
548  real(kind=dp), intent(in) :: density
549  !> Aerosol data.
550  type(aero_data_t), intent(in) :: aero_data
551  !> Environment state.
552  type(env_state_t), intent(in) :: env_state
553  !> Scenario data.
554  type(scenario_t), intent(in) :: scenario
555 
556  real(kind=dp) :: v_d
557  real(kind=dp) :: v_s
558  real(kind=dp) :: d_p
559  real(kind=dp) :: density_air
560  real(kind=dp) :: visc_d, visc_k
561  real(kind=dp) :: gas_speed, gas_mean_free_path
562  real(kind=dp) :: knud, cunning
563  real(kind=dp) :: grav
564  real(kind=dp) :: r_s, r_a
565  real(kind=dp) :: diff_p
566  real(kind=dp) :: von_karman
567  real(kind=dp) :: st, sc, u_star
568  real(kind=dp) :: e_b, e_im, e_in, r1
569  type(drydep_params_t) :: drydep_params
570 
571  drydep_params = scenario%drydep
572 
573  ! particle diameter
574  d_p = aero_data_vol2diam(aero_data, vol)
575  ! density of air
576  density_air = (const%air_molec_weight * env_state%pressure) &
577  / (const%univ_gas_const * env_state%temp)
578  ! dynamic viscosity
579  visc_d = 1.8325d-5 * (416.16 / (env_state%temp + 120.0d0)) &
580  * (env_state%temp / 296.16)**1.5d0
581  ! kinematic viscosity
582  visc_k = visc_d / density_air
583  ! gas speed
584  gas_speed = sqrt((8.0d0 * const%boltzmann * env_state%temp &
585  * const%avagadro) / (const%pi * const%air_molec_weight))
586  ! gas free path
587  gas_mean_free_path = (2.0d0 * visc_d) / (density_air * gas_speed)
588  ! knudson number
589  knud = (2.0d0 * gas_mean_free_path) / d_p
590  ! cunningham correction factor
591  cunning = 1.0d0 + knud * (1.257d0 + 0.4d0 * exp(-1.1d0 / knud))
592  ! gravity
593  grav = 9.81d0
594  ! Compute V_s
595  v_s = (density * d_p**2.0d0 * grav * cunning) / (18.0d0 * visc_d)
596 
597  ! Aerodynamic resistance
598  ! For neutral stability
599  u_star = .4d0 * drydep_params%u_mean &
600  / log(drydep_params%z_ref / drydep_params%z_rough)
601  r_a = (1.0d0 / (.4d0 * u_star)) &
602  * log(drydep_params%z_ref / drydep_params%z_rough)
603  ! Brownian diffusion efficiency
604  diff_p = (const%boltzmann * env_state%temp * cunning) &
605  / (3.d0 * const%pi * visc_d * d_p)
606  sc = visc_k / diff_p
607  e_b = drydep_params%C_B * sc**(-drydep_params%gamma)
608 
609  ! Interception efficiency
610  ! Characteristic radius of large collectors
611  e_in = drydep_params%C_IN * (d_p / drydep_params%A)**drydep_params%nu
612 
613  ! Impaction efficiency
614  st = (v_s * u_star) / (grav * drydep_params%A)
615  e_im = drydep_params%C_IM * (st &
616  / (drydep_params%alpha + st))**drydep_params%beta
617 
618  ! Rebound correction
619  r1 = exp(-st**.5d0)
620 
621  ! Surface resistance
622  r_s = 1.0d0 / (drydep_params%eps_0 * u_star * (e_b + e_in + e_im) * r1)
623 
624  ! Dry deposition
625  v_d = v_s + (1.0d0 / (r_a + r_s + r_a * r_s * v_s))
626 
627  ! The loss rate
628  scenario_loss_rate_drydep = v_d / env_state%height
629 
630  end function scenario_loss_rate_drydep
631 
632 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
633 
634  !> Compute and return the integrated dry deposition rate for a given
635  !> lognormal aerosol mode (modal approximation).
636  real(kind=dp) function scenario_integrated_loss_rate_drydep(scenario, &
637  aero_mode, moment, density, env_state)
638 
639  !> Scenario data.
640  type(scenario_t), intent(in) :: scenario
641  !> Aerosol mode.
642  type(aero_mode_t), intent(in) :: aero_mode
643  !> Moment to calculate loss rate for.
644  real(kind=dp), intent(in) :: moment
645  !> Particle density assumed for entire mode (kg m^-3).
646  real(kind=dp), intent(in) :: density
647  !> Environment state.
648  type(env_state_t), intent(in) :: env_state
649 
650  real(kind=dp) :: v_d_hat, v_g_hat
651  real(kind=dp) :: v_g_bar
652  real(kind=dp) :: d_pg, ln_sigma_g
653  real(kind=dp) :: density_air
654  real(kind=dp) :: visc_d, visc_k
655  real(kind=dp) :: gas_speed, gas_mean_free_path
656  real(kind=dp) :: knud
657  real(kind=dp) :: d_bar, d_hat
658  real(kind=dp) :: st, sc
659  real(kind=dp) :: u_star
660  real(kind=dp) :: r_a, r_s
661  real(kind=dp) :: e_b, e_in, e_im, r1
662  type(drydep_params_t) :: drydep_params
663 
664 #ifdef PMC_USE_QUADPACK
665  ! Do numerical integration when QUADPACK is available
667  scenario_integrated_loss_rate_drydep_quadpack( &
668  scenario, aero_mode, moment, density, env_state)
669  return
670 #endif
671 
672  drydep_params = scenario%drydep
673 
674  ! particle diameter equal to geometric mean diameter
675  d_pg = aero_mode%char_radius * 2.0d0
676  ! natural log of geometric standard deviation
677  ln_sigma_g = aero_mode%log10_std_dev_radius / log10(exp(1.0d0))
678  ! density of air
679  density_air = (const%air_molec_weight * env_state%pressure) &
680  / (const%univ_gas_const * env_state%temp)
681  ! dynamic viscosity
682  visc_d = 1.8325d-5 * (416.16 / (env_state%temp + 120.0d0)) &
683  * (env_state%temp / 296.16)**1.5d0
684  ! kinematic viscosity
685  visc_k = visc_d / density_air
686  ! gas speed
687  gas_speed = sqrt((8.0d0 * const%boltzmann * env_state%temp &
688  * const%avagadro) / (const%pi * const%air_molec_weight))
689  ! gas mean free path
690  gas_mean_free_path = (2.0d0 * visc_d) / (density_air * gas_speed)
691  ! Knudsen number
692  knud = (2.0d0 * gas_mean_free_path) / d_pg
693  ! Settling velocity
694  v_g_bar = (density * d_pg**2.0d0 * const%std_grav) / (18.0d0 * visc_d)
695  ! Compute integrated settling velocity
696  v_g_hat = v_g_bar &
697  * (exp((4.0d0 * moment + 4.0d0) / 2.0d0 * ln_sigma_g**2.0d0) &
698  + 1.246d0 * knud &
699  * exp((2.0d0 * moment + 1.0d0) / 2.0d0 * ln_sigma_g**2.0d0))
700 
701  ! Aerodynamic resistance (assuming neutral stability)
702  u_star = 0.4d0 * drydep_params%u_mean / log(drydep_params%z_ref &
703  / drydep_params%z_rough)
704  r_a = (1.0d0 / (0.4d0 * u_star)) &
705  * log(drydep_params%z_ref / drydep_params%z_rough)
706 
707  ! Brownian diffusivity
708  d_bar = (const%boltzmann * env_state%temp) &
709  / (3.0d0 * const%pi * visc_d * d_pg)
710  ! Compute integrated Brownian diffusivity
711  d_hat = d_bar &
712  * ((exp((-2.0d0 * moment + 1.0d0) / 2.0d0 * ln_sigma_g**2.0d0) &
713  + 1.246d0 * knud &
714  * exp((-4.0d0 * moment + 4.0d0) / 2.0d0 * ln_sigma_g**2.0d0)))
715  ! Schmidt number based on integrated diffusivity
716  sc = visc_k / d_hat
717  ! Collection efficiency due to Brownian diffusion
718  e_b = drydep_params%C_B * sc**(-drydep_params%gamma)
719 
720  ! Collection efficiency due to interception
721  e_in = drydep_params%C_IN * (d_pg / drydep_params%A)**drydep_params%nu
722 
723  ! Stokes number based on integrated settling velocity
724  st = (v_g_hat * u_star) / (const%std_grav * drydep_params%A)
725  ! Collection efficiency due to impaction
726  e_im = drydep_params%C_IM * (st / (drydep_params%alpha + st)) &
727  **drydep_params%beta
728 
729  ! Rebound correction
730  r1 = exp(-st**0.5d0)
731 
732  ! Surface resistance
733  r_s = 1.0d0 / (drydep_params%eps_0 * u_star * (e_b + e_in + e_im) * r1)
734 
735  ! Integrated deposition velocity
736  v_d_hat = v_g_hat + (1.0d0 / (r_a + r_s + r_a * r_s * v_g_hat))
737 
738  ! Loss rate
739  scenario_integrated_loss_rate_drydep = v_d_hat / env_state%height
740 
742 
743 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
744 
745 #ifdef PMC_USE_QUADPACK
746  real(kind=dp) function scenario_integrated_loss_rate_drydep_quadpack( &
747  scenario, aero_mode, moment, density, env_state)
748 
749  !> Scenario data.
750  type(scenario_t), intent(in) :: scenario
751  !> Aerosol mode.
752  type(aero_mode_t), intent(in) :: aero_mode
753  !> Moment to calculate loss rate for.
754  real(kind=dp), intent(in) :: moment
755  !> Particle density assumed for entire mode (kg m^-3).
756  real(kind=dp), intent(in) :: density
757  !> Environment state.
758  type(env_state_t), intent(in) :: env_state
759 
760  real(kind=dp) :: d_pg, ln_sigma_g
761  real(kind=dp) :: density_air
762  real(kind=dp) :: visc_d, visc_k
763  real(kind=dp) :: gas_speed, gas_mean_free_path
764  real(kind=dp) :: m_k
765  real(kind=dp) :: lower, upper, epsabs, epsrel, result, abserr, ref
766  integer :: limit, neval, ier, last
767  real(kind=dp), allocatable :: alist(:), blist(:), rlist(:), elist(:)
768  integer, allocatable :: iord(:)
769 
770  type(drydep_params_t) :: drydep_params
771 
772  drydep_params = scenario%drydep
773 
774  ! particle diameter equal to geometric mean diameter
775  d_pg = aero_mode%char_radius * 2.0d0
776  ! natural log of geometric standard deviation
777  ln_sigma_g = aero_mode%log10_std_dev_radius / log10(exp(1.0d0))
778  ! density of air
779  density_air = (const%air_molec_weight * env_state%pressure) &
780  / (const%univ_gas_const * env_state%temp)
781  ! dynamic viscosity
782  visc_d = 1.8325d-5 * (416.16 / (env_state%temp + 120.0d0)) &
783  * (env_state%temp / 296.16)**1.5d0
784  ! kinematic viscosity
785  visc_k = visc_d / density_air
786  ! gas speed
787  gas_speed = sqrt((8.0d0 * const%boltzmann * env_state%temp &
788  * const%avagadro) / (const%pi * const%air_molec_weight))
789  ! gas mean free path
790  gas_mean_free_path = (2.0d0 * visc_d) / (density_air * gas_speed)
791 
792  ref = exp(log(d_pg) + moment*ln_sigma_g**2)
793  lower = ref / (exp(ln_sigma_g)*10.0)
794  upper = ref * exp(ln_sigma_g)*10.0
795 
796  ! Set integration parameters
797  limit = 1000
798  epsabs = 1.0d-10
799  epsrel = 1.0d-6
800 
801  ! Allocate arrays for QUADPACK integration
802  allocate(alist(limit), blist(limit), rlist(limit), &
803  elist(limit), iord(limit))
804 
805  ! Call QUADPACK integration routine
806  call dqagse(dep_vel_integrand, lower, upper, epsabs, epsrel, limit, &
807  result, abserr, neval, ier, alist, blist, rlist, elist, iord, last)
808 
809  call assert_msg(909106718, ier == 0, &
810  "QUADPACK integration failed, error code: " &
811  // trim(integer_to_string(ier)))
812 
813  m_k = d_pg**moment * exp(moment**2 * ln_sigma_g**2 / 2.0d0)
814 
815  ! Integration result
816  scenario_integrated_loss_rate_drydep_quadpack = 1.0d0 / m_k * result &
817  / env_state%height
818 
819  ! Clean up
820  deallocate(alist, blist, rlist, elist, iord)
821 
822  contains
823 
824  real(kind=dp) function dep_vel_integrand(d_p)
825  real(kind=dp), intent(in) :: d_p
826 
827  ! Local variables
828  real(kind=dp) :: v_d, v_s
829  real(kind=dp) :: knud_local, cunning
830  real(kind=dp) :: diff_p, sc, st
831  real(kind=dp) :: u_star, r_a, r_s
832  real(kind=dp) :: e_b, e_in, e_im, r1
833  real(kind=dp) :: ln_dp, ln_dp_g, n_ddp
834 
835  ! Knudsen number for this diameter
836  knud_local = (2.0d0 * gas_mean_free_path) / d_p
837 
838  ! Cunningham correction factor
839  cunning = 1.0d0 + knud_local * (1.257d0 + 0.4d0 * exp(-1.1d0 / knud_local))
840 
841  ! Settling velocity
842  v_s = (density * d_p**2.0d0 * const%std_grav * cunning) / (18.0d0 * visc_d)
843 
844  ! Friction velocity and aerodynamic resistance
845  u_star = 0.4d0 * drydep_params%u_mean &
846  / log(drydep_params%z_ref / drydep_params%z_rough)
847  r_a = (1.0d0 / (0.4d0 * u_star)) &
848  * log(drydep_params%z_ref / drydep_params%z_rough)
849 
850  ! Particle diffusivity and Schmidt number
851  diff_p = (const%boltzmann * env_state%temp * cunning) &
852  / (3.0d0 * const%pi * visc_d * d_p)
853  sc = visc_k / diff_p
854 
855  ! Collection efficiencies
856  e_b = drydep_params%C_B * sc**(-drydep_params%gamma)
857  e_in = drydep_params%C_IN * (d_p / drydep_params%A)**drydep_params%nu
858 
859  ! Stokes number and impaction efficiency
860  st = (v_s * u_star) / (const%std_grav * drydep_params%A)
861  e_im = drydep_params%C_IM &
862  * (st / (drydep_params%alpha + st))**drydep_params%beta
863 
864  ! Rebound correction
865  r1 = exp(-st**0.5d0)
866 
867  ! Surface resistance
868  r_s = 1.0d0 / (drydep_params%eps_0 * u_star * (e_b + e_in + e_im) * r1)
869 
870  ! Deposition velocity
871  v_d = v_s + (1.0d0 / (r_a + r_s + r_a * r_s * v_s))
872 
873  ! Log-normal size distribution
874  ln_dp = log(d_p)
875  ln_dp_g = log(d_pg)
876  n_ddp = (1.0d0/(sqrt(2.0d0 * const%pi) * d_p * ln_sigma_g)) &
877  * exp(-((ln_dp - ln_dp_g)**2) / (2.0d0 * ln_sigma_g**2))
878 
879  ! Final integrand
880  dep_vel_integrand = d_p**moment * v_d * n_ddp
881  end function dep_vel_integrand
882  end function scenario_integrated_loss_rate_drydep_quadpack
883 #endif
884 
885 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
886 
887  !> Updates an array to contain the integrated deposition velocities for the
888  !> given moment of each aerosol mode in the distribution.
889  subroutine scenario_modal_drydep_velocities(scenario, aero_dist, moment, &
890  density, env_state, velocities)
891 
892  !> Scenario data.
893  type(scenario_t), intent(in) :: scenario
894  !> Aerosol distribution.
895  type(aero_dist_t), intent(in) :: aero_dist
896  !> Moment to compute deposition velocities for.
897  real(kind=dp), intent(in) :: moment
898  !> Density for the mode.
899  real(kind=dp), intent(in) :: density
900  !> Environment state.
901  type(env_state_t), intent(in) :: env_state
902  !> Modal dry deposition velocities.
903  real(kind=dp), intent(inout) :: velocities(:)
904 
905  integer :: i_mode
906 
907  do i_mode = 1,size(velocities)
908  velocities(i_mode) = scenario_integrated_loss_rate_drydep( &
909  scenario, aero_dist%mode(i_mode), moment, density, env_state) &
910  * env_state%height
911  end do
912 
913  end subroutine
914 
915 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
916 
917  !> Compute and return the max loss rate function for a given volume.
918  real(kind=dp) function scenario_loss_rate_max(scenario, vol, aero_data, &
919  env_state)
920 
921  !> Scenario data.
922  type(scenario_t), intent(in) :: scenario
923  !> Particle volume (m^3).
924  real(kind=dp), intent(in) :: vol
925  !> Aerosol data.
926  type(aero_data_t), intent(in) :: aero_data
927  !> Environment state.
928  type(env_state_t), intent(in) :: env_state
929 
930  !> Number of density sample points.
931  integer, parameter :: n_sample = 3
932 
933  real(kind=dp) :: d, d_min, d_max, loss
934  integer :: i
935 
936  d_min = minval(aero_data%density)
937  d_max = maxval(aero_data%density)
938 
940  do i = 1,n_sample
941  d = interp_linear_disc(d_min, d_max, n_sample, i)
942  loss = scenario_loss_rate(scenario, vol, d, aero_data, env_state)
944  end do
945 
946  end function scenario_loss_rate_max
947 
948 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
949 
950  !> Compute an upper bound on the maximum kernel value for each
951  !> bin.
952  !! Value over_scale is multiplied to the maximum sampled value
953  !! to get the upper bound. A tighter bound may be reached if over_scale
954  !! is smaller, but that also risks falling below a kernel value.
955  subroutine scenario_loss_rate_bin_max(scenario, bin_grid, aero_data, &
956  env_state, loss_max)
957 
958  !> Scenario data.
959  type(scenario_t), intent(in) :: scenario
960  !> Bin_grid.
961  type(bin_grid_t), intent(in) :: bin_grid
962  !> Aerosol data.
963  type(aero_data_t), intent(in) :: aero_data
964  !> Environment state.
965  type(env_state_t), intent(in) :: env_state
966  !> Maximum loss vals.
967  real(kind=dp), intent(out) :: loss_max(bin_grid_size(bin_grid))
968 
969  !> Number of sample points per bin.
970  integer, parameter :: n_sample = 3
971  !> Over-estimation scale factor parameter.
972  real(kind=dp), parameter :: over_scale = 2d0
973 
974  real(kind=dp) :: v_low, v_high, vol, r, r_max
975  integer :: b, i
976 
977  do b = 1,bin_grid_size(bin_grid)
978  v_low = aero_data_rad2vol(aero_data, bin_grid%edges(b))
979  v_high = aero_data_rad2vol(aero_data, bin_grid%edges(b + 1))
980  r_max = 0d0
981  do i = 1,n_sample
982  vol = interp_linear_disc(v_low, v_high, n_sample, i)
983  r = scenario_loss_rate_max(scenario, vol, aero_data, env_state)
984  r_max = max(r_max, r)
985  end do
986  loss_max(b) = r_max * over_scale
987  end do
988 
989  end subroutine scenario_loss_rate_bin_max
990 
991 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
992 
993  !> Performs stochastic particle loss for one time-step.
994  !! If a particle \c i_part has a scenario_loss_rate() value of rate, then the
995  !! probability p will be removed by this function is
996  !! <tt>1 - exp(-delta_t*rate)</tt>.
997  !! Uses an accept-reject algorithm for efficiency, in which a particle
998  !! is first sampled with rate <tt>1 - exp(-delta_t*over_rate) </tt>
999  !! and then accepted with rate
1000  !! <tt>(1 - exp(-delta_t*rate))/(1 - exp(-delta_t*over_rate))</tt>.
1001  subroutine scenario_particle_loss(scenario, delta_t, aero_data, aero_state, &
1002  env_state)
1003 
1004  !> Scenario data.
1005  type(scenario_t), intent(in) :: scenario
1006  !> Time increment to update over.
1007  real(kind=dp), intent(in) :: delta_t
1008  !> Aerosol data.
1009  type(aero_data_t), intent(in) :: aero_data
1010  !> Aerosol state.
1011  type(aero_state_t), intent(inout) :: aero_state
1012  !> Environment state.
1013  type(env_state_t), intent(in) :: env_state
1014 
1015  integer :: c, b, s, i_part
1016  real(kind=dp) :: over_rate, over_prob, rand_real, rand_geom
1017 
1018  if (scenario%loss_function_type == scenario_loss_function_none .or. &
1019  scenario%loss_function_type == scenario_loss_function_invalid) return
1020 
1021  if (scenario_loss_alg_threshold <= 0d0) then
1022  ! use naive algorithm for everything
1023  do i_part = aero_state%apa%n_part, 1, -1
1024  call scenario_try_single_particle_loss(scenario, delta_t, &
1025  aero_data, aero_state, env_state, i_part, 1d0)
1026  end do
1027  return
1028  end if
1029 
1030  call aero_state_sort(aero_state, aero_data)
1031 
1032  if (.not. aero_state%aero_sorted%removal_rate_bounds_valid) then
1033  call scenario_loss_rate_bin_max(scenario, &
1034  aero_state%aero_sorted%bin_grid, aero_data, env_state, &
1035  aero_state%aero_sorted%removal_rate_max)
1036  aero_state%aero_sorted%removal_rate_bounds_valid = .true.
1037  end if
1038 
1039  do c = 1,aero_sorted_n_class(aero_state%aero_sorted)
1040  do b = 1,aero_sorted_n_bin(aero_state%aero_sorted)
1041  over_rate = aero_state%aero_sorted%removal_rate_max(b)
1042  if (delta_t * over_rate <= 0d0) cycle
1043  over_prob = 1d0 - exp(-delta_t * over_rate)
1044  if (over_prob >= scenario_loss_alg_threshold) then
1045  ! use naive algorithm over bin
1046  do s = aero_state%aero_sorted%size_class%inverse(b, c)%n_entry, &
1047  1,-1
1048  i_part = &
1049  aero_state%aero_sorted%size_class%inverse(b, c)%entry(s)
1050  call scenario_try_single_particle_loss(scenario, delta_t, &
1051  aero_data, aero_state, env_state, i_part, 1d0)
1052  end do
1053  else
1054  ! use accept-reject algorithm over bin
1055  s = aero_state%aero_sorted%size_class%inverse(b, c)%n_entry + 1
1056  do while (.true.)
1057  rand_real = pmc_random()
1058  if (rand_real <= 0d0) exit
1059  rand_geom = -log(rand_real) / (delta_t * over_rate) + 1d0
1060  if (rand_geom >= real(s, kind=dp)) exit
1061  s = s - floor(rand_geom)
1062 
1063  ! note: floor(rand_geom) is a random geometric variable
1064  ! with accept probability 1 - exp(-delta_t*over_rate)
1065 
1066  i_part = &
1067  aero_state%aero_sorted%size_class%inverse(b, c)%entry(s)
1068  call scenario_try_single_particle_loss(scenario, delta_t, &
1069  aero_data, aero_state, env_state, i_part, over_prob)
1070  end do
1071  end if
1072  end do
1073  end do
1074 
1075  !call aero_state_check_sort(aero_state)
1076 
1077  end subroutine scenario_particle_loss
1078 
1079 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
1080 
1081  !> Test a candidate particle to see if it should be removed,
1082  !> and remove if necessary.
1083  !! Particle is removed with probability
1084  !! (1d0 - exp(-delta_t*rate))/over_prob, where rate is the loss function
1085  !! evaluated for the given particle.
1086  subroutine scenario_try_single_particle_loss(scenario, delta_t, &
1087  aero_data, aero_state, env_state, i_part, over_prob)
1088 
1089  !> Scenario data.
1090  type(scenario_t), intent(in) :: scenario
1091  !> Time increment to update over.
1092  real(kind=dp), intent(in) :: delta_t
1093  !> Aerosol data.
1094  type(aero_data_t), intent(in) :: aero_data
1095  !> Aerosol state.
1096  type(aero_state_t), intent(inout) :: aero_state
1097  !> Environment state.
1098  type(env_state_t), intent(in) :: env_state
1099  !> Index of particle to attempt removal
1100  integer, intent(in) :: i_part
1101  !> Overestimated removal probability used previously
1102  real(kind=dp), intent(in) :: over_prob
1103 
1104  real(kind=dp) :: prob, rate, vol, density
1105  type(aero_info_t) :: aero_info
1106 
1107  vol = aero_particle_volume(aero_state%apa%particle(i_part))
1108  density = aero_particle_density(aero_state%apa%particle(i_part), aero_data)
1109  rate = scenario_loss_rate(scenario, vol, density, aero_data, env_state)
1110  prob = 1d0 - exp(-delta_t * rate)
1111  call warn_assert_msg(295846288, prob <= over_prob, &
1112  "particle loss upper bound estimation is too tight: " &
1113  // trim(real_to_string(prob)) // " > " &
1114  // trim(real_to_string(over_prob)) )
1115  if (pmc_random() * over_prob > prob) return
1116 
1117  aero_info%id = aero_state%apa%particle(i_part)%id
1118  aero_info%action = aero_info_dilution
1119  aero_info%other_id = 0
1120  call aero_state_remove_particle_with_info(aero_state, i_part, aero_info)
1121 
1122  end subroutine scenario_try_single_particle_loss
1123 
1124 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
1125 
1126  !> Whether any of the contained aerosol modes are of the given type.
1127  elemental logical function scenario_contains_aero_mode_type(scenario, &
1128  aero_mode_type)
1129 
1130  !> Scenario data.
1131  type(scenario_t), intent(in) :: scenario
1132  !> Aerosol mode type to test for.
1133  integer, intent(in) :: aero_mode_type
1134 
1136  = any(aero_dist_contains_aero_mode_type(scenario%aero_emission, &
1137  aero_mode_type)) &
1138  .or. any(aero_dist_contains_aero_mode_type(scenario%aero_background, &
1139  aero_mode_type))
1140 
1142 
1143 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
1144 
1145  !> Read environment data from an spec file.
1146  subroutine spec_file_read_scenario(file, gas_data, aero_data, &
1147  read_aero_weight_classes, scenario)
1148 
1149  !> Spec file.
1150  type(spec_file_t), intent(inout) :: file
1151  !> Gas data values.
1152  type(gas_data_t), intent(in) :: gas_data
1153  !> Aerosol data.
1154  type(aero_data_t), intent(inout) :: aero_data
1155  !> Whether the weight classes for each source are specified in inputs.
1156  logical, intent(in) :: read_aero_weight_classes
1157  !> Scenario data.
1158  type(scenario_t), intent(inout) :: scenario
1159 
1160  character(len=PMC_MAX_FILENAME_LEN) :: sub_filename
1161  type(spec_file_t) :: sub_file
1162  character(len=SPEC_LINE_MAX_VAR_LEN) :: function_name
1163  type(spec_line_t) :: line
1164 
1165  ! note that we have to hard-code the list for doxygen below
1166 
1167  !> \page input_format_scenario Input File Format: Scenario
1168  !!
1169  !! The scenario parameters are:
1170  !! <ul>
1171  !! <li> \b temp_profile (string): the name of the file from which to
1172  !! read the temperature profile --- the file format should be
1173  !! \subpage input_format_temp_profile
1174  !! <li> \b pressure_profile (string): the name of the file from which to
1175  !! read the pressure profile --- the file format should be
1176  !! \subpage input_format_pressure_profile
1177  !! <li> \b height_profile (string): the name of the file from which
1178  !! to read the mixing layer height profile --- the file format
1179  !! should be \subpage input_format_height_profile
1180  !! <li> \b gas_emissions (string): the name of the file from which to
1181  !! read the gas emissions profile --- the file format should be
1182  !! \subpage input_format_gas_profile
1183  !! <li> \b gas_background (string): the name of the file from which
1184  !! to read the gas background profile --- the file format should
1185  !! be \subpage input_format_gas_profile
1186  !! <li> \b aero_emissions (string): the name of the file from which
1187  !! to read the aerosol emissions profile --- the file format
1188  !! should be \subpage input_format_aero_dist_profile
1189  !! <li> \b aero_background (string): the name of the file from which
1190  !! to read the aerosol background profile --- the file format
1191  !! should be \subpage input_format_aero_dist_profile
1192  !! <li> \b loss_function (string): the type of loss function ---
1193  !! must be one of: \c none for no particle loss, \c constant
1194  !! for constant loss rate, \c volume for particle loss proportional
1195  !! to particle volume, \c drydep for particle loss proportional
1196  !! to dry deposition velocity, or \c chamber for a chamber model.
1197  !! If \c loss_function is \c chamber, then the following
1198  !! parameters must also be provided:
1199  !! - \subpage input_format_chamber
1200  !! </ul>
1201  !!
1202  !! See also:
1203  !! - \ref spec_file_format --- the input file text format
1204 
1205  ! temperature profile
1206  call spec_file_read_string(file, "temp_profile", sub_filename)
1207  call spec_file_open(sub_filename, sub_file)
1208  call spec_file_read_timed_real_array(sub_file, "temp", &
1209  scenario%temp_time, scenario%temp)
1210  call spec_file_close(sub_file)
1211 
1212  ! pressure profile
1213  call spec_file_read_string(file, "pressure_profile", sub_filename)
1214  call spec_file_open(sub_filename, sub_file)
1215  call spec_file_read_timed_real_array(sub_file, "pressure", &
1216  scenario%pressure_time, scenario%pressure)
1217  call spec_file_close(sub_file)
1218 
1219  ! height profile
1220  call spec_file_read_string(file, "height_profile", sub_filename)
1221  call spec_file_open(sub_filename, sub_file)
1222  call spec_file_read_timed_real_array(sub_file, "height", &
1223  scenario%height_time, scenario%height)
1224  call spec_file_close(sub_file)
1225 
1226  ! gas emissions profile
1227  call spec_file_read_string(file, "gas_emissions", sub_filename)
1228  call spec_file_open(sub_filename, sub_file)
1229  call spec_file_read_gas_states_times_rates(sub_file, gas_data, &
1230  scenario%gas_emission_time, scenario%gas_emission_rate_scale, &
1231  scenario%gas_emission)
1232  call spec_file_close(sub_file)
1233 
1234  ! gas background profile
1235  call spec_file_read_string(file, "gas_background", sub_filename)
1236  call spec_file_open(sub_filename, sub_file)
1237  call spec_file_read_gas_states_times_rates(sub_file, gas_data, &
1238  scenario%gas_dilution_time, scenario%gas_dilution_rate, &
1239  scenario%gas_background)
1240  call spec_file_close(sub_file)
1241 
1242  ! aerosol emissions profile
1243  call spec_file_read_string(file, "aero_emissions", sub_filename)
1244  call spec_file_open(sub_filename, sub_file)
1245  call spec_file_read_aero_dists_times_rates(sub_file, aero_data, &
1246  read_aero_weight_classes, scenario%aero_emission_time, &
1247  scenario%aero_emission_rate_scale, scenario%aero_emission)
1248  call spec_file_close(sub_file)
1249 
1250  ! aerosol background profile
1251  call spec_file_read_string(file, "aero_background", sub_filename)
1252  call spec_file_open(sub_filename, sub_file)
1253  call spec_file_read_aero_dists_times_rates(sub_file, aero_data, &
1254  read_aero_weight_classes, scenario%aero_dilution_time, &
1255  scenario%aero_dilution_rate, scenario%aero_background)
1256  call spec_file_close(sub_file)
1257 
1258  ! loss function
1259  call spec_file_read_string(file, 'loss_function', function_name)
1260  if (trim(function_name) == 'none') then
1261  scenario%loss_function_type = scenario_loss_function_none
1262  else if (trim(function_name) == 'constant') then
1263  scenario%loss_function_type = scenario_loss_function_constant
1264  else if (trim(function_name) == 'volume') then
1265  scenario%loss_function_type = scenario_loss_function_volume
1266  else if (trim(function_name) == 'drydep') then
1267  scenario%loss_function_type = scenario_loss_function_drydep
1268  call spec_file_read_line_no_eof(file, line)
1269  call spec_file_unread_line(file)
1270  if (line%name /= 'drydep_params') then
1271  call warn_msg(735291468, "using default dry deposition parameters")
1272  else
1273  call spec_file_read_string(file, 'drydep_params', sub_filename)
1274  call spec_file_open(sub_filename, sub_file)
1275  call spec_file_read_drydep_params(sub_file, scenario%drydep)
1276  call spec_file_close(sub_file)
1277  end if
1278  else if (trim(function_name) == 'chamber') then
1279  scenario%loss_function_type = scenario_loss_function_chamber
1280  call spec_file_read_chamber(file, scenario%chamber)
1281  else
1282  call spec_file_die_msg(518248400, file, &
1283  "Unknown loss function type: " // trim(function_name))
1284  end if
1285 
1286  end subroutine spec_file_read_scenario
1287 
1288  ! the following blocks belong in the subroutine above, but they are
1289  ! outside because Doxygen 1.8.7 doesn't resolve references when
1290  ! multiple \page blocks are in one subroutine
1291 
1292  !> \page input_format_temp_profile Input File Format: Temperature Profile
1293  !!
1294  !! A temperature profile input file must consist of two lines:
1295  !! - the first line must begin with \c time and should be followed
1296  !! by \f$N\f$ space-separated real scalars, giving the times (in
1297  !! s after the start of the simulation) of the temperature set
1298  !! points --- the times must be in increasing order
1299  !! - the second line must begin with \c temp and should be followed
1300  !! by \f$N\f$ space-separated real scalars, giving the
1301  !! temperatures (in K) at the corresponding times
1302  !!
1303  !! The temperature profile is linearly interpolated between the
1304  !! specified times, while before the first time it takes the first
1305  !! temperature value and after the last time it takes the last
1306  !! temperature value.
1307  !!
1308  !! Example:
1309  !! <pre>
1310  !! time 0 600 1800 # time (in s) after simulation start
1311  !! temp 270 290 280 # temperature (in K)
1312  !! </pre>
1313  !! Here the temperature starts at 270&nbsp;K at the start of the
1314  !! simulation, rises to 290&nbsp;K after 10&nbsp;min, and then
1315  !! falls again to 280&nbsp;K at 30&nbsp;min. Between these times
1316  !! the temperature is linearly interpolated, while after
1317  !! 30&nbsp;min it is held constant at 280&nbsp;K.
1318  !!
1319  !! See also:
1320  !! - \ref spec_file_format --- the input file text format
1321  !! - \ref input_format_scenario --- the environment data
1322  !! containing the temperature profile
1323 
1324  !> \page input_format_pressure_profile Input File Format: Pressure Profile
1325  !!
1326  !! A pressure profile input file must consist of two lines:
1327  !! - the first line must begin with \c time and should be followed
1328  !! by \f$N\f$ space-separated real scalars, giving the times (in
1329  !! s after the start of the simulation) of the pressure set
1330  !! points --- the times must be in increasing order
1331  !! - the second line must begin with \c pressure and should be followed
1332  !! by \f$N\f$ space-separated real scalars, giving the
1333  !! pressures (in Pa) at the corresponding times
1334  !!
1335  !! The pressure profile is linearly interpolated between the
1336  !! specified times, while before the first time it takes the first
1337  !! pressure value and after the last time it takes the last
1338  !! pressure value.
1339  !!
1340  !! Example:
1341  !! <pre>
1342  !! time 0 600 1800 # time (in s) after simulation start
1343  !! pressure 1e5 9e4 7.5e4 # pressure (in Pa)
1344  !! </pre>
1345  !! Here the pressure starts at 1e5&nbsp;Pa at the start of the
1346  !! simulation, decreases to 9e4&nbsp;Pa after 10&nbsp;min, and then
1347  !! decreases further to 7.5e4&nbsp;Pa at 30&nbsp;min. Between these times
1348  !! the pressure is linearly interpolated, while after
1349  !! 30&nbsp;min it is held constant at 7.5e4&nbsp;Pa.
1350  !!
1351  !! See also:
1352  !! - \ref spec_file_format --- the input file text format
1353  !! - \ref input_format_scenario --- the environment data
1354  !! containing the pressure profile
1355 
1356  !> \page input_format_height_profile Input File Format: Mixing Layer Height Profile
1357  !!
1358  !! A mixing layer height profile input file must consist of two
1359  !! lines:
1360  !! - the first line must begin with \c time and should be followed
1361  !! by \f$N\f$ space-separated real scalars, giving the times (in
1362  !! s after the start of the simulation) of the height set
1363  !! points --- the times must be in increasing order
1364  !! - the second line must begin with \c height and should be
1365  !! followed by \f$N\f$ space-separated real scalars, giving the
1366  !! mixing layer heights (in m) at the corresponding times
1367  !!
1368  !! The mixing layer height profile is linearly interpolated
1369  !! between the specified times, while before the first time it
1370  !! takes the first height value and after the last time it takes
1371  !! the last height value.
1372  !!
1373  !! Example:
1374  !! <pre>
1375  !! time 0 600 1800 # time (in s) after simulation start
1376  !! height 500 1000 800 # mixing layer height (in m)
1377  !! </pre>
1378  !! Here the mixing layer height starts at 500&nbsp;m at the start
1379  !! of the simulation, rises to 1000&nbsp;m after 10&nbsp;min, and
1380  !! then falls again to 800&nbsp;m at 30&nbsp;min. Between these
1381  !! times the mixing layer height is linearly interpolated, while
1382  !! after 30&nbsp;min it is held constant at 800&nbsp;m.
1383  !!
1384  !! See also:
1385  !! - \ref spec_file_format --- the input file text format
1386  !! - \ref input_format_scenario --- the environment data
1387  !! containing the mixing layer height profile
1388 
1389 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
1390 
1391  !> Read dry deposition parameters from a spec file.
1392  subroutine spec_file_read_drydep_params(file, drydep_params)
1393 
1394  !> Spec file.
1395  type(spec_file_t), intent(inout) :: file
1396  !> Dry deposition parameters.
1397  type(drydep_params_t), intent(inout) :: drydep_params
1398 
1399  !> \page input_format_drydep_params Input File Format: Dry Deposition Parameters
1400  !!
1401  !! Dry deposition is simulatied using the specified parameters:
1402  !! - \b z_ref (real, unit m): the reference height \f$z_{\rm ref}\f$ used
1403  !! in the calculation of aerodynamic resistance \f$R_a\f$
1404  !! - \b u_mean (real, unit m s^{-1}): the wind speed at the reference
1405  !! height
1406  !! - \b z_rough (real, unit m): the roughness length associated with
1407  !! the surface
1408  !! - \b A (real, unit m): the characteristic radius of collectors
1409  !! associated with the surface
1410  !! - \b alpha (real, dimensionless): the parameter \f$\alpha\f$ used
1411  !! in the calculation of impaction efficiency \f$E_{\rm IM}\f$
1412  !! - \b eps_0 (real, dimensionless): the empirical constant used in the
1413  !! calculation of surface resistance \f$R_s\f$
1414  !! - \b gamma (real, dimensionless): the exponent \f$\gamma\f$ used in the
1415  !! calculation of Brownian diffusion collection
1416  !! efficiency \f$E_{\rm B}\f$
1417  !! - \b C_B (real, dimensionless): the coefficient for Brownian diffusion
1418  !! collection efficiency \f$E_{\rm B}\f$
1419  !! - \b C_IN (real, dimensionless): the coefficient for interception
1420  !! collection efficiency \f$E_{\rm IN}\f$
1421  !! - \b C_IM (real, dimensionless): the coefficient for impaction
1422  !! collection efficiency \f$E_{\rm IM}\f$
1423  !! - \b nu (real, dimensionless): the exponent \f$\nu\f$ used in the
1424  !! calculation of interception collection efficiency \f$E_{\rm IN}\f$
1425  !! - \b beta (real, dimensionless): the exponent \f$\beta\f$ used in the
1426  !! calculation of impaction collection efficiency \f$E_{\rm IM}\f$
1427  !!
1428  !! See also:
1429  !! - \ref spec_file_format --- the input file text format
1430  !! - \ref input_format_scenario --- the prescribed profiles of
1431  !! other environment data
1432 
1433  call spec_file_read_real(file, "z_ref", drydep_params%z_ref)
1434  call spec_file_read_real(file, "u_mean", drydep_params%u_mean)
1435  call spec_file_read_real(file, "z_rough", drydep_params%z_rough)
1436  call spec_file_read_real(file, "A", drydep_params%A)
1437  call spec_file_read_real(file, "alpha", drydep_params%alpha)
1438  call spec_file_read_real(file, "eps_0", drydep_params%eps_0)
1439  call spec_file_read_real(file, "gamma", drydep_params%gamma)
1440  call spec_file_read_real(file, "C_B", drydep_params%C_B)
1441  call spec_file_read_real(file, "C_IN", drydep_params%C_IN)
1442  call spec_file_read_real(file, "C_IM", drydep_params%C_IM)
1443  call spec_file_read_real(file, "nu", drydep_params%nu)
1444  call spec_file_read_real(file, "beta", drydep_params%beta)
1445 
1446  end subroutine spec_file_read_drydep_params
1447 
1448 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
1449 
1450  !> Write dry deposition parameters to a NetCDF file.
1451  subroutine drydep_params_output_netcdf(drydep_params, ncid)
1452 
1453  !> Dry deposition parameters.
1454  type(drydep_params_t), intent(in) :: drydep_params
1455  !> NetCDF file ID, in data mode.
1456  integer, intent(in) :: ncid
1457 
1458  associate(d => drydep_params)
1459  call pmc_nc_write_real(ncid, d%z_ref, "drydep_z_ref", unit="m")
1460  call pmc_nc_write_real(ncid, d%u_mean, "drydep_u_mean", &
1461  unit="m s^{-1}")
1462  call pmc_nc_write_real(ncid, d%z_rough, "drydep_z_rough", unit="m")
1463  call pmc_nc_write_real(ncid, d%A, "drydep_A", unit="m")
1464  call pmc_nc_write_real(ncid, d%alpha, "drydep_alpha", unit="1")
1465  call pmc_nc_write_real(ncid, d%eps_0, "drydep_eps_0", unit="1")
1466  call pmc_nc_write_real(ncid, d%gamma, "drydep_gamma", unit="1")
1467  call pmc_nc_write_real(ncid, d%C_B, "drydep_C_B", unit="1")
1468  call pmc_nc_write_real(ncid, d%C_IN, "drydep_C_IN", unit="1")
1469  call pmc_nc_write_real(ncid, d%C_IM, "drydep_C_IM", unit="1")
1470  call pmc_nc_write_real(ncid, d%nu, "drydep_nu", unit="1")
1471  call pmc_nc_write_real(ncid, d%beta, "drydep_beta", unit="1")
1472  end associate
1473 
1474  end subroutine drydep_params_output_netcdf
1475 
1476 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
1477 
1478  !> Read dry deposition parameters from a NetCDF file.
1479  subroutine drydep_params_input_netcdf(drydep_params, ncid)
1480 
1481  !> Dry deposition parameters.
1482  type(drydep_params_t), intent(inout) :: drydep_params
1483  !> NetCDF file ID, in data mode.
1484  integer, intent(in) :: ncid
1485 
1486  associate(d => drydep_params)
1487  call pmc_nc_read_real(ncid, d%z_ref, "drydep_z_ref")
1488  call pmc_nc_read_real(ncid, d%u_mean, "drydep_u_mean")
1489  call pmc_nc_read_real(ncid, d%z_rough, "drydep_z_rough")
1490  call pmc_nc_read_real(ncid, d%A, "drydep_A")
1491  call pmc_nc_read_real(ncid, d%alpha, "drydep_alpha")
1492  call pmc_nc_read_real(ncid, d%eps_0, "drydep_eps_0")
1493  call pmc_nc_read_real(ncid, d%gamma, "drydep_gamma")
1494  call pmc_nc_read_real(ncid, d%C_B, "drydep_C_B")
1495  call pmc_nc_read_real(ncid, d%C_IN, "drydep_C_IN")
1496  call pmc_nc_read_real(ncid, d%C_IM, "drydep_C_IM")
1497  call pmc_nc_read_real(ncid, d%nu, "drydep_nu")
1498  call pmc_nc_read_real(ncid, d%beta, "drydep_beta")
1499  end associate
1500 
1501  end subroutine drydep_params_input_netcdf
1502 
1503 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
1504 
1505  !> Determines the number of bytes required to pack the given value.
1506  integer function pmc_mpi_pack_size_scenario(val)
1507 
1508  !> Value to pack.
1509  type(scenario_t), intent(in) :: val
1510 
1511  integer :: total_size, i
1512 
1513  total_size = &
1514  pmc_mpi_pack_size_real_array(val%temp_time) &
1515  + pmc_mpi_pack_size_real_array(val%temp) &
1516  + pmc_mpi_pack_size_real_array(val%pressure_time) &
1517  + pmc_mpi_pack_size_real_array(val%pressure) &
1518  + pmc_mpi_pack_size_real_array(val%height_time) &
1519  + pmc_mpi_pack_size_real_array(val%height) &
1520  + pmc_mpi_pack_size_real_array(val%gas_emission_time) &
1521  + pmc_mpi_pack_size_real_array(val%gas_emission_rate_scale) &
1522  + pmc_mpi_pack_size_real_array(val%gas_dilution_time) &
1523  + pmc_mpi_pack_size_real_array(val%gas_dilution_rate) &
1524  + pmc_mpi_pack_size_real_array(val%aero_emission_time) &
1525  + pmc_mpi_pack_size_real_array(val%aero_emission_rate_scale) &
1526  + pmc_mpi_pack_size_real_array(val%aero_dilution_time) &
1527  + pmc_mpi_pack_size_real_array(val%aero_dilution_rate) &
1528  + pmc_mpi_pack_size_integer(val%loss_function_type) &
1529  + pmc_mpi_pack_size_drydep(val%drydep) &
1530  + pmc_mpi_pack_size_chamber(val%chamber)
1531  if (allocated(val%gas_emission_time)) then
1532  do i = 1,size(val%gas_emission)
1533  total_size = total_size &
1534  + pmc_mpi_pack_size_gas_state(val%gas_emission(i))
1535  end do
1536  end if
1537  if (allocated(val%gas_dilution_time)) then
1538  do i = 1,size(val%gas_background)
1539  total_size = total_size &
1540  + pmc_mpi_pack_size_gas_state(val%gas_background(i))
1541  end do
1542  end if
1543  if (allocated(val%aero_emission_time)) then
1544  do i = 1,size(val%aero_emission)
1545  total_size = total_size &
1546  + pmc_mpi_pack_size_aero_dist(val%aero_emission(i))
1547  end do
1548  end if
1549  if (allocated(val%aero_dilution_time)) then
1550  do i = 1,size(val%aero_background)
1551  total_size = total_size &
1552  + pmc_mpi_pack_size_aero_dist(val%aero_background(i))
1553  end do
1554  end if
1555 
1556  pmc_mpi_pack_size_scenario = total_size
1557 
1558  end function pmc_mpi_pack_size_scenario
1559 
1560 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
1561 
1562  !> Packs the given value into the buffer, advancing position.
1563  subroutine pmc_mpi_pack_scenario(buffer, position, val)
1564 
1565  !> Memory buffer.
1566  character, intent(inout) :: buffer(:)
1567  !> Current buffer position.
1568  integer, intent(inout) :: position
1569  !> Value to pack.
1570  type(scenario_t), intent(in) :: val
1571 
1572 #ifdef PMC_USE_MPI
1573  integer :: prev_position, i
1574 
1575  prev_position = position
1576  call pmc_mpi_pack_real_array(buffer, position, val%temp_time)
1577  call pmc_mpi_pack_real_array(buffer, position, val%temp)
1578  call pmc_mpi_pack_real_array(buffer, position, val%pressure_time)
1579  call pmc_mpi_pack_real_array(buffer, position, val%pressure)
1580  call pmc_mpi_pack_real_array(buffer, position, val%height_time)
1581  call pmc_mpi_pack_real_array(buffer, position, val%height)
1582  call pmc_mpi_pack_real_array(buffer, position, val%gas_emission_time)
1583  call pmc_mpi_pack_real_array(buffer, position, val%gas_emission_rate_scale)
1584  call pmc_mpi_pack_real_array(buffer, position, val%gas_dilution_time)
1585  call pmc_mpi_pack_real_array(buffer, position, val%gas_dilution_rate)
1586  call pmc_mpi_pack_real_array(buffer, position, val%aero_emission_time)
1587  call pmc_mpi_pack_real_array(buffer, position, &
1588  val%aero_emission_rate_scale)
1589  call pmc_mpi_pack_real_array(buffer, position, val%aero_dilution_time)
1590  call pmc_mpi_pack_real_array(buffer, position, val%aero_dilution_rate)
1591  call pmc_mpi_pack_integer(buffer, position, val%loss_function_type)
1592  call pmc_mpi_pack_drydep(buffer, position, val%drydep)
1593  call pmc_mpi_pack_chamber(buffer, position, val%chamber)
1594  if (allocated(val%gas_emission_time)) then
1595  do i = 1,size(val%gas_emission)
1596  call pmc_mpi_pack_gas_state(buffer, position, val%gas_emission(i))
1597  end do
1598  end if
1599  if (allocated(val%gas_dilution_time)) then
1600  do i = 1,size(val%gas_background)
1601  call pmc_mpi_pack_gas_state(buffer, position, val%gas_background(i))
1602  end do
1603  end if
1604  if (allocated(val%aero_emission_time)) then
1605  do i = 1,size(val%aero_emission)
1606  call pmc_mpi_pack_aero_dist(buffer, position, val%aero_emission(i))
1607  end do
1608  end if
1609  if (allocated(val%aero_dilution_time)) then
1610  do i = 1,size(val%aero_background)
1611  call pmc_mpi_pack_aero_dist(buffer, position, val%aero_background(i))
1612  end do
1613  end if
1614  call assert(639466930, &
1615  position - prev_position <= pmc_mpi_pack_size_scenario(val))
1616 #endif
1617 
1618  end subroutine pmc_mpi_pack_scenario
1619 
1620 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
1621 
1622  !> Unpacks the given value from the buffer, advancing position.
1623  subroutine pmc_mpi_unpack_scenario(buffer, position, val)
1624 
1625  !> Memory buffer.
1626  character, intent(inout) :: buffer(:)
1627  !> Current buffer position.
1628  integer, intent(inout) :: position
1629  !> Value to pack.
1630  type(scenario_t), intent(inout) :: val
1631 
1632 #ifdef PMC_USE_MPI
1633  integer :: prev_position, i
1634 
1635  prev_position = position
1636  call pmc_mpi_unpack_real_array(buffer, position, val%temp_time)
1637  call pmc_mpi_unpack_real_array(buffer, position, val%temp)
1638  call pmc_mpi_unpack_real_array(buffer, position, val%pressure_time)
1639  call pmc_mpi_unpack_real_array(buffer, position, val%pressure)
1640  call pmc_mpi_unpack_real_array(buffer, position, val%height_time)
1641  call pmc_mpi_unpack_real_array(buffer, position, val%height)
1642  call pmc_mpi_unpack_real_array(buffer, position, val%gas_emission_time)
1643  call pmc_mpi_unpack_real_array(buffer, position, &
1644  val%gas_emission_rate_scale)
1645  call pmc_mpi_unpack_real_array(buffer, position, val%gas_dilution_time)
1646  call pmc_mpi_unpack_real_array(buffer, position, val%gas_dilution_rate)
1647  call pmc_mpi_unpack_real_array(buffer, position, val%aero_emission_time)
1648  call pmc_mpi_unpack_real_array(buffer, position, &
1649  val%aero_emission_rate_scale)
1650  call pmc_mpi_unpack_real_array(buffer, position, val%aero_dilution_time)
1651  call pmc_mpi_unpack_real_array(buffer, position, val%aero_dilution_rate)
1652  call pmc_mpi_unpack_integer(buffer, position, val%loss_function_type)
1653  call pmc_mpi_unpack_drydep(buffer, position, val%drydep)
1654  call pmc_mpi_unpack_chamber(buffer, position, val%chamber)
1655  if (allocated(val%gas_emission)) deallocate(val%gas_emission)
1656  if (allocated(val%gas_background)) deallocate(val%gas_background)
1657  if (allocated(val%aero_emission)) deallocate(val%aero_emission)
1658  if (allocated(val%aero_background)) deallocate(val%aero_background)
1659  if (allocated(val%gas_emission_time)) then
1660  allocate(val%gas_emission(size(val%gas_emission_time)))
1661  do i = 1,size(val%gas_emission)
1662  call pmc_mpi_unpack_gas_state(buffer, position, val%gas_emission(i))
1663  end do
1664  end if
1665  if (allocated(val%gas_dilution_time)) then
1666  allocate(val%gas_background(size(val%gas_dilution_time)))
1667  do i = 1,size(val%gas_background)
1668  call pmc_mpi_unpack_gas_state(buffer, position, &
1669  val%gas_background(i))
1670  end do
1671  end if
1672  if (allocated(val%aero_emission_time)) then
1673  allocate(val%aero_emission(size(val%aero_emission_time)))
1674  do i = 1,size(val%aero_emission)
1675  call pmc_mpi_unpack_aero_dist(buffer, position, val%aero_emission(i))
1676  end do
1677  end if
1678  if (allocated(val%aero_dilution_time)) then
1679  allocate(val%aero_background(size(val%aero_dilution_time)))
1680  do i = 1,size(val%aero_background)
1681  call pmc_mpi_unpack_aero_dist(buffer, position, &
1682  val%aero_background(i))
1683  end do
1684  end if
1685  call assert(611542570, &
1686  position - prev_position <= pmc_mpi_pack_size_scenario(val))
1687 #endif
1688 
1689  end subroutine pmc_mpi_unpack_scenario
1690 
1691 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
1692 
1693  !> Determines the number of bytes required to pack the given value.
1694  integer function pmc_mpi_pack_size_drydep(val)
1695 
1696  !> Value to pack.
1697  type(drydep_params_t), intent(in) :: val
1698 
1700  pmc_mpi_pack_size_real(val%z_ref) &
1701  + pmc_mpi_pack_size_real(val%u_mean) &
1702  + pmc_mpi_pack_size_real(val%z_rough) &
1703  + pmc_mpi_pack_size_real(val%A) &
1704  + pmc_mpi_pack_size_real(val%alpha) &
1705  + pmc_mpi_pack_size_real(val%eps_0) &
1706  + pmc_mpi_pack_size_real(val%gamma) &
1707  + pmc_mpi_pack_size_real(val%C_B) &
1708  + pmc_mpi_pack_size_real(val%C_IN) &
1709  + pmc_mpi_pack_size_real(val%C_IM) &
1710  + pmc_mpi_pack_size_real(val%nu) &
1711  + pmc_mpi_pack_size_real(val%beta)
1712 
1713  end function pmc_mpi_pack_size_drydep
1714 
1715 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
1716 
1717  !> Packs the given value into the buffer, advancing position.
1718  subroutine pmc_mpi_pack_drydep(buffer, position, val)
1719 
1720  !> Memory buffer.
1721  character, intent(inout) :: buffer(:)
1722  !> Current buffer position.
1723  integer, intent(inout) :: position
1724  !> Value to pack.
1725  type(drydep_params_t), intent(in) :: val
1726 
1727 #ifdef PMC_USE_MPI
1728  integer :: prev_position
1729 
1730  prev_position = position
1731  call pmc_mpi_pack_real(buffer, position, val%z_ref)
1732  call pmc_mpi_pack_real(buffer, position, val%u_mean)
1733  call pmc_mpi_pack_real(buffer, position, val%z_rough)
1734  call pmc_mpi_pack_real(buffer, position, val%A)
1735  call pmc_mpi_pack_real(buffer, position, val%alpha)
1736  call pmc_mpi_pack_real(buffer, position, val%eps_0)
1737  call pmc_mpi_pack_real(buffer, position, val%gamma)
1738  call pmc_mpi_pack_real(buffer, position, val%C_B)
1739  call pmc_mpi_pack_real(buffer, position, val%C_IN)
1740  call pmc_mpi_pack_real(buffer, position, val%C_IM)
1741  call pmc_mpi_pack_real(buffer, position, val%nu)
1742  call pmc_mpi_pack_real(buffer, position, val%beta)
1743  call assert(371592468, &
1744  position - prev_position <= pmc_mpi_pack_size_drydep(val))
1745 #endif
1746 
1747  end subroutine pmc_mpi_pack_drydep
1748 
1749 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
1750 
1751  !> Unpacks the given value from the buffer, advancing position.
1752  subroutine pmc_mpi_unpack_drydep(buffer, position, val)
1753 
1754  !> Memory buffer.
1755  character, intent(inout) :: buffer(:)
1756  !> Current buffer position.
1757  integer, intent(inout) :: position
1758  !> Value to pack.
1759  type(drydep_params_t), intent(inout) :: val
1760 
1761 #ifdef PMC_USE_MPI
1762  integer :: prev_position
1763 
1764  prev_position = position
1765  call pmc_mpi_unpack_real(buffer, position, val%z_ref)
1766  call pmc_mpi_unpack_real(buffer, position, val%u_mean)
1767  call pmc_mpi_unpack_real(buffer, position, val%z_rough)
1768  call pmc_mpi_unpack_real(buffer, position, val%A)
1769  call pmc_mpi_unpack_real(buffer, position, val%alpha)
1770  call pmc_mpi_unpack_real(buffer, position, val%eps_0)
1771  call pmc_mpi_unpack_real(buffer, position, val%gamma)
1772  call pmc_mpi_unpack_real(buffer, position, val%C_B)
1773  call pmc_mpi_unpack_real(buffer, position, val%C_IN)
1774  call pmc_mpi_unpack_real(buffer, position, val%C_IM)
1775  call pmc_mpi_unpack_real(buffer, position, val%nu)
1776  call pmc_mpi_unpack_real(buffer, position, val%beta)
1777  call assert(582741936, &
1778  position - prev_position <= pmc_mpi_pack_size_drydep(val))
1779 #endif
1780 
1781  end subroutine pmc_mpi_unpack_drydep
1782 
1783 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
1784 
1785 end module pmc_scenario
pmc_aero_state::aero_state_zero
subroutine aero_state_zero(aero_state)
Resets an aero_state to have zero particles per bin.
Definition: aero_state.F90:350
pmc_scenario::scenario_contains_aero_mode_type
elemental logical function scenario_contains_aero_mode_type(scenario, aero_mode_type)
Whether any of the contained aerosol modes are of the given type.
Definition: scenario.F90:1129
pmc_scenario::scenario_update_aero_binned
subroutine scenario_update_aero_binned(scenario, delta_t, env_state, old_env_state, bin_grid, aero_data, aero_binned)
Do emissions and background dilution from the environment for a binned aerosol distribution.
Definition: scenario.F90:385
pmc_scenario::pmc_mpi_unpack_drydep
subroutine pmc_mpi_unpack_drydep(buffer, position, val)
Unpacks the given value from the buffer, advancing position.
Definition: scenario.F90:1753
pmc_aero_dist::aero_dist_n_mode
elemental integer function aero_dist_n_mode(aero_dist)
Return the number of modes.
Definition: aero_dist.F90:44
pmc_gas_data::gas_data_t
Constant gas data.
Definition: gas_data.F90:35
pmc_mpi
Wrapper functions for MPI.
Definition: mpi.F90:13
pmc_scenario::drydep_params_output_netcdf
subroutine drydep_params_output_netcdf(drydep_params, ncid)
Write dry deposition parameters to a NetCDF file.
Definition: scenario.F90:1452
pmc_mpi::pmc_mpi_pack_size_real
integer function pmc_mpi_pack_size_real(val)
Determines the number of bytes required to pack the given value.
Definition: mpi.F90:385
pmc_scenario::scenario_loss_rate
real(kind=dp) function scenario_loss_rate(scenario, vol, density, aero_data, env_state)
Evaluate a loss rate function.
Definition: scenario.F90:498
pmc_scenario::pmc_mpi_pack_drydep
subroutine pmc_mpi_pack_drydep(buffer, position, val)
Packs the given value into the buffer, advancing position.
Definition: scenario.F90:1719
pmc_scenario::scenario_modal_drydep_velocities
subroutine scenario_modal_drydep_velocities(scenario, aero_dist, moment, density, env_state, velocities)
Updates an array to contain the integrated deposition velocities for the given moment of each aerosol...
Definition: scenario.F90:891
pmc_spec_file::spec_file_close
subroutine spec_file_close(file)
Close a spec file.
Definition: spec_file.F90:135
pmc_gas_state::gas_state_ensure_nonnegative
subroutine gas_state_ensure_nonnegative(gas_state)
Set any negative values to zero.
Definition: gas_state.F90:167
pmc_aero_state::aero_state_add_aero_dist_sample
subroutine aero_state_add_aero_dist_sample(aero_state, aero_data, aero_dist, sample_prop, characteristic_factor, create_time, allow_doubling, allow_halving, n_part_add)
Generates a Poisson sample of an aero_dist, adding to aero_state, with the given sample proportion.
Definition: aero_state.F90:761
pmc_scenario::scenario_loss_function_volume
integer, parameter scenario_loss_function_volume
Type code for a loss rate function proportional to particle volume.
Definition: scenario.F90:35
pmc_scenario
The scenario_t structure and associated subroutines.
Definition: scenario.F90:9
pmc_scenario::scenario_t
Scenario data.
Definition: scenario.F90:86
pmc_gas_data
The gas_data_t structure and associated subroutines.
Definition: gas_data.F90:9
pmc_scenario::pmc_mpi_pack_scenario
subroutine pmc_mpi_pack_scenario(buffer, position, val)
Packs the given value into the buffer, advancing position.
Definition: scenario.F90:1564
pmc_util::warn_assert_msg
subroutine warn_assert_msg(code, condition_ok, warning_msg)
Prints a warning message if condition_ok is false.
Definition: util.F90:60
pmc_util::die_msg
subroutine die_msg(code, error_msg)
Error immediately.
Definition: util.F90:135
pmc_scenario::scenario_loss_function_constant
integer, parameter scenario_loss_function_constant
Type code for a constant loss function.
Definition: scenario.F90:33
pmc_scenario::scenario_loss_function_none
integer, parameter scenario_loss_function_none
Type code for a zero loss function.
Definition: scenario.F90:31
pmc_constants::dp
integer, parameter dp
Kind of a double precision real number.
Definition: constants.F90:12
pmc_aero_dist::aero_dist_interp_1d
subroutine aero_dist_interp_1d(aero_dist_list, time_list, rate_list, time, aero_dist, rate)
Determine the current aero_dist and rate by interpolating at the current time with the lists of aero_...
Definition: aero_dist.F90:177
pmc_spec_file
Reading formatted text input.
Definition: spec_file.F90:43
pmc_gas_state::gas_state_add_scaled
subroutine gas_state_add_scaled(gas_state, gas_state_delta, alpha)
Adds the given gas_state_delta scaled by alpha.
Definition: gas_state.F90:124
pmc_util::interp_1d
real(kind=dp) function interp_1d(x_vals, y_vals, x)
1D linear interpolation.
Definition: util.F90:627
pmc_bin_grid::bin_grid_size
elemental integer function bin_grid_size(bin_grid)
Return the number of bins in the grid, or -1 if the bin grid is not allocated.
Definition: bin_grid.F90:51
pmc_scenario::pmc_mpi_unpack_scenario
subroutine pmc_mpi_unpack_scenario(buffer, position, val)
Unpacks the given value from the buffer, advancing position.
Definition: scenario.F90:1624
pmc_env_state::env_state_t
Current environment state.
Definition: env_state.F90:29
pmc_mpi::pmc_mpi_pack_real
subroutine pmc_mpi_pack_real(buffer, position, val)
Packs the given value into the buffer, advancing position.
Definition: mpi.F90:761
pmc_util::assert
subroutine assert(code, condition_ok)
Errors unless condition_ok is true.
Definition: util.F90:104
pmc_scenario::scenario_update_gas_state
subroutine scenario_update_gas_state(scenario, delta_t, env_state, old_env_state, gas_data, gas_state)
Do gas emissions and background dilution.
Definition: scenario.F90:243
pmc_scenario::scenario_update_aero_state
subroutine scenario_update_aero_state(scenario, delta_t, env_state, old_env_state, aero_data, aero_state, n_emit, n_dil_in, n_dil_out, allow_doubling, allow_halving)
Do emissions and background dilution for a particle aerosol distribution.
Definition: scenario.F90:296
pmc_aero_state
The aero_state_t structure and assocated subroutines.
Definition: aero_state.F90:9
pmc_mpi::pmc_mpi_pack_size_real_array
integer function pmc_mpi_pack_size_real_array(val)
Determines the number of bytes required to pack the given value.
Definition: mpi.F90:527
pmc_scenario::scenario_loss_rate_max
real(kind=dp) function scenario_loss_rate_max(scenario, vol, aero_data, env_state)
Compute and return the max loss rate function for a given volume.
Definition: scenario.F90:920
pmc_util::interp_linear_disc
real(kind=dp) function interp_linear_disc(x_1, x_n, n, i)
Linear interpolation over discrete indices.
Definition: util.F90:664
pmc_scenario::scenario_update_env_state
subroutine scenario_update_env_state(scenario, env_state, time)
Update time-dependent contents of the environment. scenario_init_env_state() should have been called ...
Definition: scenario.F90:168
pmc_spec_file::spec_file_t
An input file with extra data for printing messages.
Definition: spec_file.F90:59
pmc_scenario::scenario_loss_rate_bin_max
subroutine scenario_loss_rate_bin_max(scenario, bin_grid, aero_data, env_state, loss_max)
Compute an upper bound on the maximum kernel value for each bin. Value over_scale is multiplied to th...
Definition: scenario.F90:957
pmc_scenario::scenario_loss_alg_threshold
real(kind=dp), parameter scenario_loss_alg_threshold
Parameter to switch between algorithms for particle loss. A value of 0 will always use the naive algo...
Definition: scenario.F90:44
pmc_gas_state
The gas_state_t structure and associated subroutines.
Definition: gas_state.F90:9
pmc_util::warn_msg
subroutine warn_msg(code, warning_msg, already_warned)
Prints a warning message.
Definition: util.F90:38
pmc_spec_file::spec_file_read_real
subroutine spec_file_read_real(file, name, var)
Read a real number from a spec file that must have the given name.
Definition: spec_file.F90:582
pmc_scenario::drydep_params_input_netcdf
subroutine drydep_params_input_netcdf(drydep_params, ncid)
Read dry deposition parameters from a NetCDF file.
Definition: scenario.F90:1480
pmc_util::integer_to_string
character(len=pmc_util_convert_string_len) function integer_to_string(val)
Convert an integer to a string format.
Definition: util.F90:767
pmc_util::real_to_string
character(len=pmc_util_convert_string_len) function real_to_string(val)
Convert a real to a string format.
Definition: util.F90:799
pmc_util::assert_msg
subroutine assert_msg(code, condition_ok, error_msg)
Errors unless condition_ok is true.
Definition: util.F90:78
pmc_scenario::drydep_params_t
Parameters for simulating dry deposition.
Definition: scenario.F90:47
pmc_aero_dist
The aero_dist_t structure and associated subroutines.
Definition: aero_dist.F90:18
pmc_scenario::scenario_integrated_loss_rate_drydep
real(kind=dp) function scenario_integrated_loss_rate_drydep(scenario, aero_mode, moment, density, env_state)
Compute and return the integrated dry deposition rate for a given lognormal aerosol mode (modal appro...
Definition: scenario.F90:638
pmc_gas_state::gas_state_scale
subroutine gas_state_scale(gas_state, alpha)
Scale a gas state.
Definition: gas_state.F90:86
pmc_scenario::scenario_update_aero_modes
subroutine scenario_update_aero_modes(aero_dist, del_t, env_state, density, scenario)
Update the modal aerosol distribution to account for particle loss.
Definition: scenario.F90:434
pmc_aero_state::aero_state_remove_particle_with_info
subroutine aero_state_remove_particle_with_info(aero_state, i_part, aero_info)
Remove the given particle and record the removal.
Definition: aero_state.F90:410
pmc_aero_state::aero_state_total_particles
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:288
pmc_chamber::chamber_t
Definition: chamber.F90:18
pmc_constants::const
type(const_t), save const
Fixed variable for accessing the constant's values.
Definition: constants.F90:81
pmc_chamber::chamber_loss_rate_sedi
real(kind=dp) function chamber_loss_rate_sedi(chamber, vol, density, aero_data, env_state)
Calculate the loss rate due to sedimentation in chamber. Based on Eq. 37b in Naumann 2003 J....
Definition: chamber.F90:118
pmc_scenario::pmc_mpi_pack_size_drydep
integer function pmc_mpi_pack_size_drydep(val)
Determines the number of bytes required to pack the given value.
Definition: scenario.F90:1695
pmc_spec_file::spec_file_open
subroutine spec_file_open(filename, file)
Open a spec file for reading.
Definition: spec_file.F90:112
pmc_env_state::env_state_sat_vapor_pressure
real(kind=dp) function env_state_sat_vapor_pressure(env_state)
Computes the current saturation vapor pressure (Pa).
Definition: env_state.F90:166
pmc_spec_file::spec_file_read_timed_real_array
subroutine spec_file_read_timed_real_array(file, name, times, vals)
Read an a time-indexed array of real data.
Definition: spec_file.F90:707
pmc_scenario::pmc_mpi_pack_size_scenario
integer function pmc_mpi_pack_size_scenario(val)
Determines the number of bytes required to pack the given value.
Definition: scenario.F90:1507
pmc_spec_file::spec_file_read_line_no_eof
subroutine spec_file_read_line_no_eof(file, line)
Read a spec_line from the spec_file. This will always succeed or error out, so should only be called ...
Definition: spec_file.F90:298
pmc_env_state
The env_state_t structure and associated subroutines.
Definition: env_state.F90:9
pmc_gas_state::gas_state_t
Current state of the gas mixing ratios in the system.
Definition: gas_state.F90:33
pmc_aero_data::aero_data_t
Aerosol material properties and associated data.
Definition: aero_data.F90:55
pmc_mpi::pmc_mpi_unpack_integer
subroutine pmc_mpi_unpack_integer(buffer, position, val)
Unpacks the given value from the buffer, advancing position.
Definition: mpi.F90:1139
pmc_aero_state::aero_state_copy_weight
subroutine aero_state_copy_weight(aero_state_from, aero_state_to)
Copies weighting information for an aero_state.
Definition: aero_state.F90:171
pmc_aero_dist::aero_dist_t
A complete aerosol distribution, consisting of several modes.
Definition: aero_dist.F90:33
pmc_scenario::scenario_loss_function_drydep
integer, parameter scenario_loss_function_drydep
Type code for a loss rate function based on dry deposition.
Definition: scenario.F90:37
pmc_util
Common utility subroutines.
Definition: util.F90:9
pmc_spec_file::spec_file_unread_line
subroutine spec_file_unread_line(file)
Set the line number and file read pointer back one entry.
Definition: spec_file.F90:470
pmc_scenario::scenario_init_env_state
subroutine scenario_init_env_state(scenario, env_state, time)
Initialize the time-dependent contents of the environment. Thereafter scenario_update_env_state() sho...
Definition: scenario.F90:145
pmc_spec_file::spec_file_die_msg
subroutine spec_file_die_msg(code, file, msg)
Exit with an error message containing filename and line number.
Definition: spec_file.F90:74
pmc_aero_data::aero_data_vol2diam
real(kind=dp) elemental function aero_data_vol2diam(aero_data, v)
Convert mass-equivalent volume (m^3) to geometric diameter (m).
Definition: aero_data.F90:121
pmc_spec_file::spec_file_read_string
subroutine spec_file_read_string(file, name, var)
Read a string from a spec file that must have a given name.
Definition: spec_file.F90:624
pmc_scenario::scenario_try_single_particle_loss
subroutine scenario_try_single_particle_loss(scenario, delta_t, aero_data, aero_state, env_state, i_part, over_prob)
Test a candidate particle to see if it should be removed, and remove if necessary....
Definition: scenario.F90:1088
pmc_chamber
Definition: chamber.F90:8
pmc_gas_state::gas_state_interp_1d
subroutine gas_state_interp_1d(gas_state_list, time_list, rate_list, time, gas_state, rate)
Determine the current gas_state and rate by interpolating at the current time with the lists of gas_s...
Definition: gas_state.F90:239
pmc_mpi::pmc_mpi_pack_size_integer
integer function pmc_mpi_pack_size_integer(val)
Determines the number of bytes required to pack the given value.
Definition: mpi.F90:345
pmc_mpi::pmc_mpi_pack_integer
subroutine pmc_mpi_pack_integer(buffer, position, val)
Packs the given value into the buffer, advancing position.
Definition: mpi.F90:711
pmc_aero_data::aero_data_rad2vol
real(kind=dp) elemental function aero_data_rad2vol(aero_data, r)
Convert geometric radius (m) to mass-equivalent volume (m^3).
Definition: aero_data.F90:136
pmc_aero_data
The aero_data_t structure and associated subroutines.
Definition: aero_data.F90:9
pmc_mpi::pmc_mpi_pack_real_array
subroutine pmc_mpi_pack_real_array(buffer, position, val)
Packs the given value into the buffer, advancing position.
Definition: mpi.F90:927
pmc_bin_grid::bin_grid_t
1D grid, either logarithmic or linear.
Definition: bin_grid.F90:33
pmc_chamber::chamber_loss_rate_wall
real(kind=dp) function chamber_loss_rate_wall(chamber, vol, aero_data, env_state)
Calculate the loss rate due to wall diffusion in chamber. Based on Eq. 37a in Naumann 2003 J....
Definition: chamber.F90:91
pmc_gas_state::gas_state_molar_conc_to_ppb
subroutine gas_state_molar_conc_to_ppb(gas_state, env_state)
Convert (mol m^{-3}) to (ppb).
Definition: gas_state.F90:181
pmc_scenario::scenario_particle_loss
subroutine scenario_particle_loss(scenario, delta_t, aero_data, aero_state, env_state)
Performs stochastic particle loss for one time-step. If a particle i_part has a scenario_loss_rate() ...
Definition: scenario.F90:1003
pmc_mpi::pmc_mpi_unpack_real
subroutine pmc_mpi_unpack_real(buffer, position, val)
Unpacks the given value from the buffer, advancing position.
Definition: mpi.F90:1189
pmc_scenario::scenario_loss_function_chamber
integer, parameter scenario_loss_function_chamber
Type code for a loss rate function for chamber experiments.
Definition: scenario.F90:39
pmc_aero_dist::aero_dist_contains_aero_mode_type
elemental logical function aero_dist_contains_aero_mode_type(aero_dist, aero_mode_type)
Whether any of the modes are of the given type.
Definition: aero_dist.F90:155
pmc_mpi::pmc_mpi_unpack_real_array
subroutine pmc_mpi_unpack_real_array(buffer, position, val)
Unpacks the given value from the buffer, advancing position.
Definition: mpi.F90:1356
pmc_scenario::scenario_loss_function_invalid
integer, parameter scenario_loss_function_invalid
Type code for an undefined or invalid loss function.
Definition: scenario.F90:29
pmc_aero_state::aero_state_t
The current collection of aerosol particles.
Definition: aero_state.F90:69
pmc_aero_state::aero_state_sort
subroutine aero_state_sort(aero_state, aero_data, bin_grid, all_procs_same)
Sorts the particles if necessary.
Definition: aero_state.F90:3292
pmc_rand::pmc_random
real(kind=dp) function pmc_random()
Returns a random number between 0 and 1.
Definition: rand.F90:139
pmc_aero_state::aero_state_sample_particles
subroutine aero_state_sample_particles(aero_state_from, aero_state_to, aero_data, sample_prob, removal_action)
Generates a random sample by removing particles from aero_state_from and adding them to aero_state_to...
Definition: aero_state.F90:858
pmc_scenario::scenario_loss_rate_drydep
real(kind=dp) function scenario_loss_rate_drydep(vol, density, aero_data, env_state, scenario)
Compute and return the dry deposition rate for a given particle. All equations used here are written ...
Definition: scenario.F90:544