PartMC  2.6.1
condense.F90
Go to the documentation of this file.
1 ! Copyright (C) 2005-2012 Nicole Riemer and Matthew West
2 ! Copyright (C) 2009 Joseph Ching
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_condense module.
8 
9 !> Water condensation onto aerosol particles.
10 !!
11 !! The model here assumes that the temperature \f$ T \f$ and pressure
12 !! \f$ p \f$ are prescribed as functions of time, while water content
13 !! per-particle and relative humidity are to be calculated by
14 !! integrating their rates of change.
15 !!
16 !! The state of the system is defined by the per-particle wet
17 !! diameters \f$ D_i \f$ and the relative humidity \f$ H \f$. The
18 !! state vector stores these in the order \f$ (D_1,\ldots,D_n,H)
19 !! \f$. The time-derivative of the state vector and the Jacobian
20 !! (derivative of the time-derivative with repsect to the state) all
21 !! conform to this ordering.
22 !!
23 !! The SUNDIALS ODE solver is used to compute the system evolution
24 !! using an implicit method. The system Jacobian is explicitly
25 !! inverted as its structure is very simple.
26 !!
27 !! All equations used in this file are written in detail in the file
28 !! \c doc/condense.tex.
30 
31  use pmc_aero_state
32  use pmc_env_state
33  use pmc_aero_data
34  use pmc_util
36  use pmc_constants
37 #ifdef PMC_USE_SUNDIALS
38  use iso_c_binding
39 #endif
40 
41  !> Whether to numerically test the Jacobian-solve function during
42  !> execution (for debugging only).
43  logical, parameter :: condense_do_test_jac_solve = .false.
44  !> Whether to print call-counts for helper routines during execution
45  !> (for debugging only).
46  logical, parameter :: condense_do_test_counts = .false.
47 
48  !> Result code indicating successful completion.
49  integer, parameter :: pmc_condense_solver_success = 0
50  !> Result code indicating failure to allocate \c y vector.
51  integer, parameter :: pmc_condense_solver_init_y = 1
52  !> Result code indicating failure to allocate \c abstol vector.
53  integer, parameter :: pmc_condense_solver_init_abstol = 2
54  !> Result code indicating failure to create the solver.
55  integer, parameter :: pmc_condense_solver_init_cvode_mem = 3
56  !> Result code indicating failure to initialize the solver.
57  integer, parameter :: pmc_condense_solver_init_cvode = 4
58  !> Result code indicating failure to set tolerances.
59  integer, parameter :: pmc_condense_solver_svtol = 5
60  !> Result code indicating failure to set maximum steps.
61  integer, parameter :: pmc_condense_solver_set_max_steps = 6
62  !> Result code indicating failure of the solver.
63  integer, parameter :: pmc_condense_solver_fail = 7
64 
65  !> Internal-use structure for storing the inputs for the
66  !> rate-calculation function.
68  !> Temperature (K).
69  real(kind=dp) :: t
70  !> Rate of change of temperature (K s^{-1}).
71  real(kind=dp) :: tdot
72  !> Relative humidity (1).
73  real(kind=dp) :: h
74  !> Pressure (Pa).
75  real(kind=dp) :: p
76  !> Rate of change of pressure (Pa s^{-1}).
77  real(kind=dp) :: pdot
78  !> Computational volume (m^3).
79  real(kind=dp) :: v_comp
80  !> Particle diameter (m).
81  real(kind=dp) :: d
82  !> Particle dry diameter (m).
83  real(kind=dp) :: d_dry
84  !> Kappa parameter (1).
85  real(kind=dp) :: kappa
87 
88  !> Internal-use structure for storing the outputs from the
89  !> rate-calculation function.
91  !> Change rate of diameter (m s^{-1}).
92  real(kind=dp) :: ddot
93  !> Change rate of relative humidity due to this particle (s^{-1}).
94  real(kind=dp) :: hdot_i
95  !> Change rate of relative humidity due to environment changes (s^{-1}).
96  real(kind=dp) :: hdot_env
97  !> Sensitivity of \c Ddot to input \c D (m s^{-1} m^{-1}).
98  real(kind=dp) :: dddot_dd
99  !> Sensitivity of \c Ddot to input \c H (m s^{-1}).
100  real(kind=dp) :: dddot_dh
101  !> Sensitivity of \c Hdot_i to input \c D (s^{-1} m^{-1}).
102  real(kind=dp) :: dhdoti_dd
103  !> Sensitivity of \c Hdot_i to input \c D (s^{-1}).
104  real(kind=dp) :: dhdoti_dh
105  !> Sensitivity of \c Hdot_env to input \c D (s^{-1} m^{-1}).
106  real(kind=dp) :: dhdotenv_dd
107  !> Sensitivity of \c Hdot_env to input \c D (s^{-1}).
108  real(kind=dp) :: dhdotenv_dh
109  end type condense_rates_outputs_t
110 
111  !> Internal-use variable for storing the aerosol data during calls
112  !> to the ODE solver.
114  !> Internal-use variable for storing the initial environment state
115  !> during calls to the ODE solver.
117  !> Internal-use variable for storing the rate of change of the
118  !> temperature during calls to the ODE solver.
119  real(kind=dp) :: condense_saved_tdot
120  !> Internal-use variable for storing the rate of change of the
121  !> pressure during calls to the ODE solver.
122  real(kind=dp) :: condense_saved_pdot
123  !> Internal-use variable for storing the per-particle kappa values
124  !> during calls to the ODE solver.
125  real(kind=dp), allocatable :: condense_saved_kappa(:)
126  !> Internal-use variable for storing the per-particle dry diameters
127  !> during calls to the ODE solver.
128  real(kind=dp), allocatable :: condense_saved_d_dry(:)
129  !> Internal-use variable for storing the per-particle number
130  !> concentrations during calls to the ODE solver.
131  real(kind=dp), allocatable :: condense_saved_num_conc(:)
132 
133  !> Internal-use variable for counting calls to the vector field
134  !> subroutine.
135  integer, save :: condense_count_vf
136  !> Internal-use variable for counting calls to the Jacobian-solving
137  !> subroutine.
138  integer, save :: condense_count_solve
139 
140 contains
141 
142 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
143 
144  !> Do condensation to all the particles for a given time interval,
145  !> including updating the environment to account for the lost
146  !> water vapor.
147  subroutine condense_particles(aero_state, aero_data, env_state_initial, &
148  env_state_final, del_t)
149 
150  !> Aerosol state.
151  type(aero_state_t), intent(inout) :: aero_state
152  !> Aerosol data.
153  type(aero_data_t), intent(in) :: aero_data
154  !> Environment state at the start of the timestep.
155  type(env_state_t), intent(in) :: env_state_initial
156  !> Environment state at the end of the timestep. The rel_humid
157  !> value will be ignored and overwritten with the
158  !> condensation-computed value.
159  type(env_state_t), intent(inout) :: env_state_final
160  !> Total time to integrate.
161  real(kind=dp), intent(in) :: del_t
162 
163  integer :: i_part, n_eqn, i_eqn
164  real(kind=dp) :: state(aero_state_n_part(aero_state) + 1)
165  real(kind=dp) :: init_time, final_time
166  real(kind=dp) :: abs_tol_vector(aero_state_n_part(aero_state) + 1)
167  real(kind=dp) :: num_conc
168  real(kind=dp) :: reweight_num_conc(aero_state_n_part(aero_state))
169  real(kind=dp) :: water_vol_conc_initial, water_vol_conc_final
170  real(kind=dp) :: vapor_vol_conc_initial, vapor_vol_conc_final
171  real(kind=dp) :: d_water_vol_conc, d_vapor_vol_conc
172  real(kind=dp) :: v_comp_ratio, water_rel_error
173 #ifdef PMC_USE_SUNDIALS
174  real(kind=c_double), target :: state_f(aero_state_n_part(aero_state) + 1)
175  real(kind=c_double), target :: abstol_f(aero_state_n_part(aero_state) + 1)
176  type(c_ptr) :: state_f_p, abstol_f_p
177  integer(kind=c_int) :: n_eqn_f, solver_stat
178  real(kind=c_double) :: reltol_f, t_initial_f, t_final_f
179 #endif
180 
181 #ifdef PMC_USE_SUNDIALS
182 #ifndef DOXYGEN_SKIP_DOC
183  interface
184  integer(kind=c_int) function condense_solver(neq, x_f, abstol_f, &
185  reltol_f, t_initial_f, t_final_f) bind(c)
186  use iso_c_binding
187  integer(kind=c_int), value :: neq
188  type(c_ptr), value :: x_f
189  type(c_ptr), value :: abstol_f
190  real(kind=c_double), value :: reltol_f
191  real(kind=c_double), value :: t_initial_f
192  real(kind=c_double), value :: t_final_f
193  end function condense_solver
194  end interface
195 #endif
196 #endif
197 
198  ! initial water concentration in the aerosol particles
199  water_vol_conc_initial = 0d0
200  do i_part = 1,aero_state_n_part(aero_state)
201  num_conc = aero_weight_array_num_conc(aero_state%awa, &
202  aero_state%apa%particle(i_part), aero_data)
203  water_vol_conc_initial = water_vol_conc_initial &
204  + aero_state%apa%particle(i_part)%vol(aero_data%i_water) * num_conc
205  end do
206 
207  ! save data for use within the timestepper
208  condense_saved_aero_data = aero_data
209  condense_saved_env_state_initial = env_state_initial
211  = (env_state_final%temp - env_state_initial%temp) / del_t
213  = (env_state_final%pressure - env_state_initial%pressure) / del_t
214 
215  ! construct initial state vector from aero_state and env_state
216  allocate(condense_saved_kappa(aero_state_n_part(aero_state)))
217  allocate(condense_saved_d_dry(aero_state_n_part(aero_state)))
218  allocate(condense_saved_num_conc(aero_state_n_part(aero_state)))
219  ! work backwards for consistency with the later number
220  ! concentration adjustment, which has specific ordering
221  ! requirements
222  do i_part = aero_state_n_part(aero_state),1,-1
223  condense_saved_kappa(i_part) &
224  = aero_particle_solute_kappa(aero_state%apa%particle(i_part), &
225  aero_data)
226  condense_saved_d_dry(i_part) = aero_data_vol2diam(aero_data, &
227  aero_particle_solute_volume(aero_state%apa%particle(i_part), &
228  aero_data))
229  condense_saved_num_conc(i_part) &
230  = aero_weight_array_num_conc(aero_state%awa, &
231  aero_state%apa%particle(i_part), aero_data)
232  state(i_part) = aero_particle_diameter(&
233  aero_state%apa%particle(i_part), aero_data)
234  abs_tol_vector(i_part) = max(1d-30, &
235  1d-8 * (state(i_part) - condense_saved_d_dry(i_part)))
236  end do
237  state(aero_state_n_part(aero_state) + 1) = env_state_initial%rel_humid
238  abs_tol_vector(aero_state_n_part(aero_state) + 1) = 1d-10
239 
240 #ifdef PMC_USE_SUNDIALS
241  ! call SUNDIALS solver
242  n_eqn = aero_state_n_part(aero_state) + 1
243  n_eqn_f = int(n_eqn, kind=c_int)
244  reltol_f = real(1d-8, kind=c_double)
245  t_initial_f = real(0, kind=c_double)
246  t_final_f = real(del_t, kind=c_double)
247  do i_eqn = 1,n_eqn
248  state_f(i_eqn) = real(state(i_eqn), kind=c_double)
249  abstol_f(i_eqn) = real(abs_tol_vector(i_eqn), kind=c_double)
250  end do
251  state_f_p = c_loc(state_f)
252  abstol_f_p = c_loc(abstol_f)
255  solver_stat = condense_solver(n_eqn_f, state_f_p, abstol_f_p, reltol_f, &
256  t_initial_f, t_final_f)
257  call condense_check_solve(solver_stat)
258  if (condense_do_test_counts) then
259  write(0,*) 'condense_count_vf ', condense_count_vf
260  write(0,*) 'condense_count_solve ', condense_count_solve
261  end if
262  do i_eqn = 1,n_eqn
263  state(i_eqn) = real(state_f(i_eqn), kind=dp)
264  end do
265 #endif
266 
267  ! unpack result state vector into env_state_final
268  env_state_final%rel_humid = state(aero_state_n_part(aero_state) + 1)
269 
270  ! unpack result state vector into aero_state, compute the final
271  ! water volume concentration, and adjust particle number to
272  ! account for number concentration changes
273  water_vol_conc_final = 0d0
274  call aero_state_num_conc_for_reweight(aero_state, aero_data, &
275  reweight_num_conc)
276  do i_part = 1,aero_state_n_part(aero_state)
277  num_conc = aero_weight_array_num_conc(aero_state%awa, &
278  aero_state%apa%particle(i_part), aero_data)
279 
280  ! translate output back to particle
281  aero_state%apa%particle(i_part)%vol(aero_data%i_water) &
282  = aero_data_diam2vol(aero_data, state(i_part)) &
283  - aero_particle_solute_volume(aero_state%apa%particle(i_part), &
284  aero_data)
285 
286  ! ensure volumes stay positive
287  aero_state%apa%particle(i_part)%vol(aero_data%i_water) = max(0d0, &
288  aero_state%apa%particle(i_part)%vol(aero_data%i_water))
289 
290  ! add up total water volume, using old number concentrations
291  water_vol_conc_final = water_vol_conc_final &
292  + aero_state%apa%particle(i_part)%vol(aero_data%i_water) * num_conc
293  end do
294  ! adjust particles to account for weight changes
295  call aero_state_reweight(aero_state, aero_data, reweight_num_conc)
296 
297  ! Check that water removed from particles equals water added to
298  ! vapor. Note that water concentration is not conserved (due to
299  ! computational volume changes), and we need to consider particle
300  ! weightings correctly.
301  v_comp_ratio = env_state_final%temp * env_state_initial%pressure &
302  / (env_state_initial%temp * env_state_final%pressure)
303  vapor_vol_conc_initial = aero_data%molec_weight(aero_data%i_water) &
304  / (const%univ_gas_const * env_state_initial%temp) &
305  * env_state_sat_vapor_pressure(env_state_initial) &
306  * env_state_initial%rel_humid &
307  / aero_particle_water_density(aero_data)
308  vapor_vol_conc_final = aero_data%molec_weight(aero_data%i_water) &
309  / (const%univ_gas_const * env_state_final%temp) &
310  * env_state_sat_vapor_pressure(env_state_final) &
311  * env_state_final%rel_humid &
312  * v_comp_ratio / aero_particle_water_density(aero_data)
313  d_vapor_vol_conc = vapor_vol_conc_final - vapor_vol_conc_initial
314  d_water_vol_conc = water_vol_conc_final - water_vol_conc_initial
315  water_rel_error = (d_vapor_vol_conc + d_water_vol_conc) &
316  / (vapor_vol_conc_final + water_vol_conc_final)
317  call warn_assert_msg(477865387, abs(water_rel_error) < 1d-6, &
318  "condensation water imbalance too high: " &
319  // trim(real_to_string(water_rel_error)))
320 
321  deallocate(condense_saved_kappa)
322  deallocate(condense_saved_d_dry)
323  deallocate(condense_saved_num_conc)
324 
325  end subroutine condense_particles
326 
327 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
328 
329 #ifdef PMC_USE_SUNDIALS
330  !> Check the return code from the condense_solver() function.
331  subroutine condense_check_solve(value)
332 
333  !> Return code to check.
334  integer(kind=c_int), intent(in) :: value
335 
336  if (value == pmc_condense_solver_success) then
337  return
338  elseif (value == pmc_condense_solver_init_y) then
339  call die_msg(123749472, "condense_solver: " &
340  // "failed to allocate y vector")
341  elseif (value == pmc_condense_solver_init_abstol) then
342  call die_msg(563665949, "condense_solver: " &
343  // "failed to allocate abstol vector")
344  elseif (value == pmc_condense_solver_init_cvode_mem) then
345  call die_msg(700541443, "condense_solver: " &
346  // "failed to create the solver")
347  elseif (value == pmc_condense_solver_init_cvode) then
348  call die_msg(297559183, "condense_solver: " &
349  // "failure to initialize the solver")
350  elseif (value == pmc_condense_solver_svtol) then
351  call die_msg(848342417, "condense_solver: " &
352  // "failed to set tolerances")
353  elseif (value == pmc_condense_solver_set_max_steps) then
354  call die_msg(275591501, "condense_solver: " &
355  // "failed to set maximum steps")
356  elseif (value == pmc_condense_solver_fail) then
357  call die_msg(862254233, "condense_solver: solver failed")
358  else
359  call die_msg(635697577, "condense_solver: unknown return code: " &
360  // trim(integer_to_string(value)))
361  end if
362 
363  end subroutine condense_check_solve
364 #endif
365 
366 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
367 
368  !> Compute the rate of change of particle diameter and relative
369  !> humidity for a single particle, together with the derivatives of
370  !> the rates with respect to the input variables.
371  subroutine condense_rates(inputs, outputs)
372 
373  !> Inputs to rates.
374  type(condense_rates_inputs_t), intent(in) :: inputs
375  !> Outputs rates.
376  type(condense_rates_outputs_t), intent(out) :: outputs
377 
378  real(kind=dp) :: rho_w, m_w, p_0, dp0_dt_div_p0, rho_air, k_a, d_v, u
379  real(kind=dp) :: v, w, x, y, z, k_ap, dkap_dd, d_vp, ddvp_dd
380  real(kind=dp) :: a_w, daw_dd, delta_star, h, dh_ddelta, dh_dd
381  real(kind=dp) :: dh_dh, ddeltastar_dd, ddeltastar_dh
382  integer :: newton_step
383 
384  rho_w = const%water_density
385  m_w = const%water_molec_weight
386  p_0 = const%water_eq_vap_press &
387  * 10d0**(7.45d0 * (inputs%T - const%water_freeze_temp) &
388  / (inputs%T - 38d0))
389  dp0_dt_div_p0 = 7.45d0 * log(10d0) * (const%water_freeze_temp - 38d0) &
390  / (inputs%T - 38d0)**2
391  rho_air = const%air_molec_weight * inputs%p &
392  / (const%univ_gas_const * inputs%T)
393 
394  k_a = 1d-3 * (4.39d0 + 0.071d0 * inputs%T)
395  d_v = 0.211d-4 / (inputs%p / const%air_std_press) &
396  * (inputs%T / 273d0)**1.94d0
397  u = const%water_latent_heat * rho_w / (4d0 * inputs%T)
398  v = 4d0 * m_w * p_0 / (rho_w * const%univ_gas_const * inputs%T)
399  w = const%water_latent_heat * m_w / (const%univ_gas_const * inputs%T)
400  x = 4d0 * m_w * const%water_surf_eng &
401  / (const%univ_gas_const * inputs%T * rho_w)
402  y = 2d0 * k_a / (const%accom_coeff * rho_air &
403  * const%air_spec_heat) &
404  * sqrt(2d0 * const%pi * const%air_molec_weight &
405  / (const%univ_gas_const * inputs%T))
406  z = 2d0 * d_v / const%accom_coeff * sqrt(2d0 * const%pi * m_w &
407  / (const%univ_gas_const * inputs%T))
408 
409  outputs%Hdot_env = - dp0_dt_div_p0 * inputs%Tdot * inputs%H &
410  + inputs%H * inputs%pdot / inputs%p
411  outputs%dHdotenv_dD = 0d0
412  outputs%dHdotenv_dH = - dp0_dt_div_p0 * inputs%Tdot &
413  + inputs%pdot / inputs%p
414 
415  if (inputs%D <= inputs%D_dry) then
416  k_ap = k_a / (1d0 + y / inputs%D_dry)
417  dkap_dd = 0d0
418  d_vp = d_v / (1d0 + z / inputs%D_dry)
419  ddvp_dd = 0d0
420  a_w = 0d0
421  daw_dd = 0d0
422 
423  delta_star = u * v * d_vp * inputs%H / k_ap
424 
425  outputs%Ddot = k_ap * delta_star / (u * inputs%D_dry)
426  outputs%Hdot_i = - 2d0 * const%pi / (v * inputs%V_comp) &
427  * inputs%D_dry**2 * outputs%Ddot
428 
429  dh_ddelta = k_ap
430  dh_dd = 0d0
431  dh_dh = - u * v * d_vp
432 
433  ddeltastar_dd = - dh_dd / dh_ddelta
434  ddeltastar_dh = - dh_dh / dh_ddelta
435 
436  outputs%dDdot_dD = 0d0
437  outputs%dDdot_dH = k_ap / (u * inputs%D_dry) * ddeltastar_dh
438  outputs%dHdoti_dD = - 2d0 * const%pi / (v * inputs%V_comp) &
439  * inputs%D_dry**2 * outputs%dDdot_dD
440  outputs%dHdoti_dH = - 2d0 * const%pi / (v * inputs%V_comp) &
441  * inputs%D_dry**2 * outputs%dDdot_dH
442 
443  return
444  end if
445 
446  k_ap = k_a / (1d0 + y / inputs%D)
447  dkap_dd = k_a * y / (inputs%D + y)**2
448  d_vp = d_v / (1d0 + z / inputs%D)
449  ddvp_dd = d_v * z / (inputs%D + z)**2
450  a_w = (inputs%D**3 - inputs%D_dry**3) &
451  / (inputs%D**3 + (inputs%kappa - 1d0) * inputs%D_dry**3)
452  daw_dd = 3d0 * inputs%D**2 * inputs%kappa * inputs%D_dry**3 &
453  / (inputs%D**3 + (inputs%kappa - 1d0) * inputs%D_dry**3)**2
454 
455  delta_star = 0d0
456  h = 0d0
457  dh_ddelta = 1d0
458  do newton_step = 1,5
459  ! update delta_star first so when the newton loop ends we have
460  ! h and dh_ddelta evaluated at the final delta_star value
461  delta_star = delta_star - h / dh_ddelta
462  h = k_ap * delta_star - u * v * d_vp &
463  * (inputs%H - a_w / (1d0 + delta_star) &
464  * exp(w * delta_star / (1d0 + delta_star) &
465  + (x / inputs%D) / (1d0 + delta_star)))
466  dh_ddelta = &
467  k_ap - u * v * d_vp * a_w / (1d0 + delta_star)**2 &
468  * (1d0 - w / (1d0 + delta_star) &
469  + (x / inputs%D) / (1d0 + delta_star)) &
470  * exp(w * delta_star / (1d0 + delta_star) &
471  + (x / inputs%D) / (1d0 + delta_star))
472  end do
473  call warn_assert_msg(387362320, &
474  abs(h) < 1d3 * epsilon(1d0) * abs(u * v * d_vp * inputs%H), &
475  "condensation newton loop did not satisfy convergence tolerance")
476 
477  outputs%Ddot = k_ap * delta_star / (u * inputs%D)
478  outputs%Hdot_i = - 2d0 * const%pi / (v * inputs%V_comp) &
479  * inputs%D**2 * outputs%Ddot
480 
481  dh_dd = dkap_dd * delta_star &
482  - u * v * ddvp_dd * inputs%H + u * v &
483  * (a_w * ddvp_dd + d_vp * daw_dd &
484  - d_vp * a_w * (x / inputs%D**2) / (1d0 + delta_star)) &
485  * (1d0 / (1d0 + delta_star)) &
486  * exp((w * delta_star) / (1d0 + delta_star) &
487  + (x / inputs%D) / (1d0 + delta_star))
488  dh_dh = - u * v * d_vp
489 
490  ddeltastar_dd = - dh_dd / dh_ddelta
491  ddeltastar_dh = - dh_dh / dh_ddelta
492 
493  outputs%dDdot_dD = dkap_dd * delta_star / (u * inputs%D) &
494  + k_ap * ddeltastar_dd / (u * inputs%D) &
495  - k_ap * delta_star / (u * inputs%D**2)
496  outputs%dDdot_dH = k_ap / (u * inputs%D) * ddeltastar_dh
497  outputs%dHdoti_dD = - 2d0 * const%pi / (v * inputs%V_comp) &
498  * (2d0 * inputs%D * outputs%Ddot + inputs%D**2 * outputs%dDdot_dD)
499  outputs%dHdoti_dH = - 2d0 * const%pi / (v * inputs%V_comp) &
500  * inputs%D**2 * outputs%dDdot_dH
501 
502  end subroutine condense_rates
503 
504 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
505 
506 #ifdef PMC_USE_SUNDIALS
507  !> Compute the condensation rates (Ddot and Hdot) at the current
508  !> value of the state (D and H).
509  subroutine condense_vf_f(n_eqn, time, state_p, state_dot_p) bind(c)
510 
511  !> Length of state vector.
512  integer(kind=c_int), value, intent(in) :: n_eqn
513  !> Current time (s).
514  real(kind=c_double), value, intent(in) :: time
515  !> Pointer to state data.
516  type(c_ptr), value, intent(in) :: state_p
517  !> Pointer to state_dot data.
518  type(c_ptr), value, intent(in) :: state_dot_p
519 
520  real(kind=c_double), pointer :: state(:)
521  real(kind=c_double), pointer :: state_dot(:)
522  real(kind=dp) :: hdot
523  integer :: i_part
524  type(condense_rates_inputs_t) :: inputs
525  type(condense_rates_outputs_t) :: outputs
526 
528 
529  call c_f_pointer(state_p, state, (/ n_eqn /))
530  call c_f_pointer(state_dot_p, state_dot, (/ n_eqn /))
531 
532  inputs%T = condense_saved_env_state_initial%temp &
533  + time * condense_saved_tdot
534  inputs%p = condense_saved_env_state_initial%pressure &
535  + time * condense_saved_pdot
536  inputs%Tdot = condense_saved_tdot
537  inputs%pdot = condense_saved_pdot
538  inputs%H = state(n_eqn)
539 
540  hdot = 0d0
541  do i_part = 1,(n_eqn - 1)
542  inputs%D = state(i_part)
543  inputs%D_dry = condense_saved_d_dry(i_part)
544  inputs%V_comp = (inputs%T &
546  / (condense_saved_env_state_initial%temp * inputs%p) &
547  / condense_saved_num_conc(i_part)
548  inputs%kappa = condense_saved_kappa(i_part)
549  call condense_rates(inputs, outputs)
550  state_dot(i_part) = outputs%Ddot
551  hdot = hdot + outputs%Hdot_i
552  end do
553  hdot = hdot + outputs%Hdot_env
554 
555  state_dot(n_eqn) = hdot
556 
557  end subroutine condense_vf_f
558 #endif
559 
560 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
561 
562 #ifdef PMC_USE_SUNDIALS
563  !> Compute the Jacobian given by the derivatives of the condensation
564  !> rates (Ddot and Hdot) with respect to the input variables (D and
565  !> H).
566  subroutine condense_jac(n_eqn, time, state_p, dDdot_dD, dDdot_dH, &
567  dHdot_dD, dHdot_dH)
568 
569  !> Length of state vector.
570  integer(kind=c_int), intent(in) :: n_eqn
571  !> Current time (s).
572  real(kind=c_double), intent(in) :: time
573  !> Pointer to current state vector.
574  type(c_ptr), intent(in) :: state_p
575  !> Derivative of Ddot with respect to D.
576  real(kind=dp), intent(out) :: dddot_dd(n_eqn - 1)
577  !> Derivative of Ddot with respect to H.
578  real(kind=dp), intent(out) :: dddot_dh(n_eqn - 1)
579  !> Derivative of Hdot with respect to D.
580  real(kind=dp), intent(out) :: dhdot_dd(n_eqn - 1)
581  !> Derivative of Hdot with respect to H.
582  real(kind=dp), intent(out) :: dhdot_dh
583 
584  real(kind=c_double), pointer :: state(:)
585  integer :: i_part
586  type(condense_rates_inputs_t) :: inputs
587  type(condense_rates_outputs_t) :: outputs
588 
589  call c_f_pointer(state_p, state, (/ n_eqn /))
590 
591  inputs%T = condense_saved_env_state_initial%temp &
592  + time * condense_saved_tdot
593  inputs%p = condense_saved_env_state_initial%pressure &
594  + time * condense_saved_pdot
595  inputs%Tdot = condense_saved_tdot
596  inputs%pdot = condense_saved_pdot
597  inputs%H = state(n_eqn)
598 
599  dhdot_dh = 0d0
600  do i_part = 1,(n_eqn - 1)
601  inputs%D = state(i_part)
602  inputs%D_dry = condense_saved_d_dry(i_part)
603  inputs%V_comp = (inputs%T &
605  / (condense_saved_env_state_initial%temp * inputs%p) &
606  / condense_saved_num_conc(i_part)
607  inputs%kappa = condense_saved_kappa(i_part)
608  call condense_rates(inputs, outputs)
609  dddot_dd(i_part) = outputs%dDdot_dD
610  dddot_dh(i_part) = outputs%dDdot_dH
611  dhdot_dd(i_part) = outputs%dHdoti_dD + outputs%dHdotenv_dD
612  dhdot_dh = dhdot_dh + outputs%dHdoti_dH
613  end do
614  dhdot_dh = dhdot_dh + outputs%dHdotenv_dH
615 
616  end subroutine condense_jac
617 #endif
618 
619 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
620 
621 #ifdef PMC_USE_SUNDIALS
622  !> Solve the system \f$ Pz = r \f$ where \f$ P = I - \gamma J \f$
623  !> and \f$ J = \partial f / \partial y \f$. The solution is returned
624  !> in the \f$ r \f$ vector.
625  subroutine condense_jac_solve_f(n_eqn, time, state_p, state_dot_p, &
626  rhs_p, gamma) bind(c)
627 
628  !> Length of state vector.
629  integer(kind=c_int), value, intent(in) :: n_eqn
630  !> Current time (s).
631  real(kind=c_double), value, intent(in) :: time
632  !> Pointer to current state vector.
633  type(c_ptr), value, intent(in) :: state_p
634  !> Pointer to current state derivative vector.
635  type(c_ptr), value, intent(in) :: state_dot_p
636  !> Pointer to right-hand-side vector.
637  type(c_ptr), value, intent(in) :: rhs_p
638  !> Value of \c gamma scalar parameter.
639  real(kind=c_double), value, intent(in) :: gamma
640 
641  real(kind=c_double), pointer :: state(:), state_dot(:), rhs(:)
642  real(kind=c_double) :: soln(n_eqn)
643  real(kind=dp) :: dddot_dd(n_eqn - 1), dddot_dh(n_eqn - 1)
644  real(kind=dp) :: dhdot_dd(n_eqn - 1), dhdot_dh
645  real(kind=dp) :: lhs_n, rhs_n
646  real(kind=c_double) :: residual(n_eqn)
647  real(kind=dp) :: rhs_norm, soln_norm, residual_norm
648  integer :: i_part
649 
651 
652  call condense_jac(n_eqn, time, state_p, dddot_dd, dddot_dh, &
653  dhdot_dd, dhdot_dh)
654 
655  call c_f_pointer(state_p, state, (/ n_eqn /))
656  call c_f_pointer(state_dot_p, state_dot, (/ n_eqn /))
657  call c_f_pointer(rhs_p, rhs, (/ n_eqn /))
658 
659  !FIXME: write this all in matrix-vector notation, no i_part looping
660  lhs_n = 1d0 - gamma * dhdot_dh
661  rhs_n = rhs(n_eqn)
662  do i_part = 1,(n_eqn - 1)
663  lhs_n = lhs_n - (- gamma * dddot_dh(i_part)) &
664  * (- gamma * dhdot_dd(i_part)) / (1d0 - gamma * dddot_dd(i_part))
665  rhs_n = rhs_n - (- gamma * dhdot_dd(i_part)) * rhs(i_part) &
666  / (1d0 - gamma * dddot_dd(i_part))
667  end do
668  soln(n_eqn) = rhs_n / lhs_n
669 
670  do i_part = 1,(n_eqn - 1)
671  soln(i_part) = (rhs(i_part) &
672  - (- gamma * dddot_dh(i_part)) * soln(n_eqn)) &
673  / (1d0 - gamma * dddot_dd(i_part))
674  end do
675 
677  ! (I - g J) soln = rhs
678 
679  ! residual = J soln
680  residual(n_eqn) = sum(dhdot_dd * soln(1:(n_eqn-1))) &
681  + dhdot_dh * soln(n_eqn)
682  residual(1:(n_eqn-1)) = dddot_dd * soln(1:(n_eqn-1)) &
683  + dddot_dh * soln(n_eqn)
684 
685  residual = rhs - (soln - gamma * residual)
686  rhs_norm = sqrt(sum(rhs**2))
687  soln_norm = sqrt(sum(soln**2))
688  residual_norm = sqrt(sum(residual**2))
689  write(0,*) 'rhs, soln, residual, residual/rhs = ', &
690  rhs_norm, soln_norm, residual_norm, residual_norm / rhs_norm
691  end if
692 
693  rhs = soln
694 
695  end subroutine condense_jac_solve_f
696 #endif
697 
698 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
699 
700  !> Determine the water equilibrium state of a single particle.
701  subroutine condense_equilib_particle(env_state, aero_data, &
702  aero_particle)
703 
704  !> Environment state.
705  type(env_state_t), intent(in) :: env_state
706  !> Aerosol data.
707  type(aero_data_t), intent(in) :: aero_data
708  !> Particle.
709  type(aero_particle_t), intent(inout) :: aero_particle
710 
711  real(kind=dp) :: x, kappa, d_dry, d, g, dg_dd, a_w, daw_dd
712  integer :: newton_step
713 
714  x = 4d0 * const%water_molec_weight * const%water_surf_eng &
715  / (const%univ_gas_const * env_state%temp &
716  * const%water_density)
717  kappa = aero_particle_solute_kappa(aero_particle, aero_data)
718  d_dry = aero_data_vol2diam(aero_data, &
719  aero_particle_solute_volume(aero_particle, &
720  aero_data))
721 
722  d = d_dry
723  g = 0d0
724  dg_dd = 1d0
725  do newton_step = 1,20
726  d = d - g / dg_dd
727  a_w = (d**3 - d_dry**3) / (d**3 + (kappa - 1d0) * d_dry**3)
728  daw_dd = 3d0 * d**2 * kappa * d_dry**3 &
729  / (d**3 + (kappa - 1d0) * d_dry**3)**2
730  g = env_state%rel_humid - a_w * exp(x / d)
731  dg_dd = - daw_dd * exp(x / d) + a_w * exp(x / d) * (x / d**2)
732  end do
733  call warn_assert_msg(426620001, abs(g) < 1d3 * epsilon(1d0), &
734  "convergence problem in equilibriation")
735 
736  aero_particle%vol(aero_data%i_water) = aero_data_diam2vol(aero_data, d) &
737  - aero_data_diam2vol(aero_data, d_dry)
738 
739  end subroutine condense_equilib_particle
740 
741 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
742 
743  !> Call condense_equilib_particle() on each particle in the aerosol
744  !> to ensure that every particle has its water content in
745  !> equilibrium.
746  subroutine condense_equilib_particles(env_state, aero_data, aero_state)
747 
748  !> Environment state.
749  type(env_state_t), intent(inout) :: env_state
750  !> Aerosol data.
751  type(aero_data_t), intent(in) :: aero_data
752  !> Aerosol state.
753  type(aero_state_t), intent(inout) :: aero_state
754 
755  integer :: i_part
756  real(kind=dp) :: reweight_num_conc(aero_state_n_part(aero_state))
757 
758  ! We're modifying particle diameters, so bin sorting is now invalid
759  aero_state%valid_sort = .false.
760 
761  call aero_state_num_conc_for_reweight(aero_state, aero_data, &
762  reweight_num_conc)
763  do i_part = aero_state_n_part(aero_state),1,-1
764  call condense_equilib_particle(env_state, aero_data, &
765  aero_state%apa%particle(i_part))
766  end do
767  ! adjust particles to account for weight changes
768  call aero_state_reweight(aero_state, aero_data, reweight_num_conc)
769 
770  end subroutine condense_equilib_particles
771 
772 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
773 
774 end module pmc_condense
pmc_aero_particle::aero_particle_t
Single aerosol particle data structure.
Definition: aero_particle.F90:26
pmc_condense::pmc_condense_solver_init_cvode
integer, parameter pmc_condense_solver_init_cvode
Result code indicating failure to initialize the solver.
Definition: condense.F90:57
pmc_condense::pmc_condense_solver_fail
integer, parameter pmc_condense_solver_fail
Result code indicating failure of the solver.
Definition: condense.F90:63
pmc_condense::condense_rates
subroutine condense_rates(inputs, outputs)
Compute the rate of change of particle diameter and relative humidity for a single particle,...
Definition: condense.F90:372
pmc_aero_state::aero_state_n_part
elemental integer function aero_state_n_part(aero_state)
Return the current number of particles.
Definition: aero_state.F90:88
pmc_aero_particle::aero_particle_solute_kappa
real(kind=dp) function aero_particle_solute_kappa(aero_particle, aero_data)
Returns the average of the solute kappas (1).
Definition: aero_particle.F90:674
pmc_condense::pmc_condense_solver_init_y
integer, parameter pmc_condense_solver_init_y
Result code indicating failure to allocate y vector.
Definition: condense.F90:51
condense_jac_solve_f
void condense_jac_solve_f(int neq, double t, double *ycur_f, double *fcur_f, double *b_f, double gamma)
pmc_aero_particle
The aero_particle_t structure and associated subroutines.
Definition: aero_particle.F90:9
pmc_aero_state::aero_state_num_conc_for_reweight
subroutine aero_state_num_conc_for_reweight(aero_state, aero_data, reweight_num_conc)
Save the correct number concentrations for later use by aero_state_reweight().
Definition: aero_state.F90:531
pmc_condense::pmc_condense_solver_svtol
integer, parameter pmc_condense_solver_svtol
Result code indicating failure to set tolerances.
Definition: condense.F90:59
pmc_aero_particle::aero_particle_diameter
elemental real(kind=dp) function aero_particle_diameter(aero_particle, aero_data)
Total diameter of the particle (m).
Definition: aero_particle.F90:371
pmc_condense::condense_saved_kappa
real(kind=dp), dimension(:), allocatable condense_saved_kappa
Internal-use variable for storing the per-particle kappa values during calls to the ODE solver.
Definition: condense.F90:125
pmc_aero_particle::aero_particle_water_density
real(kind=dp) function aero_particle_water_density(aero_data)
Returns the water density (kg/m^3).
Definition: aero_particle.F90:575
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:59
pmc_util::die_msg
subroutine die_msg(code, error_msg)
Error immediately.
Definition: util.F90:134
pmc_condense::condense_saved_env_state_initial
type(env_state_t) condense_saved_env_state_initial
Internal-use variable for storing the initial environment state during calls to the ODE solver.
Definition: condense.F90:116
pmc_aero_particle::aero_particle_solute_volume
real(kind=dp) function aero_particle_solute_volume(aero_particle, aero_data)
Returns the total solute volume (m^3).
Definition: aero_particle.F90:638
pmc_constants::dp
integer, parameter dp
Kind of a double precision real number.
Definition: constants.F90:12
pmc_condense
Water condensation onto aerosol particles.
Definition: condense.F90:29
pmc_condense::condense_equilib_particles
subroutine condense_equilib_particles(env_state, aero_data, aero_state)
Call condense_equilib_particle() on each particle in the aerosol to ensure that every particle has it...
Definition: condense.F90:747
pmc_env_state::env_state_t
Current environment state.
Definition: env_state.F90:29
pmc_aero_state
The aero_state_t structure and assocated subroutines.
Definition: aero_state.F90:9
pmc_condense::condense_equilib_particle
subroutine condense_equilib_particle(env_state, aero_data, aero_particle)
Determine the water equilibrium state of a single particle.
Definition: condense.F90:703
pmc_condense::condense_saved_aero_data
type(aero_data_t) condense_saved_aero_data
Internal-use variable for storing the aerosol data during calls to the ODE solver.
Definition: condense.F90:113
pmc_condense::condense_rates_inputs_t
Internal-use structure for storing the inputs for the rate-calculation function.
Definition: condense.F90:67
pmc_condense::pmc_condense_solver_success
integer, parameter pmc_condense_solver_success
Result code indicating successful completion.
Definition: condense.F90:49
pmc_condense::condense_particles
subroutine condense_particles(aero_state, aero_data, env_state_initial, env_state_final, del_t)
Do condensation to all the particles for a given time interval, including updating the environment to...
Definition: condense.F90:149
pmc_condense::pmc_condense_solver_init_cvode_mem
integer, parameter pmc_condense_solver_init_cvode_mem
Result code indicating failure to create the solver.
Definition: condense.F90:55
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:766
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:782
condense_solver
int condense_solver(int neq, double *x_f, double *abstol_f, double reltol_f, double t_initial_f, double t_final_f)
Call the ODE solver.
Definition: condense_solver.c:79
pmc_constants::const
type(const_t), save const
Fixed variable for accessing the constant's values.
Definition: constants.F90:73
pmc_aero_state::aero_state_reweight
subroutine aero_state_reweight(aero_state, aero_data, reweight_num_conc)
Reweight all particles after their constituent volumes have been altered.
Definition: aero_state.F90:563
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:140
pmc_condense::condense_do_test_jac_solve
logical, parameter condense_do_test_jac_solve
Whether to numerically test the Jacobian-solve function during execution (for debugging only).
Definition: condense.F90:43
pmc_condense::condense_count_solve
integer, save condense_count_solve
Internal-use variable for counting calls to the Jacobian-solving subroutine.
Definition: condense.F90:138
pmc_condense::condense_count_vf
integer, save condense_count_vf
Internal-use variable for counting calls to the vector field subroutine.
Definition: condense.F90:135
pmc_env_state
The env_state_t structure and associated subroutines.
Definition: env_state.F90:9
pmc_aero_data::aero_data_t
Aerosol material properties and associated data.
Definition: aero_data.F90:49
pmc_constants
Physical constants.
Definition: constants.F90:9
pmc_condense::condense_saved_d_dry
real(kind=dp), dimension(:), allocatable condense_saved_d_dry
Internal-use variable for storing the per-particle dry diameters during calls to the ODE solver.
Definition: condense.F90:128
pmc_condense::condense_rates_outputs_t
Internal-use structure for storing the outputs from the rate-calculation function.
Definition: condense.F90:90
pmc_condense::condense_saved_tdot
real(kind=dp) condense_saved_tdot
Internal-use variable for storing the rate of change of the temperature during calls to the ODE solve...
Definition: condense.F90:119
pmc_util
Common utility subroutines.
Definition: util.F90:9
pmc_condense::pmc_condense_solver_set_max_steps
integer, parameter pmc_condense_solver_set_max_steps
Result code indicating failure to set maximum steps.
Definition: condense.F90:61
pmc_condense::pmc_condense_solver_init_abstol
integer, parameter pmc_condense_solver_init_abstol
Result code indicating failure to allocate abstol vector.
Definition: condense.F90:53
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:107
condense_vf_f
void condense_vf_f(int neq, realtype t, double *y_f, double *ydot_f)
pmc_condense::condense_saved_pdot
real(kind=dp) condense_saved_pdot
Internal-use variable for storing the rate of change of the pressure during calls to the ODE solver.
Definition: condense.F90:122
pmc_aero_data
The aero_data_t structure and associated subroutines.
Definition: aero_data.F90:9
pmc_aero_state::aero_state_t
The current collection of aerosol particles.
Definition: aero_state.F90:63
pmc_condense::condense_saved_num_conc
real(kind=dp), dimension(:), allocatable condense_saved_num_conc
Internal-use variable for storing the per-particle number concentrations during calls to the ODE solv...
Definition: condense.F90:131
pmc_aero_data::aero_data_diam2vol
real(kind=dp) elemental function aero_data_diam2vol(aero_data, d)
Convert geometric diameter (m) to mass-equivalent volume (m^3).
Definition: aero_data.F90:137
pmc_condense::condense_do_test_counts
logical, parameter condense_do_test_counts
Whether to print call-counts for helper routines during execution (for debugging only).
Definition: condense.F90:46