PartMC  2.6.1
run_sect.F90
Go to the documentation of this file.
1 ! Copyright (C) 2005-2012 Nicole Riemer and Matthew West
2 ! Copyright (C) Andreas Bott
3 ! Licensed under the GNU General Public License version 2 or (at your
4 ! option) any later version. See the file COPYING for details.
5 
6 !> \file
7 !> The pmc_run_sect module.
8 
9 !> 1D sectional simulation.
10 !!
11 !! Sectional code based on \c coad1d.f by Andreas Bott
12 !! - http://www.meteo.uni-bonn.de/mitarbeiter/ABott/
13 !! - Released under the GPL to Nicole Riemer (personal communication)
14 !! - A. Bott, A flux method for the numerical solution of the
15 !! stochastic collection equation, J. Atmos. Sci. 55, 2284-2293,
16 !! 1998.
18 
19  use pmc_bin_grid
20  use pmc_aero_binned
21  use pmc_util
22  use pmc_aero_dist
23  use pmc_scenario
24  use pmc_env_state
25  use pmc_aero_data
26  use pmc_coag_kernel
27  use pmc_output
28  use pmc_gas_data
29  use pmc_gas_state
30 
31  !> Options controlling the operation of run_sect().
33  !> Final time (s).
34  real(kind=dp) :: t_max
35  !> Timestep for coagulation (s).
36  real(kind=dp) :: del_t
37  !> Output interval (0 disables) (s).
38  real(kind=dp) :: t_output
39  !> Progress interval (0 disables) (s).
40  real(kind=dp) :: t_progress
41  !> Whether to do coagulation.
42  logical :: do_coagulation
43  !> Output prefix.
44  character(len=300) :: prefix
45  !> Type of coagulation kernel.
46  integer :: coag_kernel_type
47  !> UUID of the simulation.
48  character(len=PMC_UUID_LEN) :: uuid
49  end type run_sect_opt_t
50 
51 contains
52 
53 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
54 
55  !> Run a sectional simulation.
56  subroutine run_sect(bin_grid, gas_data, aero_data, aero_dist, &
57  scenario, env_state, run_sect_opt)
58 
59  !> Bin grid.
60  type(bin_grid_t), intent(in) :: bin_grid
61  !> Gas data.
62  type(gas_data_t), intent(in) :: gas_data
63  !> Aerosol data.
64  type(aero_data_t), intent(in) :: aero_data
65  !> Aerosol distribution.
66  type(aero_dist_t), intent(inout) :: aero_dist
67  !> Environment data.
68  type(scenario_t), intent(inout) :: scenario
69  !> Environment state.
70  type(env_state_t), intent(inout) :: env_state
71  !> Options.
72  type(run_sect_opt_t), intent(in) :: run_sect_opt
73 
74  real(kind=dp) c(bin_grid_size(bin_grid),bin_grid_size(bin_grid))
75  integer ima(bin_grid_size(bin_grid),bin_grid_size(bin_grid))
76  real(kind=dp) g(bin_grid_size(bin_grid)), r(bin_grid_size(bin_grid))
77  real(kind=dp) e(bin_grid_size(bin_grid))
78  real(kind=dp) k_bin(bin_grid_size(bin_grid),bin_grid_size(bin_grid))
79  real(kind=dp) ck(bin_grid_size(bin_grid),bin_grid_size(bin_grid))
80  real(kind=dp) ec(bin_grid_size(bin_grid),bin_grid_size(bin_grid))
81  real(kind=dp) taug(bin_grid_size(bin_grid)), taup(bin_grid_size(bin_grid))
82  real(kind=dp) taul(bin_grid_size(bin_grid)), tauu(bin_grid_size(bin_grid))
83  real(kind=dp) prod(bin_grid_size(bin_grid)), ploss(bin_grid_size(bin_grid))
84  real(kind=dp) time, last_output_time, last_progress_time
85  type(env_state_t) :: old_env_state
86  type(aero_binned_t) :: aero_binned
87  type(gas_state_t) :: gas_state
88 
89  integer i, j, i_time, num_t, i_summary
90  logical do_output, do_progress
91 
92  call check_time_multiple("t_max", run_sect_opt%t_max, &
93  "del_t", run_sect_opt%del_t)
94  call check_time_multiple("t_output", run_sect_opt%t_output, &
95  "del_t", run_sect_opt%del_t)
96  call check_time_multiple("t_progress", run_sect_opt%t_progress, &
97  "del_t", run_sect_opt%del_t)
98 
99  ! g : spectral mass distribution (mg/cm^3)
100  ! e : droplet mass grid (mg)
101  ! r : droplet radius grid (um)
102  ! log_width : constant grid distance of logarithmic grid
103 
104  if (aero_data_n_spec(aero_data) /= 1) then
105  call die_msg(844211192, &
106  'run_sect() can only use one aerosol species')
107  end if
108 
109  ! output data structure
110  call gas_state_set_size(gas_state, gas_data_n_spec(gas_data))
111 
112  ! mass and radius grid
113  do i = 1,bin_grid_size(bin_grid)
114  r(i) = bin_grid%centers(i) * 1d6 ! radius in m to um
115  e(i) = aero_data_rad2vol(aero_data, bin_grid%centers(i)) &
116  * aero_data%density(1) * 1d6 ! vol in m^3 to mass in mg
117  end do
118 
119  ! initial mass distribution
120  call aero_binned_add_aero_dist(aero_binned, bin_grid, aero_data, &
121  aero_dist)
122 
123  call courant(bin_grid_size(bin_grid), bin_grid%widths(1), e, ima, c)
124 
125  ! initialize time
126  last_progress_time = 0d0
127  time = 0d0
128  i_summary = 1
129 
130  ! precompute kernel values for all pairs of bins
131  call bin_kernel(bin_grid_size(bin_grid), bin_grid%centers, aero_data, &
132  run_sect_opt%coag_kernel_type, env_state, k_bin)
133  call smooth_bin_kernel(bin_grid_size(bin_grid), k_bin, ck)
134  do i = 1,bin_grid_size(bin_grid)
135  do j = 1,bin_grid_size(bin_grid)
136  ck(i,j) = ck(i,j) * 1d6 ! m^3/s to cm^3/s
137  end do
138  end do
139 
140  ! multiply kernel with constant timestep and logarithmic grid distance
141  do i = 1,bin_grid_size(bin_grid)
142  do j = 1,bin_grid_size(bin_grid)
143  ck(i,j) = ck(i,j) * run_sect_opt%del_t * bin_grid%widths(i)
144  end do
145  end do
146 
147  ! initial output
148  call check_event(time, run_sect_opt%del_t, run_sect_opt%t_output, &
149  last_output_time, do_output)
150  if (do_output) then
151  call output_sectional(run_sect_opt%prefix, bin_grid, aero_data, &
152  aero_binned, gas_data, gas_state, env_state, i_summary, &
153  time, run_sect_opt%t_output, run_sect_opt%uuid)
154  end if
155 
156  ! main time-stepping loop
157  num_t = nint(run_sect_opt%t_max / run_sect_opt%del_t)
158  do i_time = 1, num_t
159 
160  if (run_sect_opt%do_coagulation) then
161  g = aero_binned%vol_conc(:,1) * aero_data%density(1)
162  call coad(bin_grid_size(bin_grid), run_sect_opt%del_t, taug, taup, &
163  taul, tauu, prod, ploss, c, ima, g, r, e, ck, ec)
164  aero_binned%vol_conc(:,1) = g / aero_data%density(1)
165  aero_binned%num_conc = aero_binned%vol_conc(:,1) &
166  / aero_data_rad2vol(aero_data, bin_grid%centers)
167  end if
168 
169  time = run_sect_opt%t_max * real(i_time, kind=dp) &
170  / real(num_t, kind=dp)
171 
172  old_env_state = env_state
173  call scenario_update_env_state(scenario, env_state, time)
174  call scenario_update_gas_state(scenario, run_sect_opt%del_t, &
175  env_state, old_env_state, gas_data, gas_state)
176  call scenario_update_aero_binned(scenario, run_sect_opt%del_t, &
177  env_state, old_env_state, bin_grid, aero_data, aero_binned)
178 
179  ! print output
180  call check_event(time, run_sect_opt%del_t, run_sect_opt%t_output, &
181  last_output_time, do_output)
182  if (do_output) then
183  i_summary = i_summary + 1
184  call output_sectional(run_sect_opt%prefix, bin_grid, aero_data, &
185  aero_binned, gas_data, gas_state, env_state, i_summary, &
186  time, run_sect_opt%t_output, run_sect_opt%uuid)
187  end if
188 
189  ! print progress to stdout
190  call check_event(time, run_sect_opt%del_t, run_sect_opt%t_progress, &
191  last_progress_time, do_progress)
192  if (do_progress) then
193  write(*,'(a6,a8)') 'step', 'time'
194  write(*,'(i6,f8.1)') i_time, time
195  end if
196  end do
197 
198  end subroutine run_sect
199 
200 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
201 
202  !> Collision subroutine, exponential approach.
203  subroutine coad(n_bin, dt, taug, taup, taul, tauu, prod, ploss, &
204  c, ima, g, r, e, ck, ec)
205 
206  integer n_bin
207  real(kind=dp) dt
208  real(kind=dp) taug(n_bin)
209  real(kind=dp) taup(n_bin)
210  real(kind=dp) taul(n_bin)
211  real(kind=dp) tauu(n_bin)
212  real(kind=dp) prod(n_bin)
213  real(kind=dp) ploss(n_bin)
214  real(kind=dp) c(n_bin,n_bin)
215  integer ima(n_bin,n_bin)
216  real(kind=dp) g(n_bin)
217  real(kind=dp) r(n_bin)
218  real(kind=dp) e(n_bin)
219  real(kind=dp) ck(n_bin,n_bin)
220  real(kind=dp) ec(n_bin,n_bin)
221 
222  real(kind=dp), parameter :: gmin = 1d-60
223 
224  integer i, i0, i1, j, k, kp
225  real(kind=dp) x0, gsi, gsj, gsk, gk, x1, flux
226 
227  do i = 1,n_bin
228  prod(i) = 0d0
229  ploss(i) = 0d0
230  end do
231 
232  ! lower and upper integration limit i0,i1
233  do i0 = 1,(n_bin - 1)
234  if (g(i0) .gt. gmin) goto 2000
235  end do
236 2000 continue
237  do i1 = (n_bin - 1),1,-1
238  if (g(i1) .gt. gmin) goto 2010
239  end do
240 2010 continue
241 
242  do i = i0,i1
243  do j = i,i1
244  k = ima(i,j) ! k = 0 means that i + j goes nowhere
245  kp = k + 1
246 
247  x0 = ck(i,j) * g(i) * g(j)
248  x0 = min(x0, g(i) * e(j))
249 
250  if (j .ne. k) x0 = min(x0, g(j) * e(i))
251  gsi = x0 / e(j)
252  gsj = x0 / e(i)
253  gsk = gsi + gsj
254 
255  ! loss from positions i, j
256  ploss(i) = ploss(i) + gsi
257  ploss(j) = ploss(j) + gsj
258  g(i) = g(i) - gsi
259  g(j) = g(j) - gsj
260 
261  if (k > 0) then ! do we have a valid bin for the coagulation result?
262  gk = g(k) + gsk
263 
264  if (gk .gt. gmin) then
265  x1 = log(g(kp) / gk + 1d-60)
266  flux = gsk / x1 * (exp(0.5d0 * x1) &
267  - exp(x1 * (0.5d0 - c(i,j))))
268  flux = min(flux, gk)
269  g(k) = gk - flux
270  g(kp) = g(kp) + flux
271  ! gain for positions i, j
272  prod(k) = prod(k) + gsk - flux
273  prod(kp) = prod(kp) + flux
274  end if
275  end if
276  end do
277  end do
278 
279  end subroutine coad
280 
281 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
282 
283  !> Determines the Courant number for each bin pair.
284  subroutine courant(n_bin, log_width, e, ima, c)
285 
286  !> Number of bins.
287  integer, intent(in) :: n_bin
288  !> Bin scale factor.
289  real(kind=dp), intent(in) :: log_width
290  !> Droplet mass grid (mg).
291  real(kind=dp), intent(in) :: e(n_bin)
292  !> i + j goes in bin ima(i,j).
293  integer, intent(out) :: ima(n_bin,n_bin)
294  !> Courant number for bin pairs.
295  real(kind=dp), intent(out) :: c(n_bin,n_bin)
296 
297  integer i, j, k, kk
298  real(kind=dp) x0
299 
300  c = 0d0 ! added to avoid uninitialized access errors
301  ima = 0 ! ima(i,j) = 0 means that particles i + j go nowhere
302  do i = 1,n_bin
303  do j = i,n_bin
304  x0 = e(i) + e(j)
305  ! this is basically the same as particle_in_bin(), but that
306  ! is actually slightly different than what was always done
307  ! here
308 
309  ! MW 2011-04-28: I think the above comment no longer
310  ! applies, and we can make this just
311  ! bin_grid_find(). FIXME.
312  k = find_1d(n_bin, e, x0)
313  if (k < n_bin) then
314  k = k + 1
315  if (c(i,j) .lt. 1d0 - 1d-08) then
316  kk = k - 1
317  c(i,j) = log(x0 / e(k-1)) / (3d0 * log_width)
318  else
319  c(i,j) = 0d0
320  kk = k
321  end if
322  ima(i,j) = min(n_bin - 1, kk)
323  end if
324  c(j,i) = c(i,j)
325  ima(j,i) = ima(i,j)
326  end do
327  end do
328 
329  end subroutine courant
330 
331 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
332 
333  !> Smooths kernel values for bin pairs, and halves the self-rate.
334  subroutine smooth_bin_kernel(n_bin, k, k_smooth)
335 
336  !> Number of bins.
337  integer, intent(in) :: n_bin
338  !> Kernel values.
339  real(kind=dp), intent(in) :: k(n_bin,n_bin)
340  !> Smoothed kernel values.
341  real(kind=dp), intent(out) :: k_smooth(n_bin,n_bin)
342 
343  integer i, j, im, ip, jm, jp
344 
345  do i = 1,n_bin
346  do j = 1,n_bin
347  jm = max0(j - 1, 1)
348  im = max0(i - 1, 1)
349  jp = min0(j + 1, n_bin)
350  ip = min0(i + 1, n_bin)
351  k_smooth(i,j) = 0.125d0 * (k(i,jm) + k(im,j) &
352  + k(ip,j) + k(i,jp)) &
353  + 0.5d0 * k(i,j)
354  if (i .eq. j) then
355  k_smooth(i,j) = 0.5d0 * k_smooth(i,j)
356  end if
357  end do
358  end do
359 
360  end subroutine smooth_bin_kernel
361 
362 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
363 
364 end module pmc_run_sect
pmc_run_sect::run_sect_opt_t
Options controlling the operation of run_sect().
Definition: run_sect.F90:32
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:332
pmc_run_sect
1D sectional simulation.
Definition: run_sect.F90:17
pmc_aero_data::aero_data_n_spec
elemental integer function aero_data_n_spec(aero_data)
Return the number of aerosol species, or -1 if uninitialized.
Definition: aero_data.F90:236
pmc_gas_data::gas_data_t
Constant gas data.
Definition: gas_data.F90:35
pmc_gas_state::gas_state_set_size
subroutine gas_state_set_size(gas_state, n_spec)
Sets the sizes of the gas state.
Definition: gas_state.F90:56
pmc_scenario
The scenario_t structure and associated subroutines.
Definition: scenario.F90:9
pmc_scenario::scenario_t
Scenario data.
Definition: scenario.F90:54
pmc_gas_data
The gas_data_t structure and associated subroutines.
Definition: gas_data.F90:9
pmc_output::output_sectional
subroutine output_sectional(prefix, bin_grid, aero_data, aero_binned, gas_data, gas_state, env_state, index, time, del_t, uuid)
Write the current sectional data.
Definition: output.F90:665
pmc_util::die_msg
subroutine die_msg(code, error_msg)
Error immediately.
Definition: util.F90:134
pmc_constants::dp
integer, parameter dp
Kind of a double precision real number.
Definition: constants.F90:12
pmc_coag_kernel
Generic coagulation kernel.
Definition: coag_kernel.F90:9
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_env_state::env_state_t
Current environment state.
Definition: env_state.F90:29
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:209
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:134
pmc_run_sect::smooth_bin_kernel
subroutine smooth_bin_kernel(n_bin, k, k_smooth)
Smooths kernel values for bin pairs, and halves the self-rate.
Definition: run_sect.F90:335
pmc_gas_state
The gas_state_t structure and associated subroutines.
Definition: gas_state.F90:9
pmc_aero_binned::aero_binned_add_aero_dist
subroutine aero_binned_add_aero_dist(aero_binned, bin_grid, aero_data, aero_dist)
Add an aero_dist_t to an aero_binned_t.
Definition: aero_binned.F90:229
pmc_util::check_time_multiple
subroutine check_time_multiple(first_name, first_time, second_name, second_time)
Check that the first time interval is close to an integer multiple of the second, and warn if it is n...
Definition: util.F90:376
pmc_aero_dist
The aero_dist_t structure and associated subroutines.
Definition: aero_dist.F90:18
pmc_coag_kernel::bin_kernel
subroutine bin_kernel(n_bin, bin_r, aero_data, coag_kernel_type, env_state, k)
Computes an array of kernel values for each bin pair. k(i,j) is the kernel value at the centers of bi...
Definition: coag_kernel.F90:218
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:49
pmc_output
Write data in NetCDF format.
Definition: output.F90:68
pmc_aero_dist::aero_dist_t
A complete aerosol distribution, consisting of several modes.
Definition: aero_dist.F90:33
pmc_util
Common utility subroutines.
Definition: util.F90:9
pmc_util::check_event
subroutine check_event(time, timestep, interval, last_time, do_event)
Computes whether an event is scheduled to take place.
Definition: util.F90:407
pmc_gas_data::gas_data_n_spec
elemental integer function gas_data_n_spec(gas_data)
Return the number of gas species.
Definition: gas_data.F90:109
pmc_aero_binned
The aero_binned_t structure and associated subroutines.
Definition: aero_binned.F90:9
pmc_run_sect::coad
subroutine coad(n_bin, dt, taug, taup, taul, tauu, prod, ploss, c, ima, g, r, e, ck, ec)
Collision subroutine, exponential approach.
Definition: run_sect.F90:205
pmc_aero_binned::aero_binned_t
Aerosol number and volume distributions stored per bin.
Definition: aero_binned.F90:37
pmc_bin_grid
The bin_grid_t structure and associated subroutines.
Definition: bin_grid.F90:9
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:122
pmc_aero_data
The aero_data_t structure and associated subroutines.
Definition: aero_data.F90:9
pmc_run_sect::run_sect
subroutine run_sect(bin_grid, gas_data, aero_data, aero_dist, scenario, env_state, run_sect_opt)
Run a sectional simulation.
Definition: run_sect.F90:58
pmc_bin_grid::bin_grid_t
1D grid, either logarithmic or linear.
Definition: bin_grid.F90:33
pmc_run_sect::courant
subroutine courant(n_bin, log_width, e, ima, c)
Determines the Courant number for each bin pair.
Definition: run_sect.F90:285
pmc_util::find_1d
integer function find_1d(n, x_vals, x)
Find the position of a real number in an arbitrary 1D array.
Definition: util.F90:592