PartMC  2.3.0
output.F90
Go to the documentation of this file.
1 ! Copyright (C) 2005-2015 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.3.0"
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  call env_state_allocate(env_state_write)
185  call gas_state_allocate(gas_state_write)
186  call env_state_copy(env_state, env_state_write)
187  call gas_state_copy(gas_state, gas_state_write)
188  call env_state_reduce_avg(env_state_write)
189  call gas_state_reduce_avg(gas_state_write)
190  call aero_state_allocate(aero_state_write)
191  call aero_state_mpi_gather(aero_state, aero_state_write)
192  if (rank == 0) then
193  call output_state_to_file(prefix, aero_data, aero_state_write, &
194  gas_data, gas_state_write, env_state_write, index, time, &
195  del_t, i_repeat, record_removals, record_optical, uuid, &
196  rank, 1)
197  end if
198  call aero_state_deallocate(aero_state_write)
199  call env_state_deallocate(env_state_write)
200  call gas_state_deallocate(gas_state_write)
201 #endif
202  end if
203  else
204  call die_msg(626743323, "Unknown output_type: " &
205  // trim(integer_to_string(output_type)))
206  end if
207 
208  end subroutine output_state
209 
210 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
211 
212  !> Make a filename from a given prefix and other information.
213  subroutine make_filename(filename, prefix, suffix, index, i_repeat, &
214  write_rank, write_n_proc)
215 
216  !> Filename to create.
217  character(len=*), intent(out) :: filename
218  !> Filename prefix.
219  character(len=*), intent(in) :: prefix
220  !> Filename suffix.
221  character(len=*), intent(in) :: suffix
222  !> Filename index.
223  integer, intent(in), optional :: index
224  !> Current repeat number.
225  integer, intent(in), optional :: i_repeat
226  !> Rank to write into file.
227  integer, intent(in), optional :: write_rank
228  !> Number of processes to write into file.
229  integer, intent(in), optional :: write_n_proc
230 
231  integer :: ncid, use_rank, use_n_proc
232  character(len=100) :: proc_string, index_string, repeat_string
233 
234  if (present(write_rank)) then
235  use_rank = write_rank
236  else
237  use_rank = pmc_mpi_rank()
238  end if
239  if (present(write_n_proc)) then
240  use_n_proc = write_n_proc
241  else
242  use_n_proc = pmc_mpi_size()
243  end if
244 
245  repeat_string = ""
246  proc_string = ""
247  index_string = ""
248  if (present(i_repeat)) write(repeat_string, '(a,i4.4)') '_', i_repeat
249  if (use_n_proc > 1) write(proc_string, '(a,i4.4)') '_', (use_rank + 1)
250  if (present(index)) write(index_string, '(a,i8.8)') '_', index
251  write(filename, '(a,a,a,a,a)') trim(prefix), trim(repeat_string), &
252  trim(proc_string), trim(index_string), trim(suffix)
253 
254  end subroutine make_filename
255 
256 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
257 
258  !> Helper routine to write time variables. Do not call directly.
259  subroutine write_time(ncid, time, del_t, index)
260 
261  !> NetCDF file ID, in data mode.
262  integer, intent(in) :: ncid
263  !> Current time (s).
264  real(kind=dp), intent(in) :: time
265  !> Current timestep (s).
266  real(kind=dp), intent(in) :: del_t
267  !> Filename index.
268  integer, intent(in) :: index
269 
270  call pmc_nc_write_real(ncid, time, "time", unit="s", &
271  description="time elapsed since simulation start")
272  call pmc_nc_write_real(ncid, del_t, "timestep", unit="s", &
273  description="current timestep size")
274  call pmc_nc_write_integer(ncid, index, "timestep_index", &
275  description="an integer that is 1 on the first timestep, " &
276  // "2 on the second timestep, etc.")
277 
278  end subroutine write_time
279 
280 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
281 
282  !> Write the current state for a single process. Do not call this
283  !> subroutine directly, but rather call output_state().
284  subroutine output_state_to_file(prefix, aero_data, aero_state, gas_data, &
285  gas_state, env_state, index, time, del_t, i_repeat, record_removals, &
286  record_optical, uuid, write_rank, write_n_proc)
287 
288  !> Prefix of state file.
289  character(len=*), intent(in) :: prefix
290  !> Aerosol data.
291  type(aero_data_t), intent(in) :: aero_data
292  !> Aerosol state.
293  type(aero_state_t), intent(in) :: aero_state
294  !> Gas data.
295  type(gas_data_t), intent(in) :: gas_data
296  !> Gas state.
297  type(gas_state_t), intent(in) :: gas_state
298  !> Environment state.
299  type(env_state_t), intent(in) :: env_state
300  !> Filename index.
301  integer, intent(in) :: index
302  !> Current time (s).
303  real(kind=dp), intent(in) :: time
304  !> Current timestep (s).
305  real(kind=dp), intent(in) :: del_t
306  !> Current repeat number.
307  integer, intent(in) :: i_repeat
308  !> Whether to output particle removal info.
309  logical, intent(in) :: record_removals
310  !> Whether to output aerosol optical properties.
311  logical, intent(in) :: record_optical
312  !> UUID of the simulation.
313  character(len=PMC_UUID_LEN), intent(in) :: uuid
314  !> Rank to write into file.
315  integer, intent(in), optional :: write_rank
316  !> Number of processes to write into file.
317  integer, intent(in), optional :: write_n_proc
318 
319  character(len=len(prefix)+100) :: filename
320  integer :: ncid
321 
322  !> \page output_format_general Output File Format: General Information
323  !!
324  !! The general information global NetCDF attributes are:
325  !! - \b title: always set to the string "PartMC version V.V.V
326  !! output file" where V.V.V is the PartMC version that created
327  !! the file
328  !! - \b source: set to the string "PartMC version V.V.V"
329  !! - \b UUID: a string of the form F47AC10B-58CC-4372-A567-0E02B2C3D479
330  !! which is the same for all files generated by a single call of
331  !! PartMC.
332  !! - \b Conventions: set to the string "CF-1.4", indicating
333  !! compliance with the <a
334  !! href="http://cf-pcmdi.llnl.gov/documents/cf-conventions/1.4">CF
335  !! convention format</a>
336  !! - \b history: set to the string "YYYY-MM-DDThh:mm:ss[+-]ZZ:zz
337  !! created by PartMC version V.V.V" where the first term is
338  !! the file creation time in the <a
339  !! href="http://en.wikipedia.org/wiki/ISO_8601">ISO 8601
340  !! format</a>. For example, noon Pacific Standard Time (PST)
341  !! on February 1st, 2000 would be written
342  !! 2000-02-01T12:00:00-08:00. The date and time variables
343  !! are:
344  !! - YYYY: four-digit year
345  !! - MM: two-digit month number
346  !! - DD: two-digit day within month
347  !! - T: literal "T" character
348  !! - hh: two-digit hour in 24-hour format
349  !! - mm: two-digit minute
350  !! - ss: two-digit second
351  !! - [+-]: a literal "+" or "-" character giving the time zone
352  !! offset sign
353  !! - ZZ: two-digit hours of the time zone offset from UTC
354  !! - zz: two-digit minutes of the time zone offset from UTC
355  !!
356  !! The general information NetCDF variables are:
357  !! - \b time (unit s): time elapsed since the simulation start time,
358  !! as specified in the \ref output_format_env_state section
359  !! - \b timestep (unit s): the current timestep size
360  !! - \b repeat: the repeat number of this simulation (starting from 1)
361  !! - \b timestep_index: an integer that is 1 on the first timestep, 2
362  !! on the second timestep, etc.
363  !! - \b process (MPI only): the process number (starting from 1)
364  !! that output this data file
365  !! - \b total_processes (MPI only): the total number of processes
366  !! involved in writing data (may be less than the total number of
367  !! processes that computed the data)
368 
369  call make_filename(filename, prefix, ".nc", index, i_repeat, write_rank, &
370  write_n_proc)
371  call pmc_nc_open_write(filename, ncid)
372  call pmc_nc_write_info(ncid, uuid, &
373  "PartMC version " // trim(partmc_version), write_rank, write_n_proc)
374  call write_time(ncid, time, del_t, index)
375  call pmc_nc_write_integer(ncid, i_repeat, "repeat", &
376  description="repeat number of this simulation (starting from 1)")
377 
378  call env_state_output_netcdf(env_state, ncid)
379  call gas_data_output_netcdf(gas_data, ncid)
380  call gas_state_output_netcdf(gas_state, ncid, gas_data)
381  call aero_data_output_netcdf(aero_data, ncid)
382  call aero_state_output_netcdf(aero_state, ncid, aero_data, &
383  record_removals, record_optical)
384 
385  call pmc_nc_check(nf90_close(ncid))
386 
387  end subroutine output_state_to_file
388 
389 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
390 
391  !> Send the state for the "central" output method to the root process.
392  subroutine send_output_state_central(aero_state, gas_state, env_state)
393 
394  !> Aerosol state.
395  type(aero_state_t), intent(in) :: aero_state
396  !> Gas state.
397  type(gas_state_t), intent(in) :: gas_state
398  !> Environment state.
399  type(env_state_t), intent(in) :: env_state
400 
401 #ifdef PMC_USE_MPI
402  integer :: buffer_size, max_buffer_size, position, ierr
403  character, allocatable :: buffer(:)
404 
405  call assert(645797304, pmc_mpi_rank() /= 0)
406 
407  max_buffer_size = 0
408  max_buffer_size = max_buffer_size &
409  + pmc_mpi_pack_size_env_state(env_state)
410  max_buffer_size = max_buffer_size &
411  + pmc_mpi_pack_size_gas_state(gas_state)
412  max_buffer_size = max_buffer_size &
413  + pmc_mpi_pack_size_aero_state(aero_state)
414  allocate(buffer(max_buffer_size))
415  position = 0
416  call pmc_mpi_pack_env_state(buffer, position, env_state)
417  call pmc_mpi_pack_gas_state(buffer, position, gas_state)
418  call pmc_mpi_pack_aero_state(buffer, position, aero_state)
419  call assert(839343839, position <= max_buffer_size)
420  buffer_size = position
421  call mpi_send(buffer, buffer_size, mpi_character, 0, &
422  tag_output_state_central, mpi_comm_world, ierr)
423  call pmc_mpi_check_ierr(ierr)
424  deallocate(buffer)
425 #endif
426 
427  end subroutine send_output_state_central
428 
429 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
430 
431  !> Receive the state for the "central" output method on the root
432  !> process.
433  subroutine recv_output_state_central(prefix, aero_data, gas_data, index, &
434  time, del_t, i_repeat, record_removals, record_optical, uuid, &
435  remote_proc)
436 
437  !> Prefix of state file.
438  character(len=*), intent(in) :: prefix
439  !> Aerosol data.
440  type(aero_data_t), intent(in) :: aero_data
441  !> Gas data.
442  type(gas_data_t), intent(in) :: gas_data
443  !> Filename index.
444  integer, intent(in) :: index
445  !> Current time (s).
446  real(kind=dp), intent(in) :: time
447  !> Current timestep (s).
448  real(kind=dp), intent(in) :: del_t
449  !> Current repeat number.
450  integer, intent(in) :: i_repeat
451  !> Whether to output particle removal info.
452  logical, intent(in) :: record_removals
453  !> Whether to output aerosol_optical_properties.
454  logical, intent(in) :: record_optical
455  !> UUID of the simulation.
456  character(len=PMC_UUID_LEN), intent(in) :: uuid
457  !> Process number to receive from.
458  integer, intent(in) :: remote_proc
459 
460 #ifdef PMC_USE_MPI
461  type(env_state_t) :: env_state
462  type(gas_state_t) :: gas_state
463  type(aero_state_t) :: aero_state
464  integer :: buffer_size, position, status(mpi_status_size)
465  integer :: n_proc, ierr
466  character, allocatable :: buffer(:)
467 
468  call assert(206980035, pmc_mpi_rank() == 0)
469  call assert(291452117, remote_proc /= 0)
470  n_proc = pmc_mpi_size()
471 
472  ! get buffer size
473  call mpi_probe(remote_proc, tag_output_state_central, mpi_comm_world, &
474  status, ierr)
475  call pmc_mpi_check_ierr(ierr)
476  call mpi_get_count(status, mpi_character, buffer_size, ierr)
477 
478  ! get message
479  allocate(buffer(buffer_size))
480  call mpi_recv(buffer, buffer_size, mpi_character, remote_proc, &
481  tag_output_state_central, mpi_comm_world, status, ierr)
482  call pmc_mpi_check_ierr(ierr)
483 
484  ! unpack message
485  position = 0
486  call env_state_allocate(env_state)
487  call gas_state_allocate(gas_state)
488  call aero_state_allocate(aero_state)
489  call pmc_mpi_unpack_env_state(buffer, position, env_state)
490  call pmc_mpi_unpack_gas_state(buffer, position, gas_state)
491  call pmc_mpi_unpack_aero_state(buffer, position, aero_state)
492  call assert(279581330, position == buffer_size)
493  deallocate(buffer)
494 
495  call output_state_to_file(prefix, aero_data, aero_state, gas_data, &
496  gas_state, env_state, index, time, del_t, i_repeat, &
497  record_removals, record_optical, uuid, remote_proc, n_proc)
498 
499  call env_state_deallocate(env_state)
500  call gas_state_deallocate(gas_state)
501  call aero_state_deallocate(aero_state)
502 #endif
503 
504  end subroutine recv_output_state_central
505 
506 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
507 
508  !> Read the current state.
509  subroutine input_state(filename, index, time, del_t, i_repeat, uuid, &
510  aero_data, aero_state, gas_data, gas_state, env_state)
511 
512  !> Prefix of state file.
513  character(len=*), intent(in) :: filename
514  !> Filename index.
515  integer, intent(out) :: index
516  !> Current time (s).
517  real(kind=dp), intent(out) :: time
518  !> Current timestep (s).
519  real(kind=dp), intent(out) :: del_t
520  !> Current repeat number.
521  integer, intent(out) :: i_repeat
522  !> UUID of the simulation.
523  character(len=PMC_UUID_LEN), intent(out) :: uuid
524  !> Aerosol data.
525  type(aero_data_t), optional, intent(inout) :: aero_data
526  !> Aerosol state.
527  type(aero_state_t), optional, intent(inout) :: aero_state
528  !> Gas data.
529  type(gas_data_t), optional, intent(inout) :: gas_data
530  !> Gas state.
531  type(gas_state_t), optional, intent(inout) :: gas_state
532  !> Environment state.
533  type(env_state_t), optional, intent(inout) :: env_state
534 
535  integer :: ncid
536 
537  call assert_msg(819739354, pmc_mpi_rank() == 0, &
538  "can only call from process 0")
539 
540  call pmc_nc_open_read(filename, ncid)
541 
542  call pmc_nc_check(nf90_get_att(ncid, nf90_global, "UUID", uuid))
543 
544  call pmc_nc_read_real(ncid, time, "time")
545  call pmc_nc_read_real(ncid, del_t, "timestep")
546  call pmc_nc_read_integer(ncid, i_repeat, "repeat")
547  call pmc_nc_read_integer(ncid, index, "timestep_index")
548 
549  if (present(aero_data)) then
550  call aero_data_input_netcdf(aero_data, ncid)
551  if (present(aero_state)) then
552  call aero_state_input_netcdf(aero_state, ncid, aero_data)
553  end if
554  else
555  call assert_msg(289621231, present(aero_state) .eqv. .false., &
556  "cannot input aero_state without aero_data")
557  end if
558 
559  if (present(gas_data)) then
560  call gas_data_input_netcdf(gas_data, ncid)
561  if (present(gas_state)) then
562  call gas_state_input_netcdf(gas_state, ncid, gas_data)
563  end if
564  else
565  call assert_msg(874298496, present(gas_state) .eqv. .false., &
566  "cannot input gas_state without gas_data")
567  end if
568 
569  if (present(env_state)) then
570  call env_state_input_netcdf(env_state, ncid)
571  end if
572 
573  call pmc_nc_close(ncid)
574 
575  end subroutine input_state
576 
577 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
578 
579  !> Find all NetCDF (.nc) filenames that match the given prefix.
580  subroutine input_filename_list(prefix, filename_list)
581 
582  !> Filename prefix to search for.
583  character(len=*), intent(in) :: prefix
584  !> Filename list.
585  character(len=*), intent(inout), allocatable :: filename_list(:)
586 
587  integer :: n_file, index, unit, ios
588  character(len=len(prefix)+100) :: filename
589  logical :: done
590 
591  call assert_msg(277193351, pmc_mpi_rank() == 0, &
592  "can only call from process 0")
593 
594  index = 1
595  done = .false.
596  unit = get_unit()
597  do while (.not. done)
598  write(filename, '(a,a,i8.8,a)') trim(prefix), '_', index, '.nc'
599  open(unit=unit, file=filename, status='old', iostat=ios)
600  if (ios /= 0) then
601  done = .true.
602  else
603  index = index + 1
604  close(unit)
605  end if
606  end do
607  call free_unit(unit)
608 
609  n_file = index - 1
610  call ensure_string_array_size(filename_list, n_file)
611  do index = 1,n_file
612  write(filename, '(a,a,i8.8,a)') trim(prefix), '_', index, '.nc'
613  filename_list(index) = filename
614  end do
615 
616  end subroutine input_filename_list
617 
618 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
619 
620  !> Find the number of repeats and indices for the given prefix.
621  subroutine input_n_files(prefix, n_repeat, n_index)
622 
623  !> Filename prefix to search for.
624  character(len=*), intent(in) :: prefix
625  !> Number of repeats found.
626  integer, intent(out) :: n_repeat
627  !> Number of indices found.
628  integer, intent(out) :: n_index
629 
630  integer :: repeat, index, unit, ios
631  character(len=len(prefix)+100) :: filename
632  logical :: done
633 
634  call assert_msg(711223711, pmc_mpi_rank() == 0, &
635  "can only call from process 0")
636 
637  unit = get_unit()
638 
639  repeat = 1
640  index = 1
641  done = .false.
642  do while (.not. done)
643  call make_filename(filename, prefix, ".nc", index, repeat)
644  open(unit=unit, file=filename, status='old', iostat=ios)
645  if (ios /= 0) then
646  done = .true.
647  else
648  repeat = repeat + 1
649  close(unit)
650  end if
651  end do
652  n_repeat = repeat - 1
653  call assert_msg(252703940, n_repeat >= 1, &
654  "no files found with prefix: " // trim(prefix))
655 
656  repeat = 1
657  index = 1
658  done = .false.
659  do while (.not. done)
660  call make_filename(filename, prefix, ".nc", index, repeat)
661  open(unit=unit, file=filename, status='old', iostat=ios)
662  if (ios /= 0) then
663  done = .true.
664  else
665  index = index + 1
666  close(unit)
667  end if
668  end do
669  n_index = index - 1
670 
671  end subroutine input_n_files
672 
673 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
674 
675  !> Write the current sectional data.
676  subroutine output_sectional(prefix, bin_grid, aero_data, aero_binned, &
677  gas_data, gas_state, env_state, index, time, del_t, uuid)
678 
679  !> Prefix of filename to write
680  character(len=*), intent(in) :: prefix
681  !> Bin grid.
682  type(bin_grid_t), intent(in) :: bin_grid
683  !> Aerosol data.
684  type(aero_data_t), intent(in) :: aero_data
685  !> Binned aerosol data.
686  type(aero_binned_t), intent(in) :: aero_binned
687  !> Gas data.
688  type(gas_data_t), intent(in) :: gas_data
689  !> Gas state.
690  type(gas_state_t), intent(in) :: gas_state
691  !> Environment state.
692  type(env_state_t), intent(in) :: env_state
693  !> Filename index.
694  integer, intent(in) :: index
695  !> Current time (s).
696  real(kind=dp), intent(in) :: time
697  !> Current output time-step (s).
698  real(kind=dp), intent(in) :: del_t
699  !> UUID of the simulation.
700  character(len=PMC_UUID_LEN), intent(in) :: uuid
701 
702  integer :: ncid
703  character(len=len(prefix)+100) :: filename
704 
705  write(filename, '(a,a,i8.8,a)') trim(prefix), &
706  '_', index, '.nc'
707  call pmc_nc_open_write(filename, ncid)
708  call pmc_nc_write_info(ncid, uuid, &
709  "PartMC version " // trim(partmc_version))
710  call write_time(ncid, time, del_t, index)
711 
712  ! write data
713  call env_state_output_netcdf(env_state, ncid)
714  call gas_data_output_netcdf(gas_data, ncid)
715  call gas_state_output_netcdf(gas_state, ncid, gas_data)
716  call aero_data_output_netcdf(aero_data, ncid)
717  call aero_binned_output_netcdf(aero_binned, ncid, bin_grid, &
718  aero_data)
719 
720  call pmc_nc_check(nf90_close(ncid))
721 
722  end subroutine output_sectional
723 
724 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
725 
726  !> Input sectional data.
727  subroutine input_sectional(filename, index, time, del_t, uuid, bin_grid, &
728  aero_data, aero_binned, gas_data, gas_state, env_state)
729 
730  !> Filename to read.
731  character(len=*), intent(in) :: filename
732  !> Filename index.
733  integer, intent(out) :: index
734  !> Current time (s).
735  real(kind=dp), intent(out) :: time
736  !> Current output time-step (s).
737  real(kind=dp), intent(out) :: del_t
738  !> UUID of the simulation.
739  character(len=PMC_UUID_LEN), intent(out) :: uuid
740  !> Bin grid.
741  type(bin_grid_t), optional, intent(inout) :: bin_grid
742  !> Aerosol data.
743  type(aero_data_t), optional, intent(inout) :: aero_data
744  !> Binned aerosol data.
745  type(aero_binned_t), optional, intent(inout) :: aero_binned
746  !> Gas data.
747  type(gas_data_t), optional, intent(inout) :: gas_data
748  !> Gas state.
749  type(gas_state_t), optional, intent(inout) :: gas_state
750  !> Environment state.
751  type(env_state_t), optional, intent(inout) :: env_state
752 
753  integer :: ncid
754 
755  call assert_msg(559676785, pmc_mpi_rank() == 0, &
756  "can only call from process 0")
757 
758  call pmc_nc_open_read(filename, ncid)
759 
760  call pmc_nc_check(nf90_get_att(ncid, nf90_global, "UUID", uuid))
761 
762  call pmc_nc_read_real(ncid, time, "time")
763  call pmc_nc_read_real(ncid, del_t, "timestep")
764  call pmc_nc_read_integer(ncid, index, "timestep_index")
765 
766  if (present(bin_grid)) then
767  call bin_grid_input_netcdf(bin_grid, ncid, "aero_diam", scale=0.5d0)
768  end if
769  if (present(aero_data)) then
770  call aero_data_input_netcdf(aero_data, ncid)
771  end if
772  if (present(aero_binned)) then
773  call assert_msg(585353528, &
774  present(bin_grid) .and. present(aero_data), &
775  "cannot input aero_binned without bin_grid and aero_data")
776  call aero_binned_input_netcdf(aero_binned, ncid, bin_grid, &
777  aero_data)
778  end if
779 
780  if (present(gas_data)) then
781  call gas_data_input_netcdf(gas_data, ncid)
782  if (present(gas_state)) then
783  call gas_state_input_netcdf(gas_state, ncid, gas_data)
784  end if
785  else
786  call assert_msg(214545112, present(gas_state) .eqv. .false., &
787  "cannot input gas_state without gas_data")
788  end if
789 
790  if (present(env_state)) then
791  call env_state_input_netcdf(env_state, ncid)
792  end if
793 
794  call pmc_nc_close(ncid)
795 
796  end subroutine input_sectional
797 
798 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
799 
800  !> Read the specification for an output type from a spec file and
801  !> generate it.
802  subroutine spec_file_read_output_type(file, output_type)
803 
804  !> Spec file.
805  type(spec_file_t), intent(inout) :: file
806  !> Kernel type.
807  integer, intent(out) :: output_type
808 
809  character(len=SPEC_LINE_MAX_VAR_LEN) :: output_type_name
810 
811  !> \page input_format_output Input File Format: Output Type
812  !!
813  !! The output type is specified by the parameter:
814  !! - \b output_type (string): type of disk output --- must be
815  !! one of: \c central to write one file per process, but all
816  !! written by process 0; \c dist for every process to
817  !! write its own state file; or \c single to transfer all data
818  !! to process 0 and write a single unified output file
819  !!
820  !! See also:
821  !! - \ref spec_file_format --- the input file text format
822 
823  call spec_file_read_string(file, 'output_type', output_type_name)
824  if (trim(output_type_name) == 'central') then
825  output_type = output_type_central
826  elseif (trim(output_type_name) == 'dist') then
827  output_type = output_type_dist
828  elseif (trim(output_type_name) == 'single') then
829  output_type = output_type_single
830  else
831  call spec_file_die_msg(392313600, file, &
832  "Unknown output type: " // trim(output_type_name))
833  end if
834 
835  end subroutine spec_file_read_output_type
836 
837 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
838 
839 end module pmc_output
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:433
An input file with extra data for printing messages.
Definition: spec_file.F90:59
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:509
The aero_data_t structure and associated subroutines.
Definition: aero_data.F90:9
integer function pmc_mpi_pack_size_aero_state(val)
Determines the number of bytes required to pack the given value.
subroutine die_msg(code, error_msg)
Error immediately.
Definition: util.F90:133
subroutine input_filename_list(prefix, filename_list)
Find all NetCDF (.nc) filenames that match the given prefix.
Definition: output.F90:580
subroutine pmc_mpi_pack_env_state(buffer, position, val)
Packs the given value into the buffer, advancing position.
Definition: env_state.F90:396
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:1148
integer function pmc_mpi_pack_size_gas_state(val)
Determines the number of bytes required to pack the given value.
Definition: gas_state.F90:486
subroutine input_n_files(prefix, n_repeat, n_index)
Find the number of repeats and indices for the given prefix.
Definition: output.F90:621
subroutine gas_data_input_netcdf(gas_data, ncid)
Read full state.
Definition: gas_data.F90:386
The env_state_t structure and associated subroutines.
Definition: env_state.F90:9
subroutine aero_state_allocate(aero_state)
Allocates aerosol arrays.
Definition: aero_state.F90:83
subroutine pmc_nc_check(status)
Check the status of a NetCDF function call.
Definition: netcdf.F90:22
subroutine pmc_nc_read_integer(ncid, var, name, must_be_present)
Read a single integer from a NetCDF file.
Definition: netcdf.F90:183
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:213
subroutine pmc_mpi_pack_gas_state(buffer, position, val)
Packs the given value into the buffer, advancing position.
Definition: gas_state.F90:499
subroutine aero_state_mpi_gather(aero_state, aero_state_total)
Gathers data from all processes into one aero_state on process 0.
subroutine assert_msg(code, condition_ok, error_msg)
Errors unless condition_ok is true.
Definition: util.F90:76
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:109
subroutine bin_grid_input_netcdf(bin_grid, ncid, dim_name, scale)
Read full state.
Definition: bin_grid.F90:573
The gas_data_t structure and associated subroutines.
Definition: gas_data.F90:9
integer function pmc_mpi_size()
Returns the total number of processes.
Definition: mpi.F90:133
Common utility subroutines.
Definition: util.F90:9
Current environment state.
Definition: env_state.F90:26
subroutine env_state_deallocate(env_state)
Free all storage.
Definition: env_state.F90:78
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:392
The aero_state_t structure and assocated subroutines.
Definition: aero_state.F90:9
subroutine pmc_mpi_unpack_env_state(buffer, position, val)
Unpacks the given value from the buffer, advancing position.
Definition: env_state.F90:429
integer function pmc_mpi_rank()
Returns the rank of the current process.
Definition: mpi.F90:116
Wrapper functions for MPI.
Definition: mpi.F90:13
subroutine gas_state_deallocate(gas_state)
Free all storage.
Definition: gas_state.F90:63
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:625
The gas_state_t structure and associated subroutines.
Definition: gas_state.F90:9
subroutine gas_state_allocate(gas_state)
Allocate storage for gas species.
Definition: gas_state.F90:36
subroutine env_state_input_netcdf(env_state, ncid)
Read full state.
Definition: env_state.F90:542
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:727
character(len=pmc_util_convert_string_len) function integer_to_string(val)
Convert an integer to a string format.
Definition: util.F90:743
The current collection of aerosol particles.
Definition: aero_state.F90:63
subroutine pmc_mpi_pack_aero_state(buffer, position, val)
Packs the given value into the buffer, advancing position.
1D grid, either logarithmic or linear.
Definition: bin_grid.F90:33
subroutine pmc_mpi_check_ierr(ierr)
Dies if ierr is not ok.
Definition: mpi.F90:39
subroutine pmc_nc_write_info(ncid, uuid, source, write_rank, write_n_proc)
Write basic information to a NetCDF file.
Definition: netcdf.F90:97
subroutine env_state_copy(env_from, env_to)
env_to = env_from
Definition: env_state.F90:138
The bin_grid_t structure and associated subroutines.
Definition: bin_grid.F90:9
subroutine aero_state_deallocate(aero_state)
Deallocates a previously allocated aerosol.
Definition: aero_state.F90:100
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:394
subroutine pmc_nc_close(ncid)
Close a NetCDF file.
Definition: netcdf.F90:85
subroutine write_time(ncid, time, del_t, index)
Helper routine to write time variables. Do not call directly.
Definition: output.F90:259
integer function get_unit()
Returns an available unit number. This should be freed by free_unit().
Definition: util.F90:147
The aero_binned_t structure and associated subroutines.
Definition: aero_binned.F90:9
subroutine aero_state_input_netcdf(aero_state, ncid, aero_data)
Read full state.
subroutine gas_state_input_netcdf(gas_state, ncid, gas_data)
Read full state.
Definition: gas_state.F90:597
Wrapper functions for NetCDF. These all take a NetCDF ncid in data mode and return with it again in d...
Definition: netcdf.F90:11
subroutine spec_file_die_msg(code, file, msg)
Exit with an error message containing filename and line number.
Definition: spec_file.F90:73
subroutine pmc_nc_open_read(filename, ncid)
Open a NetCDF file for reading.
Definition: netcdf.F90:54
subroutine aero_data_input_netcdf(aero_data, ncid)
Read full state.
Definition: aero_data.F90:664
subroutine pmc_mpi_unpack_aero_state(buffer, position, val)
Unpacks the given value from the buffer, advancing position.
Current state of the gas mixing ratios in the system.
Definition: gas_state.F90:26
subroutine aero_binned_input_netcdf(aero_binned, ncid, bin_grid, aero_data)
Read full state.
subroutine pmc_nc_open_write(filename, ncid)
Open a NetCDF file for writing.
Definition: netcdf.F90:69
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:427
subroutine env_state_reduce_avg(val)
Average val over all processes, with the result only on the root process.
Definition: env_state.F90:348
subroutine pmc_nc_read_real(ncid, var, name, must_be_present)
Read a single real from a NetCDF file.
Definition: netcdf.F90:148
Constant gas data.
Definition: gas_data.F90:29
subroutine env_state_allocate(env_state)
Allocate an empty environment.
Definition: env_state.F90:56
Aerosol material properties and associated data.
Definition: aero_data.F90:40
subroutine assert(code, condition_ok)
Errors unless condition_ok is true.
Definition: util.F90:102
subroutine free_unit(unit)
Frees a unit number returned by get_unit().
Definition: util.F90:171
Write data in NetCDF format.
Definition: output.F90:68
integer function pmc_mpi_pack_size_env_state(val)
Determines the number of bytes required to pack the given value.
Definition: env_state.F90:373
subroutine gas_state_reduce_avg(val)
Average val over all processes, with the result only on the root process.
Definition: gas_state.F90:465
Aerosol number and volume distributions stored per bin.
Definition: aero_binned.F90:33
subroutine gas_state_copy(from_state, to_state)
Copy to an already allocated to_state.
Definition: gas_state.F90:87
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:676
subroutine pmc_mpi_unpack_gas_state(buffer, position, val)
Unpacks the given value from the buffer, advancing position.
Definition: gas_state.F90:522