PartMC  2.6.1
output.F90
Go to the documentation of this file.
1 ! Copyright (C) 2005-2022 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_output module.
7 
8 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
9 
10 !> \page output_format Output File Format
11 !!
12 !! PartMC output files are in the <a
13 !! href="http://www.unidata.ucar.edu/software/netcdf/">NetCDF Classic
14 !! Format</a> (also known as NetCDF-3 format). The dimensions and
15 !! variables in the files will depend on the type of run (particle,
16 !! analytical solution, etc), and options in the spec file (e.g. \c
17 !! record_removals and \c do_optical).
18 !!
19 !! The state of the simulation is periodically output during the run,
20 !! with frequency determined by the \c t_output input parameter. Each
21 !! output file has a filename of the form \c PREFIX_RRRR_SSSSSSSS.nc,
22 !! where \c PREFIX is given by the \c output_prefix input parameter,
23 !! \c RRRR is the four-digit repeat number (starting from 1), and \c
24 !! SSSSSSSS is the eight-digit output index (starting at 1 and
25 !! incremented each time the state is output). For exact and sectional
26 !! simulations all repeats would be identical so there is no support
27 !! for repeating and the filename is of the format \c
28 !! PREFIX_SSSSSSSS.nc.
29 !!
30 !! If run in parallel and \c output_type is \c central or \c dist,
31 !! then the output files have names like \c
32 !! PREFIX_RRRR_PPPP_SSSSSSSS.nc, where \c PPPP is a four-digit
33 !! process number (starting from 1) and the other variables are as
34 !! above. If \c output_type is \c single then the output file naming
35 !! scheme as the same as for serial runs.
36 !!
37 !! The data in each output file comes in several different groups, as
38 !! follows:
39 !!
40 !! \subpage output_format_general "General Information"
41 !!
42 !! \subpage output_format_env_state "Environment State"
43 !!
44 !! \subpage output_format_gas_data "Gas Material Data"
45 !!
46 !! \subpage output_format_gas_state "Gas State"
47 !!
48 !! \subpage output_format_aero_data "Aerosol Material Data"
49 !!
50 !! \subpage output_format_aero_state "Aerosol Particle State"
51 !! (only for particle-resolved simulations)
52 !!
53 !! \subpage output_format_aero_removed "Aerosol Particle Removal Information"
54 !! (only for particle-resolved simulations, if \c record_removals is \c yes)
55 !!
56 !! \subpage output_format_aero_weight_array "Aerosol Weighting Function"
57 !! (only for particle-resolved simulations)
58 !!
59 !! \subpage output_format_diam_bin_grid "Diameter Bin Grid Data"
60 !! (only for exact and sectional simulations)
61 !!
62 !! \subpage output_format_aero_binned "Aerosol Binned Sectional State"
63 !! (only for exact and sectional simulations)
64 
65 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
66 
67 !> Write data in NetCDF format.
68 module pmc_output
69 
70  use pmc_bin_grid
71  use pmc_aero_data
72  use pmc_aero_state
73  use pmc_aero_binned
74  use pmc_netcdf
75  use pmc_gas_state
76  use pmc_env_state
77  use pmc_util
78  use pmc_gas_data
79  use pmc_mpi
80 #ifdef PMC_USE_MPI
81  use mpi
82 #endif
83 
84  !> PartMC verson number.
85  character(len=100), parameter :: partmc_version = "2.6.1"
86 
87  !> Type code for undefined or invalid output.
88  integer, parameter :: output_type_invalid = 0
89  !> Type code for centralized output (one file per process, all written
90  !> by process 0).
91  integer, parameter :: output_type_central = 1
92  !> Type code for distributed output (one file per process, written by
93  !> each process).
94  integer, parameter :: output_type_dist = 2
95  !> Type code for single output (one file for all processes, written by
96  !> process 0).
97  integer, parameter :: output_type_single = 3
98 
99  !> Internal-use variable only.
100  integer, parameter :: tag_output_state_central = 4341
101  !> Internal-use variable only.
102  integer, parameter :: tag_output_state_single = 4342
103 
104 contains
105 
106 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
107 
108  !> Write the current state.
109  subroutine output_state(prefix, output_type, aero_data, aero_state, &
110  gas_data, gas_state, env_state, index, time, del_t, i_repeat, &
111  record_removals, record_optical, uuid)
112 
113  !> Prefix of state file.
114  character(len=*), intent(in) :: prefix
115  !> Output type for parallel runs (see module constants).
116  integer, intent(in) :: output_type
117  !> Aerosol data.
118  type(aero_data_t), intent(in) :: aero_data
119  !> Aerosol state.
120  type(aero_state_t), intent(in) :: aero_state
121  !> Gas data.
122  type(gas_data_t), intent(in) :: gas_data
123  !> Gas state.
124  type(gas_state_t), intent(in) :: gas_state
125  !> Environment state.
126  type(env_state_t), intent(in) :: env_state
127  !> Filename index.
128  integer, intent(in) :: index
129  !> Current time (s).
130  real(kind=dp), intent(in) :: time
131  !> Current timestep (s).
132  real(kind=dp), intent(in) :: del_t
133  !> Current repeat number.
134  integer, intent(in) :: i_repeat
135  !> Whether to output particle removal info.
136  logical, intent(in) :: record_removals
137  !> Whether to output aerosol optical properties.
138  logical, intent(in) :: record_optical
139  !> UUID of the simulation.
140  character(len=PMC_UUID_LEN), intent(in) :: uuid
141 
142  integer :: rank, n_proc
143 #ifdef PMC_USE_MPI
144  type(env_state_t) :: env_state_write
145  type(gas_state_t) :: gas_state_write
146  type(aero_state_t) :: aero_state_write
147  integer :: ierr, status(MPI_STATUS_SIZE), i_proc, position
148  character, allocatable :: buffer(:)
149 #endif
150 
151  rank = pmc_mpi_rank()
152  n_proc = pmc_mpi_size()
153  if (output_type == output_type_central) then
154  ! write per-process data to separate files, but do it by
155  ! transferring data to process 0 and having it do the writes
156  if (rank == 0) then
157  call output_state_to_file(prefix, aero_data, aero_state, gas_data, &
158  gas_state, env_state, index, time, del_t, i_repeat, &
159  record_removals, record_optical, uuid, rank, n_proc)
160 #ifdef PMC_USE_MPI
161  do i_proc = 1,(n_proc - 1)
162  call recv_output_state_central(prefix, aero_data, gas_data, &
163  index, time, del_t, i_repeat, record_removals, &
164  record_optical, uuid, i_proc)
165  end do
166 #endif
167  else ! rank /= 0
168  call send_output_state_central(aero_state, gas_state, env_state)
169  end if
170  elseif (output_type == output_type_dist) then
171  ! have each process write its own data directly
172  call output_state_to_file(prefix, aero_data, aero_state, gas_data, &
173  gas_state, env_state, index, time, del_t, i_repeat, &
174  record_removals, record_optical, uuid, rank, n_proc)
175  elseif (output_type == output_type_single) then
176  if (n_proc == 1) then
177  call output_state_to_file(prefix, aero_data, aero_state, gas_data, &
178  gas_state, env_state, index, time, del_t, i_repeat, &
179  record_removals, record_optical, uuid, rank, n_proc)
180  else
181 #ifdef PMC_USE_MPI
182  ! collect all data onto process 0 and then write it to a
183  ! single file
184  env_state_write = env_state
185  gas_state_write = gas_state
186  call env_state_reduce_avg(env_state_write)
187  call gas_state_reduce_avg(gas_state_write)
188  call aero_state_mpi_gather(aero_state, aero_state_write, aero_data)
189  if (rank == 0) then
190  call output_state_to_file(prefix, aero_data, aero_state_write, &
191  gas_data, gas_state_write, env_state_write, index, time, &
192  del_t, i_repeat, record_removals, record_optical, uuid, &
193  rank, 1)
194  end if
195 #endif
196  end if
197  else
198  call die_msg(626743323, "Unknown output_type: " &
199  // trim(integer_to_string(output_type)))
200  end if
201 
202  end subroutine output_state
203 
204 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
205 
206  !> Make a filename from a given prefix and other information.
207  subroutine make_filename(filename, prefix, suffix, index, i_repeat, &
208  write_rank, write_n_proc)
209 
210  !> Filename to create.
211  character(len=*), intent(out) :: filename
212  !> Filename prefix.
213  character(len=*), intent(in) :: prefix
214  !> Filename suffix.
215  character(len=*), intent(in) :: suffix
216  !> Filename index.
217  integer, intent(in), optional :: index
218  !> Current repeat number.
219  integer, intent(in), optional :: i_repeat
220  !> Rank to write into file.
221  integer, intent(in), optional :: write_rank
222  !> Number of processes to write into file.
223  integer, intent(in), optional :: write_n_proc
224 
225  integer :: ncid, use_rank, use_n_proc
226  character(len=100) :: proc_string, index_string, repeat_string
227 
228  if (present(write_rank)) then
229  use_rank = write_rank
230  else
231  use_rank = pmc_mpi_rank()
232  end if
233  if (present(write_n_proc)) then
234  use_n_proc = write_n_proc
235  else
236  use_n_proc = pmc_mpi_size()
237  end if
238 
239  repeat_string = ""
240  proc_string = ""
241  index_string = ""
242  if (present(i_repeat)) write(repeat_string, '(a,i4.4)') '_', i_repeat
243  if (use_n_proc > 1) write(proc_string, '(a,i4.4)') '_', (use_rank + 1)
244  if (present(index)) write(index_string, '(a,i8.8)') '_', index
245  write(filename, '(a,a,a,a,a)') trim(prefix), trim(repeat_string), &
246  trim(proc_string), trim(index_string), trim(suffix)
247 
248  end subroutine make_filename
249 
250 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
251 
252  !> Helper routine to write time variables. Do not call directly.
253  subroutine write_time(ncid, time, del_t, index)
254 
255  !> NetCDF file ID, in data mode.
256  integer, intent(in) :: ncid
257  !> Current time (s).
258  real(kind=dp), intent(in) :: time
259  !> Current timestep (s).
260  real(kind=dp), intent(in) :: del_t
261  !> Filename index.
262  integer, intent(in) :: index
263 
264  call pmc_nc_write_real(ncid, time, "time", unit="s", &
265  description="time elapsed since simulation start")
266  call pmc_nc_write_real(ncid, del_t, "timestep", unit="s", &
267  description="current timestep size")
268  call pmc_nc_write_integer(ncid, index, "timestep_index", &
269  description="an integer that is 1 on the first timestep, " &
270  // "2 on the second timestep, etc.")
271 
272  end subroutine write_time
273 
274 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
275 
276  !> Write the current state for a single process. Do not call this
277  !> subroutine directly, but rather call output_state().
278  subroutine output_state_to_file(prefix, aero_data, aero_state, gas_data, &
279  gas_state, env_state, index, time, del_t, i_repeat, record_removals, &
280  record_optical, uuid, write_rank, write_n_proc)
281 
282  !> Prefix of state file.
283  character(len=*), intent(in) :: prefix
284  !> Aerosol data.
285  type(aero_data_t), intent(in) :: aero_data
286  !> Aerosol state.
287  type(aero_state_t), intent(in) :: aero_state
288  !> Gas data.
289  type(gas_data_t), intent(in) :: gas_data
290  !> Gas state.
291  type(gas_state_t), intent(in) :: gas_state
292  !> Environment state.
293  type(env_state_t), intent(in) :: env_state
294  !> Filename index.
295  integer, intent(in) :: index
296  !> Current time (s).
297  real(kind=dp), intent(in) :: time
298  !> Current timestep (s).
299  real(kind=dp), intent(in) :: del_t
300  !> Current repeat number.
301  integer, intent(in) :: i_repeat
302  !> Whether to output particle removal info.
303  logical, intent(in) :: record_removals
304  !> Whether to output aerosol optical properties.
305  logical, intent(in) :: record_optical
306  !> UUID of the simulation.
307  character(len=PMC_UUID_LEN), intent(in) :: uuid
308  !> Rank to write into file.
309  integer, intent(in), optional :: write_rank
310  !> Number of processes to write into file.
311  integer, intent(in), optional :: write_n_proc
312 
313  character(len=len(prefix)+100) :: filename
314  integer :: ncid
315 
316  !> \page output_format_general Output File Format: General Information
317  !!
318  !! The general information global NetCDF attributes are:
319  !! - \b title: always set to the string "PartMC version V.V.V
320  !! output file" where V.V.V is the PartMC version that created
321  !! the file
322  !! - \b source: set to the string "PartMC version V.V.V"
323  !! - \b UUID: a string of the form F47AC10B-58CC-4372-A567-0E02B2C3D479
324  !! which is the same for all files generated by a single call of
325  !! PartMC.
326  !! - \b Conventions: set to the string "CF-1.4", indicating
327  !! compliance with the <a
328  !! href="http://cf-pcmdi.llnl.gov/documents/cf-conventions/1.4">CF
329  !! convention format</a>
330  !! - \b history: set to the string "YYYY-MM-DDThh:mm:ss[+-]ZZ:zz
331  !! created by PartMC version V.V.V" where the first term is
332  !! the file creation time in the <a
333  !! href="http://en.wikipedia.org/wiki/ISO_8601">ISO 8601
334  !! format</a>. For example, noon Pacific Standard Time (PST)
335  !! on February 1st, 2000 would be written
336  !! 2000-02-01T12:00:00-08:00. The date and time variables
337  !! are:
338  !! - YYYY: four-digit year
339  !! - MM: two-digit month number
340  !! - DD: two-digit day within month
341  !! - T: literal "T" character
342  !! - hh: two-digit hour in 24-hour format
343  !! - mm: two-digit minute
344  !! - ss: two-digit second
345  !! - [+-]: a literal "+" or "-" character giving the time zone
346  !! offset sign
347  !! - ZZ: two-digit hours of the time zone offset from UTC
348  !! - zz: two-digit minutes of the time zone offset from UTC
349  !!
350  !! The general information NetCDF variables are:
351  !! - \b time (unit s): time elapsed since the simulation start time,
352  !! as specified in the \ref output_format_env_state section
353  !! - \b timestep (unit s): the current timestep size
354  !! - \b repeat: the repeat number of this simulation (starting from 1)
355  !! - \b timestep_index: an integer that is 1 on the first timestep, 2
356  !! on the second timestep, etc.
357  !! - \b process (MPI only): the process number (starting from 1)
358  !! that output this data file
359  !! - \b total_processes (MPI only): the total number of processes
360  !! involved in writing data (may be less than the total number of
361  !! processes that computed the data)
362 
363  call make_filename(filename, prefix, ".nc", index, i_repeat, write_rank, &
364  write_n_proc)
365  call pmc_nc_open_write(filename, ncid)
366  call pmc_nc_write_info(ncid, uuid, &
367  "PartMC version " // trim(partmc_version), write_rank, write_n_proc)
368  call write_time(ncid, time, del_t, index)
369  call pmc_nc_write_integer(ncid, i_repeat, "repeat", &
370  description="repeat number of this simulation (starting from 1)")
371 
372  call env_state_output_netcdf(env_state, ncid)
373  call gas_data_output_netcdf(gas_data, ncid)
374  call gas_state_output_netcdf(gas_state, ncid, gas_data)
375  call aero_data_output_netcdf(aero_data, ncid)
376  call aero_state_output_netcdf(aero_state, ncid, aero_data, &
377  record_removals, record_optical)
378 
379  call pmc_nc_check(nf90_close(ncid))
380 
381  end subroutine output_state_to_file
382 
383 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
384 
385  !> Send the state for the "central" output method to the root process.
386  subroutine send_output_state_central(aero_state, gas_state, env_state)
387 
388  !> Aerosol state.
389  type(aero_state_t), intent(in) :: aero_state
390  !> Gas state.
391  type(gas_state_t), intent(in) :: gas_state
392  !> Environment state.
393  type(env_state_t), intent(in) :: env_state
394 
395 #ifdef PMC_USE_MPI
396  integer :: buffer_size, max_buffer_size, position, ierr
397  character, allocatable :: buffer(:)
398 
399  call assert(645797304, pmc_mpi_rank() /= 0)
400 
401  max_buffer_size = 0
402  max_buffer_size = max_buffer_size &
403  + pmc_mpi_pack_size_env_state(env_state)
404  max_buffer_size = max_buffer_size &
405  + pmc_mpi_pack_size_gas_state(gas_state)
406  max_buffer_size = max_buffer_size &
407  + pmc_mpi_pack_size_aero_state(aero_state)
408  allocate(buffer(max_buffer_size))
409  position = 0
410  call pmc_mpi_pack_env_state(buffer, position, env_state)
411  call pmc_mpi_pack_gas_state(buffer, position, gas_state)
412  call pmc_mpi_pack_aero_state(buffer, position, aero_state)
413  call assert(839343839, position <= max_buffer_size)
414  buffer_size = position
415  call mpi_send(buffer, buffer_size, mpi_character, 0, &
416  tag_output_state_central, mpi_comm_world, ierr)
417  call pmc_mpi_check_ierr(ierr)
418  deallocate(buffer)
419 #endif
420 
421  end subroutine send_output_state_central
422 
423 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
424 
425  !> Receive the state for the "central" output method on the root
426  !> process.
427  subroutine recv_output_state_central(prefix, aero_data, gas_data, index, &
428  time, del_t, i_repeat, record_removals, record_optical, uuid, &
429  remote_proc)
430 
431  !> Prefix of state file.
432  character(len=*), intent(in) :: prefix
433  !> Aerosol data.
434  type(aero_data_t), intent(in) :: aero_data
435  !> Gas data.
436  type(gas_data_t), intent(in) :: gas_data
437  !> Filename index.
438  integer, intent(in) :: index
439  !> Current time (s).
440  real(kind=dp), intent(in) :: time
441  !> Current timestep (s).
442  real(kind=dp), intent(in) :: del_t
443  !> Current repeat number.
444  integer, intent(in) :: i_repeat
445  !> Whether to output particle removal info.
446  logical, intent(in) :: record_removals
447  !> Whether to output aerosol_optical_properties.
448  logical, intent(in) :: record_optical
449  !> UUID of the simulation.
450  character(len=PMC_UUID_LEN), intent(in) :: uuid
451  !> Process number to receive from.
452  integer, intent(in) :: remote_proc
453 
454 #ifdef PMC_USE_MPI
455  type(env_state_t) :: env_state
456  type(gas_state_t) :: gas_state
457  type(aero_state_t) :: aero_state
458  integer :: buffer_size, position, status(MPI_STATUS_SIZE)
459  integer :: n_proc, ierr
460  character, allocatable :: buffer(:)
461 
462  call assert(206980035, pmc_mpi_rank() == 0)
463  call assert(291452117, remote_proc /= 0)
464  n_proc = pmc_mpi_size()
465 
466  ! get buffer size
467  call mpi_probe(remote_proc, tag_output_state_central, mpi_comm_world, &
468  status, ierr)
469  call pmc_mpi_check_ierr(ierr)
470  call mpi_get_count(status, mpi_character, buffer_size, ierr)
471 
472  ! get message
473  allocate(buffer(buffer_size))
474  call mpi_recv(buffer, buffer_size, mpi_character, remote_proc, &
475  tag_output_state_central, mpi_comm_world, status, ierr)
476  call pmc_mpi_check_ierr(ierr)
477 
478  ! unpack message
479  position = 0
480  call pmc_mpi_unpack_env_state(buffer, position, env_state)
481  call pmc_mpi_unpack_gas_state(buffer, position, gas_state)
482  call pmc_mpi_unpack_aero_state(buffer, position, aero_state)
483  call assert(279581330, position == buffer_size)
484  deallocate(buffer)
485 
486  call output_state_to_file(prefix, aero_data, aero_state, gas_data, &
487  gas_state, env_state, index, time, del_t, i_repeat, &
488  record_removals, record_optical, uuid, remote_proc, n_proc)
489 #endif
490 
491  end subroutine recv_output_state_central
492 
493 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
494 
495  !> Read the current state.
496  subroutine input_state(filename, index, time, del_t, i_repeat, uuid, &
497  aero_data, aero_state, gas_data, gas_state, env_state)
498 
499  !> Prefix of state file.
500  character(len=*), intent(in) :: filename
501  !> Filename index.
502  integer, intent(out) :: index
503  !> Current time (s).
504  real(kind=dp), intent(out) :: time
505  !> Current timestep (s).
506  real(kind=dp), intent(out) :: del_t
507  !> Current repeat number.
508  integer, intent(out) :: i_repeat
509  !> UUID of the simulation.
510  character(len=PMC_UUID_LEN), intent(out) :: uuid
511  !> Aerosol data.
512  type(aero_data_t), optional, intent(inout) :: aero_data
513  !> Aerosol state.
514  type(aero_state_t), optional, intent(inout) :: aero_state
515  !> Gas data.
516  type(gas_data_t), optional, intent(inout) :: gas_data
517  !> Gas state.
518  type(gas_state_t), optional, intent(inout) :: gas_state
519  !> Environment state.
520  type(env_state_t), optional, intent(inout) :: env_state
521 
522  integer :: ncid
523 
524  call assert_msg(819739354, pmc_mpi_rank() == 0, &
525  "can only call from process 0")
526 
527  call pmc_nc_open_read(filename, ncid)
528 
529  call pmc_nc_check(nf90_get_att(ncid, nf90_global, "UUID", uuid))
530 
531  call pmc_nc_read_real(ncid, time, "time")
532  call pmc_nc_read_real(ncid, del_t, "timestep")
533  call pmc_nc_read_integer(ncid, i_repeat, "repeat")
534  call pmc_nc_read_integer(ncid, index, "timestep_index")
535 
536  if (present(aero_data)) then
537  call aero_data_input_netcdf(aero_data, ncid)
538  if (present(aero_state)) then
539  call aero_state_input_netcdf(aero_state, ncid, aero_data)
540  end if
541  else
542  call assert_msg(289621231, present(aero_state) .eqv. .false., &
543  "cannot input aero_state without aero_data")
544  end if
545 
546  if (present(gas_data)) then
547  call gas_data_input_netcdf(gas_data, ncid)
548  if (present(gas_state)) then
549  call gas_state_input_netcdf(gas_state, ncid, gas_data)
550  end if
551  else
552  call assert_msg(874298496, present(gas_state) .eqv. .false., &
553  "cannot input gas_state without gas_data")
554  end if
555 
556  if (present(env_state)) then
557  call env_state_input_netcdf(env_state, ncid)
558  end if
559 
560  call pmc_nc_close(ncid)
561 
562  end subroutine input_state
563 
564 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
565 
566  !> Find all NetCDF (.nc) filenames that match the given prefix.
567  subroutine input_filename_list(prefix, filename_list)
568 
569  !> Filename prefix to search for.
570  character(len=*), intent(in) :: prefix
571  !> Filename list.
572  character(len=*), intent(inout), allocatable :: filename_list(:)
573 
574  integer :: n_file, index, unit, ios
575  character(len=len(prefix)+100) :: filename
576  logical :: done
577 
578  call assert_msg(277193351, pmc_mpi_rank() == 0, &
579  "can only call from process 0")
580 
581  index = 1
582  done = .false.
583  unit = get_unit()
584  do while (.not. done)
585  write(filename, '(a,a,i8.8,a)') trim(prefix), '_', index, '.nc'
586  open(unit=unit, file=filename, status='old', iostat=ios)
587  if (ios /= 0) then
588  done = .true.
589  else
590  index = index + 1
591  close(unit)
592  end if
593  end do
594  call free_unit(unit)
595 
596  n_file = index - 1
597  call ensure_string_array_size(filename_list, n_file)
598  do index = 1,n_file
599  write(filename, '(a,a,i8.8,a)') trim(prefix), '_', index, '.nc'
600  filename_list(index) = filename
601  end do
602 
603  end subroutine input_filename_list
604 
605 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
606 
607  !> Find the number of repeats and indices for the given prefix.
608  subroutine input_n_files(prefix, n_repeat, n_index)
609 
610  !> Filename prefix to search for.
611  character(len=*), intent(in) :: prefix
612  !> Number of repeats found.
613  integer, intent(out) :: n_repeat
614  !> Number of indices found.
615  integer, intent(out) :: n_index
616 
617  integer :: repeat, index, unit, ios
618  character(len=len(prefix)+100) :: filename
619  logical :: done
620 
621  call assert_msg(711223711, pmc_mpi_rank() == 0, &
622  "can only call from process 0")
623 
624  unit = get_unit()
625 
626  repeat = 1
627  index = 1
628  done = .false.
629  do while (.not. done)
630  call make_filename(filename, prefix, ".nc", index, repeat)
631  open(unit=unit, file=filename, status='old', iostat=ios)
632  if (ios /= 0) then
633  done = .true.
634  else
635  repeat = repeat + 1
636  close(unit)
637  end if
638  end do
639  n_repeat = repeat - 1
640  call assert_msg(252703940, n_repeat >= 1, &
641  "no files found with prefix: " // trim(prefix))
642 
643  repeat = 1
644  index = 1
645  done = .false.
646  do while (.not. done)
647  call make_filename(filename, prefix, ".nc", index, repeat)
648  open(unit=unit, file=filename, status='old', iostat=ios)
649  if (ios /= 0) then
650  done = .true.
651  else
652  index = index + 1
653  close(unit)
654  end if
655  end do
656  n_index = index - 1
657 
658  end subroutine input_n_files
659 
660 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
661 
662  !> Write the current sectional data.
663  subroutine output_sectional(prefix, bin_grid, aero_data, aero_binned, &
664  gas_data, gas_state, env_state, index, time, del_t, uuid)
665 
666  !> Prefix of filename to write
667  character(len=*), intent(in) :: prefix
668  !> Bin grid.
669  type(bin_grid_t), intent(in) :: bin_grid
670  !> Aerosol data.
671  type(aero_data_t), intent(in) :: aero_data
672  !> Binned aerosol data.
673  type(aero_binned_t), intent(in) :: aero_binned
674  !> Gas data.
675  type(gas_data_t), intent(in) :: gas_data
676  !> Gas state.
677  type(gas_state_t), intent(in) :: gas_state
678  !> Environment state.
679  type(env_state_t), intent(in) :: env_state
680  !> Filename index.
681  integer, intent(in) :: index
682  !> Current time (s).
683  real(kind=dp), intent(in) :: time
684  !> Current output time-step (s).
685  real(kind=dp), intent(in) :: del_t
686  !> UUID of the simulation.
687  character(len=PMC_UUID_LEN), intent(in) :: uuid
688 
689  integer :: ncid
690  character(len=len(prefix)+100) :: filename
691 
692  write(filename, '(a,a,i8.8,a)') trim(prefix), &
693  '_', index, '.nc'
694  call pmc_nc_open_write(filename, ncid)
695  call pmc_nc_write_info(ncid, uuid, &
696  "PartMC version " // trim(partmc_version))
697  call write_time(ncid, time, del_t, index)
698 
699  ! write data
700  call env_state_output_netcdf(env_state, ncid)
701  call gas_data_output_netcdf(gas_data, ncid)
702  call gas_state_output_netcdf(gas_state, ncid, gas_data)
703  call aero_data_output_netcdf(aero_data, ncid)
704  call aero_binned_output_netcdf(aero_binned, ncid, bin_grid, &
705  aero_data)
706 
707  call pmc_nc_check(nf90_close(ncid))
708 
709  end subroutine output_sectional
710 
711 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
712 
713  !> Input sectional data.
714  subroutine input_sectional(filename, index, time, del_t, uuid, bin_grid, &
715  aero_data, aero_binned, gas_data, gas_state, env_state)
716 
717  !> Filename to read.
718  character(len=*), intent(in) :: filename
719  !> Filename index.
720  integer, intent(out) :: index
721  !> Current time (s).
722  real(kind=dp), intent(out) :: time
723  !> Current output time-step (s).
724  real(kind=dp), intent(out) :: del_t
725  !> UUID of the simulation.
726  character(len=PMC_UUID_LEN), intent(out) :: uuid
727  !> Bin grid.
728  type(bin_grid_t), optional, intent(inout) :: bin_grid
729  !> Aerosol data.
730  type(aero_data_t), optional, intent(inout) :: aero_data
731  !> Binned aerosol data.
732  type(aero_binned_t), optional, intent(inout) :: aero_binned
733  !> Gas data.
734  type(gas_data_t), optional, intent(inout) :: gas_data
735  !> Gas state.
736  type(gas_state_t), optional, intent(inout) :: gas_state
737  !> Environment state.
738  type(env_state_t), optional, intent(inout) :: env_state
739 
740  integer :: ncid
741 
742  call assert_msg(559676785, pmc_mpi_rank() == 0, &
743  "can only call from process 0")
744 
745  call pmc_nc_open_read(filename, ncid)
746 
747  call pmc_nc_check(nf90_get_att(ncid, nf90_global, "UUID", uuid))
748 
749  call pmc_nc_read_real(ncid, time, "time")
750  call pmc_nc_read_real(ncid, del_t, "timestep")
751  call pmc_nc_read_integer(ncid, index, "timestep_index")
752 
753  if (present(bin_grid)) then
754  call bin_grid_input_netcdf(bin_grid, ncid, "aero_diam", scale=0.5d0)
755  end if
756  if (present(aero_data)) then
757  call aero_data_input_netcdf(aero_data, ncid)
758  end if
759  if (present(aero_binned)) then
760  call assert_msg(585353528, &
761  present(bin_grid) .and. present(aero_data), &
762  "cannot input aero_binned without bin_grid and aero_data")
763  call aero_binned_input_netcdf(aero_binned, ncid, bin_grid, &
764  aero_data)
765  end if
766 
767  if (present(gas_data)) then
768  call gas_data_input_netcdf(gas_data, ncid)
769  if (present(gas_state)) then
770  call gas_state_input_netcdf(gas_state, ncid, gas_data)
771  end if
772  else
773  call assert_msg(214545112, present(gas_state) .eqv. .false., &
774  "cannot input gas_state without gas_data")
775  end if
776 
777  if (present(env_state)) then
778  call env_state_input_netcdf(env_state, ncid)
779  end if
780 
781  call pmc_nc_close(ncid)
782 
783  end subroutine input_sectional
784 
785 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
786 
787  !> Read the specification for an output type from a spec file and
788  !> generate it.
789  subroutine spec_file_read_output_type(file, output_type)
790 
791  !> Spec file.
792  type(spec_file_t), intent(inout) :: file
793  !> Kernel type.
794  integer, intent(out) :: output_type
795 
796  character(len=SPEC_LINE_MAX_VAR_LEN) :: output_type_name
797 
798  !> \page input_format_output Input File Format: Output Type
799  !!
800  !! The output type is specified by the parameter:
801  !! - \b output_type (string): type of disk output --- must be
802  !! one of: \c central to write one file per process, but all
803  !! written by process 0; \c dist for every process to
804  !! write its own state file; or \c single to transfer all data
805  !! to process 0 and write a single unified output file
806  !!
807  !! See also:
808  !! - \ref spec_file_format --- the input file text format
809 
810  call spec_file_read_string(file, 'output_type', output_type_name)
811  if (trim(output_type_name) == 'central') then
812  output_type = output_type_central
813  elseif (trim(output_type_name) == 'dist') then
814  output_type = output_type_dist
815  elseif (trim(output_type_name) == 'single') then
816  output_type = output_type_single
817  else
818  call spec_file_die_msg(392313600, file, &
819  "Unknown output type: " // trim(output_type_name))
820  end if
821 
822  end subroutine spec_file_read_output_type
823 
824 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
825 
826 end module pmc_output
pmc_output::partmc_version
character(len=100), parameter partmc_version
PartMC verson number.
Definition: output.F90:85
pmc_netcdf::pmc_nc_write_integer
subroutine pmc_nc_write_integer(ncid, var, name, unit, long_name, standard_name, description)
Write a single integer to a NetCDF file.
Definition: netcdf.F90:459
pmc_gas_data::gas_data_t
Constant gas data.
Definition: gas_data.F90:35
pmc_mpi::pmc_mpi_size
integer function pmc_mpi_size()
Returns the total number of processes.
Definition: mpi.F90:134
pmc_mpi
Wrapper functions for MPI.
Definition: mpi.F90:13
pmc_output::input_state
subroutine input_state(filename, index, time, del_t, i_repeat, uuid, aero_data, aero_state, gas_data, gas_state, env_state)
Read the current state.
Definition: output.F90:498
pmc_util::get_unit
integer function get_unit()
Returns an available unit number. This should be freed by free_unit().
Definition: util.F90:148
pmc_env_state::env_state_input_netcdf
subroutine env_state_input_netcdf(env_state, ncid)
Read full state.
Definition: env_state.F90:504
pmc_gas_data
The gas_data_t structure and associated subroutines.
Definition: gas_data.F90:9
pmc_env_state::pmc_mpi_unpack_env_state
subroutine pmc_mpi_unpack_env_state(buffer, position, val)
Unpacks the given value from the buffer, advancing position.
Definition: env_state.F90:392
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_mpi::pmc_mpi_rank
integer function pmc_mpi_rank()
Returns the rank of the current process.
Definition: mpi.F90:117
pmc_netcdf
Wrapper functions for NetCDF. These all take a NetCDF ncid in data mode and return with it again in d...
Definition: netcdf.F90:11
pmc_output::output_type_central
integer, parameter output_type_central
Type code for centralized output (one file per process, all written by process 0).
Definition: output.F90:91
pmc_constants::dp
integer, parameter dp
Kind of a double precision real number.
Definition: constants.F90:12
pmc_output::output_state
subroutine output_state(prefix, output_type, aero_data, aero_state, gas_data, gas_state, env_state, index, time, del_t, i_repeat, record_removals, record_optical, uuid)
Write the current state.
Definition: output.F90:112
pmc_output::input_n_files
subroutine input_n_files(prefix, n_repeat, n_index)
Find the number of repeats and indices for the given prefix.
Definition: output.F90:609
pmc_env_state::env_state_t
Current environment state.
Definition: env_state.F90:29
pmc_util::assert
subroutine assert(code, condition_ok)
Errors unless condition_ok is true.
Definition: util.F90:103
pmc_output::tag_output_state_central
integer, parameter tag_output_state_central
Internal-use variable only.
Definition: output.F90:100
pmc_aero_state
The aero_state_t structure and assocated subroutines.
Definition: aero_state.F90:9
pmc_netcdf::pmc_nc_read_integer
subroutine pmc_nc_read_integer(ncid, var, name, must_be_present)
Read a single integer from a NetCDF file.
Definition: netcdf.F90:184
pmc_output::send_output_state_central
subroutine send_output_state_central(aero_state, gas_state, env_state)
Send the state for the "central" output method to the root process.
Definition: output.F90:387
pmc_output::make_filename
subroutine make_filename(filename, prefix, suffix, index, i_repeat, write_rank, write_n_proc)
Make a filename from a given prefix and other information.
Definition: output.F90:209
pmc_spec_file::spec_file_t
An input file with extra data for printing messages.
Definition: spec_file.F90:59
pmc_netcdf::pmc_nc_open_read
subroutine pmc_nc_open_read(filename, ncid)
Open a NetCDF file for reading.
Definition: netcdf.F90:55
pmc_env_state::env_state_reduce_avg
subroutine env_state_reduce_avg(val)
Average val over all processes, with the result only on the root process.
Definition: env_state.F90:313
pmc_netcdf::pmc_nc_write_info
subroutine pmc_nc_write_info(ncid, uuid, source, write_rank, write_n_proc)
Write basic information to a NetCDF file.
Definition: netcdf.F90:98
pmc_gas_state
The gas_state_t structure and associated subroutines.
Definition: gas_state.F90:9
pmc_aero_data::aero_data_input_netcdf
subroutine aero_data_input_netcdf(aero_data, ncid)
Read full state.
Definition: aero_data.F90:771
pmc_netcdf::pmc_nc_open_write
subroutine pmc_nc_open_write(filename, ncid)
Open a NetCDF file for writing.
Definition: netcdf.F90:70
pmc_gas_state::pmc_mpi_unpack_gas_state
subroutine pmc_mpi_unpack_gas_state(buffer, position, val)
Unpacks the given value from the buffer, advancing position.
Definition: gas_state.F90:555
pmc_env_state::pmc_mpi_pack_env_state
subroutine pmc_mpi_pack_env_state(buffer, position, val)
Packs the given value into the buffer, advancing position.
Definition: env_state.F90:359
pmc_gas_state::gas_state_reduce_avg
subroutine gas_state_reduce_avg(val)
Average val over all processes, with the result only on the root process.
Definition: gas_state.F90:499
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::assert_msg
subroutine assert_msg(code, condition_ok, error_msg)
Errors unless condition_ok is true.
Definition: util.F90:77
pmc_output::output_type_single
integer, parameter output_type_single
Type code for single output (one file for all processes, written by process 0).
Definition: output.F90:97
pmc_netcdf::pmc_nc_write_real
subroutine pmc_nc_write_real(ncid, var, name, unit, long_name, standard_name, description)
Write a single real to a NetCDF file.
Definition: netcdf.F90:426
pmc_bin_grid::bin_grid_input_netcdf
subroutine bin_grid_input_netcdf(bin_grid, ncid, dim_name, scale)
Read full state.
Definition: bin_grid.F90:524
pmc_output::write_time
subroutine write_time(ncid, time, del_t, index)
Helper routine to write time variables. Do not call directly.
Definition: output.F90:254
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_state::aero_state_input_netcdf
subroutine aero_state_input_netcdf(aero_state, ncid, aero_data)
Read full state.
Definition: aero_state.F90:2783
pmc_util::ensure_string_array_size
subroutine ensure_string_array_size(x, n)
Allocate or reallocate the given array to ensure it is of the given size, without preserving data.
Definition: util.F90:1189
pmc_gas_data::gas_data_input_netcdf
subroutine gas_data_input_netcdf(gas_data, ncid)
Read full state.
Definition: gas_data.F90:417
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_state::aero_state_mpi_gather
subroutine aero_state_mpi_gather(aero_state, aero_state_total, aero_data)
Gathers data from all processes into one aero_state on process 0.
Definition: aero_state.F90:2258
pmc_aero_state::pmc_mpi_unpack_aero_state
subroutine pmc_mpi_unpack_aero_state(buffer, position, val)
Unpacks the given value from the buffer, advancing position.
Definition: aero_state.F90:2231
pmc_mpi::pmc_mpi_check_ierr
subroutine pmc_mpi_check_ierr(ierr)
Dies if ierr is not ok.
Definition: mpi.F90:40
pmc_aero_state::pmc_mpi_pack_aero_state
subroutine pmc_mpi_pack_aero_state(buffer, position, val)
Packs the given value into the buffer, advancing position.
Definition: aero_state.F90:2205
pmc_aero_state::pmc_mpi_pack_size_aero_state
integer function pmc_mpi_pack_size_aero_state(val)
Determines the number of bytes required to pack the given value.
Definition: aero_state.F90:2186
pmc_netcdf::pmc_nc_close
subroutine pmc_nc_close(ncid)
Close a NetCDF file.
Definition: netcdf.F90:86
pmc_netcdf::pmc_nc_check
subroutine pmc_nc_check(status)
Check the status of a NetCDF function call.
Definition: netcdf.F90:23
pmc_util
Common utility subroutines.
Definition: util.F90:9
pmc_output::input_sectional
subroutine input_sectional(filename, index, time, del_t, uuid, bin_grid, aero_data, aero_binned, gas_data, gas_state, env_state)
Input sectional data.
Definition: output.F90:716
pmc_gas_state::pmc_mpi_pack_gas_state
subroutine pmc_mpi_pack_gas_state(buffer, position, val)
Packs the given value into the buffer, advancing position.
Definition: gas_state.F90:532
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_binned
The aero_binned_t structure and associated subroutines.
Definition: aero_binned.F90:9
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:605
pmc_aero_binned::aero_binned_input_netcdf
subroutine aero_binned_input_netcdf(aero_binned, ncid, bin_grid, aero_data)
Read full state.
Definition: aero_binned.F90:467
pmc_output::tag_output_state_single
integer, parameter tag_output_state_single
Internal-use variable only.
Definition: output.F90:102
pmc_util::free_unit
subroutine free_unit(unit)
Frees a unit number returned by get_unit().
Definition: util.F90:172
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_netcdf::pmc_nc_read_real
subroutine pmc_nc_read_real(ncid, var, name, must_be_present)
Read a single real from a NetCDF file.
Definition: netcdf.F90:149
pmc_aero_data
The aero_data_t structure and associated subroutines.
Definition: aero_data.F90:9
pmc_bin_grid::bin_grid_t
1D grid, either logarithmic or linear.
Definition: bin_grid.F90:33
pmc_env_state::pmc_mpi_pack_size_env_state
integer function pmc_mpi_pack_size_env_state(val)
Determines the number of bytes required to pack the given value.
Definition: env_state.F90:336
pmc_gas_state::pmc_mpi_pack_size_gas_state
integer function pmc_mpi_pack_size_gas_state(val)
Determines the number of bytes required to pack the given value.
Definition: gas_state.F90:519
pmc_output::output_type_invalid
integer, parameter output_type_invalid
Type code for undefined or invalid output.
Definition: output.F90:88
pmc_aero_state::aero_state_t
The current collection of aerosol particles.
Definition: aero_state.F90:63
pmc_output::recv_output_state_central
subroutine recv_output_state_central(prefix, aero_data, gas_data, index, time, del_t, i_repeat, record_removals, record_optical, uuid, remote_proc)
Receive the state for the "central" output method on the root process.
Definition: output.F90:430
pmc_output::output_type_dist
integer, parameter output_type_dist
Type code for distributed output (one file per process, written by each process).
Definition: output.F90:94
pmc_output::input_filename_list
subroutine input_filename_list(prefix, filename_list)
Find all NetCDF (.nc) filenames that match the given prefix.
Definition: output.F90:568
pmc_gas_state::gas_state_input_netcdf
subroutine gas_state_input_netcdf(gas_state, ncid, gas_data)
Read full state.
Definition: gas_state.F90:630