PartMC  2.2.0
output.F90
Go to the documentation of this file.
00001 ! Copyright (C) 2005-2011 Nicole Riemer and Matthew West
00002 ! Licensed under the GNU General Public License version 2 or (at your
00003 ! option) any later version. See the file COPYING for details.
00004 
00005 !> \file
00006 !> The pmc_output module.
00007 
00008 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
00009 
00010 !> \page output_format Output File Format
00011 !!
00012 !! PartMC output files are in the <a
00013 !! href="http://www.unidata.ucar.edu/software/netcdf/">NetCDF Classic
00014 !! Format</a> (also known as NetCDF-3 format). The dimensions and
00015 !! variables in the files will depend on the type of run (particle,
00016 !! analytical solution, etc), and options in the spec file (e.g. \c
00017 !! record_removals and \c do_optical).
00018 !!
00019 !! The state of the simulation is periodically output during the run,
00020 !! with frequency determined by the \c t_output input parameter. Each
00021 !! output file has a filename of the form \c PREFIX_RRRR_SSSSSSSS.nc,
00022 !! where \c PREFIX is given by the \c output_prefix input parameter,
00023 !! \c RRRR is the four-digit repeat number (starting from 1), and \c
00024 !! SSSSSSSS is the eight-digit output index (starting at 1 and
00025 !! incremented each time the state is output). For exact and sectional
00026 !! simulations all repeats would be identical so there is no support
00027 !! for repeating and the filename is of the format \c
00028 !! PREFIX_SSSSSSSS.nc.
00029 !!
00030 !! If run in parallel and \c output_type is \c central or \c dist,
00031 !! then the output files have names like \c
00032 !! PREFIX_RRRR_PPPP_SSSSSSSS.nc, where \c PPPP is a four-digit
00033 !! process number (starting from 1) and the other variables are as
00034 !! above. If \c output_type is \c single then the output file naming
00035 !! scheme as the same as for serial runs.
00036 !!
00037 !! The data in each output file comes in several different groups, as
00038 !! follows:
00039 !!
00040 !! \subpage output_format_general "General Information"
00041 !!
00042 !! \subpage output_format_env_state "Environment State"
00043 !!
00044 !! \subpage output_format_gas_data "Gas Material Data"
00045 !!
00046 !! \subpage output_format_gas_state "Gas State"
00047 !!
00048 !! \subpage output_format_aero_data "Aerosol Material Data"
00049 !!
00050 !! \subpage output_format_aero_state "Aerosol Particle State"
00051 !! (only for particle-resolved simulations)
00052 !!
00053 !! \subpage output_format_aero_removed "Aerosol Particle Removal Information"
00054 !! (only for particle-resolved simulations, if \c record_removals is \c yes)
00055 !!
00056 !! \subpage output_format_aero_weight_array "Aerosol Weighting Function"
00057 !! (only for particle-resolved simulations)
00058 !!
00059 !! \subpage output_format_bin_grid "Bin Grid Data"
00060 !! (only for exact and sectional simulations)
00061 !!
00062 !! \subpage output_format_aero_binned "Aerosol Binned Sectional State"
00063 !! (only for exact and sectional simulations)
00064 
00065 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
00066 
00067 !> Write data in NetCDF format.
00068 module pmc_output
00069 
00070   use pmc_bin_grid
00071   use pmc_aero_data
00072   use pmc_aero_state
00073   use pmc_aero_binned
00074   use pmc_netcdf
00075   use pmc_env_state
00076   use pmc_util
00077   use pmc_gas_data
00078   use pmc_mpi
00079 #ifdef PMC_USE_MPI
00080   use mpi
00081 #endif
00082 
00083   !> Type code for undefined or invalid output.
00084   integer, parameter :: OUTPUT_TYPE_INVALID = 0
00085   !> Type code for centralized output (one file per process, all written
00086   !> by process 0).
00087   integer, parameter :: OUTPUT_TYPE_CENTRAL = 1
00088   !> Type code for distributed output (one file per process, written by
00089   !> each process).
00090   integer, parameter :: OUTPUT_TYPE_DIST    = 2
00091   !> Type code for single output (one file for all processes, written by
00092   !> process 0).
00093   integer, parameter :: OUTPUT_TYPE_SINGLE  = 3
00094 
00095   !> Internal-use variable only.
00096   integer, parameter :: TAG_OUTPUT_STATE_CENTRAL = 4341
00097   !> Internal-use variable only.
00098   integer, parameter :: TAG_OUTPUT_STATE_SINGLE  = 4342
00099   
00100 contains
00101 
00102 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
00103 
00104   !> Write the current state.
00105   subroutine output_state(prefix, output_type, aero_data, aero_state, &
00106        gas_data, gas_state, env_state, index, time, del_t, i_repeat, &
00107        record_removals, record_optical, uuid)
00108 
00109     !> Prefix of state file.
00110     character(len=*), intent(in) :: prefix
00111     !> Output type for parallel runs (see module constants).
00112     integer, intent(in) :: output_type
00113     !> Aerosol data.
00114     type(aero_data_t), intent(in) :: aero_data
00115     !> Aerosol state.
00116     type(aero_state_t), intent(in) :: aero_state
00117     !> Gas data.
00118     type(gas_data_t), intent(in) :: gas_data
00119     !> Gas state.
00120     type(gas_state_t), intent(in) :: gas_state
00121     !> Environment state.
00122     type(env_state_t), intent(in) :: env_state
00123     !> Filename index.
00124     integer, intent(in) :: index
00125     !> Current time (s).
00126     real(kind=dp), intent(in) :: time
00127     !> Current timestep (s).
00128     real(kind=dp), intent(in) :: del_t
00129     !> Current repeat number.
00130     integer, intent(in) :: i_repeat
00131     !> Whether to output particle removal info.
00132     logical, intent(in) :: record_removals
00133     !> Whether to output aerosol optical properties.
00134     logical, intent(in) :: record_optical
00135     !> UUID of the simulation.
00136     character(len=PMC_UUID_LEN), intent(in) :: uuid
00137     
00138     integer :: rank, n_proc
00139 #ifdef PMC_USE_MPI
00140     type(env_state_t) :: env_state_write
00141     type(gas_state_t) :: gas_state_write
00142     type(aero_state_t) :: aero_state_write
00143     integer :: ierr, status(MPI_STATUS_SIZE), i_proc, position
00144     character, allocatable :: buffer(:)
00145 #endif
00146 
00147     rank = pmc_mpi_rank()
00148     n_proc = pmc_mpi_size()
00149     if (output_type == OUTPUT_TYPE_CENTRAL) then
00150        ! write per-process data to separate files, but do it by
00151        ! transferring data to process 0 and having it do the writes
00152        if (rank == 0) then
00153           call output_state_to_file(prefix, aero_data, aero_state, gas_data, &
00154                gas_state, env_state, index, time, del_t, i_repeat, &
00155                record_removals, record_optical, rank, n_proc, uuid)
00156 #ifdef PMC_USE_MPI
00157           do i_proc = 1,(n_proc - 1)
00158              call recv_output_state_central(prefix, aero_data, gas_data, &
00159                   index, time, del_t, i_repeat, record_removals, &
00160                   record_optical, uuid, i_proc)
00161           end do
00162 #endif
00163        else ! rank /= 0
00164           call send_output_state_central(aero_state, gas_state, env_state)
00165        end if
00166     elseif (output_type == OUTPUT_TYPE_DIST) then
00167        ! have each process write its own data directly
00168        call output_state_to_file(prefix, aero_data, aero_state, gas_data, &
00169             gas_state, env_state, index, time, del_t, i_repeat, &
00170             record_removals, record_optical, rank, n_proc, uuid)
00171     elseif (output_type == OUTPUT_TYPE_SINGLE) then
00172        if (n_proc == 1) then
00173           call output_state_to_file(prefix, aero_data, aero_state, gas_data, &
00174                gas_state, env_state, index, time, del_t, i_repeat, &
00175                record_removals, record_optical, rank, n_proc, uuid)
00176        else
00177 #ifdef PMC_USE_MPI
00178           ! collect all data onto process 0 and then write it to a
00179           ! single file
00180           call env_state_allocate(env_state_write)
00181           call gas_state_allocate(gas_state_write)
00182           call env_state_copy(env_state, env_state_write)
00183           call gas_state_copy(gas_state, gas_state_write)
00184           call env_state_reduce_avg(env_state_write)
00185           call gas_state_reduce_avg(gas_state_write)
00186           call aero_state_allocate(aero_state_write)
00187           call aero_state_mpi_gather(aero_state, aero_state_write)
00188           if (rank == 0) then
00189              call output_state_to_file(prefix, aero_data, aero_state_write, &
00190                   gas_data, gas_state_write, env_state_write, index, time, &
00191                   del_t, i_repeat, record_removals, record_optical, rank, 1, &
00192                   uuid)
00193           end if
00194           call aero_state_deallocate(aero_state_write)
00195           call env_state_deallocate(env_state_write)
00196           call gas_state_deallocate(gas_state_write)
00197 #endif
00198        end if
00199     else
00200        call die_msg(626743323, "Unknown output_type: " &
00201             // trim(integer_to_string(output_type)))
00202     end if
00203 
00204   end subroutine output_state
00205 
00206 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
00207 
00208   !> Helper routine to write various global attributes. Do not call
00209   !> directly.
00210   subroutine write_header_and_time(ncid, time, del_t, index, uuid)
00211 
00212     !> NetCDF file ID, in data mode.
00213     integer, intent(in) :: ncid
00214     !> Current time (s).
00215     real(kind=dp), intent(in) :: time
00216     !> Current timestep (s).
00217     real(kind=dp), intent(in) :: del_t
00218     !> Filename index.
00219     integer, intent(in) :: index
00220     !> UUID of the simulation.
00221     character(len=PMC_UUID_LEN), intent(in) :: uuid
00222 
00223     character(len=500) :: history
00224 
00225     call pmc_nc_check(nf90_redef(ncid))
00226 
00227     call pmc_nc_check(nf90_put_att(ncid, NF90_GLOBAL, "source", &
00228          "PartMC version 2.2.0"))
00229     call pmc_nc_check(nf90_put_att(ncid, NF90_GLOBAL, "UUID", uuid))
00230     call iso8601_date_and_time(history)
00231     history((len_trim(history)+1):) = " created by PartMC"
00232     call pmc_nc_check(nf90_put_att(ncid, NF90_GLOBAL, "history", history))
00233     call pmc_nc_check(nf90_put_att(ncid, NF90_GLOBAL, "Conventions", &
00234          "CF-1.4"))
00235     
00236     call pmc_nc_check(nf90_enddef(ncid))
00237     
00238     call pmc_nc_write_real(ncid, time, "time", unit="s", &
00239          description="time elapsed since simulation start")
00240     call pmc_nc_write_real(ncid, del_t, "timestep", unit="s", &
00241          description="current timestep size")
00242     call pmc_nc_write_integer(ncid, index, "timestep_index", &
00243          description="an integer that is 1 on the first timestep, " &
00244          // "2 on the second timestep, etc.")
00245 
00246   end subroutine write_header_and_time
00247 
00248 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
00249 
00250   !> Write the current state for a single process. Do not call this
00251   !> subroutine directly, but rather call output_state().
00252   subroutine output_state_to_file(prefix, aero_data, aero_state, gas_data, &
00253        gas_state, env_state, index, time, del_t, i_repeat, record_removals, &
00254        record_optical, write_rank, write_n_proc, uuid)
00255 
00256     !> Prefix of state file.
00257     character(len=*), intent(in) :: prefix
00258     !> Aerosol data.
00259     type(aero_data_t), intent(in) :: aero_data
00260     !> Aerosol state.
00261     type(aero_state_t), intent(in) :: aero_state
00262     !> Gas data.
00263     type(gas_data_t), intent(in) :: gas_data
00264     !> Gas state.
00265     type(gas_state_t), intent(in) :: gas_state
00266     !> Environment state.
00267     type(env_state_t), intent(in) :: env_state
00268     !> Filename index.
00269     integer, intent(in) :: index
00270     !> Current time (s).
00271     real(kind=dp), intent(in) :: time
00272     !> Current timestep (s).
00273     real(kind=dp), intent(in) :: del_t
00274     !> Current repeat number.
00275     integer, intent(in) :: i_repeat
00276     !> Whether to output particle removal info.
00277     logical, intent(in) :: record_removals
00278     !> Whether to output aerosol optical properties.
00279     logical, intent(in) :: record_optical
00280     !> Rank to write into file.
00281     integer, intent(in) :: write_rank
00282     !> Number of processes to write into file.
00283     integer, intent(in) :: write_n_proc
00284     !> UUID of the simulation.
00285     character(len=PMC_UUID_LEN), intent(in) :: uuid
00286     
00287     character(len=len(prefix)+100) :: filename
00288     integer :: ncid
00289 
00290 #ifdef PMC_USE_MPI
00291     if (write_n_proc > 1) then
00292        write(filename, '(a,a,i4.4,a,i4.4,a,i8.8,a)') trim(prefix), &
00293             '_', i_repeat, '_', (write_rank + 1), '_', index, '.nc'
00294     else
00295        write(filename, '(a,a,i4.4,a,i8.8,a)') trim(prefix), &
00296             '_', i_repeat, '_', index, '.nc'
00297     end if
00298 #else
00299     write(filename, '(a,a,i4.4,a,i8.8,a)') trim(prefix), &
00300          '_', i_repeat, '_', index, '.nc'
00301 #endif
00302     call pmc_nc_check_msg(nf90_create(filename, NF90_CLOBBER, ncid), &
00303          "opening " // trim(filename))
00304     
00305     !> \page output_format_general Output File Format: General Information
00306     !!
00307     !! The general information global NetCDF attributes are:
00308     !!   - \b title: always set to the string "PartMC output file"
00309     !!   - \b source: set to the string "PartMC version V.V.V" where V.V.V
00310     !!     is the PartMC version that created the file
00311     !!   - \b UUID: a string of the form F47AC10B-58CC-4372-A567-0E02B2C3D479
00312     !!     which is the same for all files generated by a single call of
00313     !!     PartMC.
00314     !!   - \b Conventions: set to the string "CF-1.4", indicating
00315     !!     compliance with the <a
00316     !!     href="http://cf-pcmdi.llnl.gov/documents/cf-conventions/1.4">CF
00317     !!     convention format</a>
00318     !!   - \b history: set to the string
00319     !!     "YYYY-MM-DDThh:mm:ss[+-]ZZ:zz created by PartMC" where the first
00320     !!     term is the file creation time in the
00321     !!     <a href="http://en.wikipedia.org/wiki/ISO_8601">ISO 8601
00322     !!     format</a>. For example, noon Pacific Standard Time (PST) on
00323     !!     February 1st, 2000 would be written 2000-02-01T12:00:00-08:00.
00324     !!     The date and time variables are:
00325     !!     - YYYY: four-digit year
00326     !!     - MM: two-digit month number
00327     !!     - DD: two-digit day within month
00328     !!     - T: literal "T" character
00329     !!     - hh: two-digit hour in 24-hour format
00330     !!     - mm: two-digit minute
00331     !!     - ss: two-digit second
00332     !!     - [+-]: a literal "+" or "-" character giving the time zone
00333     !!       offset sign
00334     !!     - ZZ: two-digit hours of the time zone offset from UTC
00335     !!     - zz: two-digit minutes of the time zone offset from UTC
00336     !!
00337     !! The general information NetCDF variables are:
00338     !!   - \b time (unit s): time elapsed since the simulation start time,
00339     !!     as specified in the \ref output_format_env_state section
00340     !!   - \b timestep (unit s): the current timestep size
00341     !!   - \b repeat: the repeat number of this simulation (starting from 1)
00342     !!   - \b timestep_index: an integer that is 1 on the first timestep, 2
00343     !!     on the second timestep, etc.
00344     !!   - \b process (MPI only): the process number (starting from 1)
00345     !!     that output this data file
00346     !!   - \b total_processes (MPI only): the total number of processes
00347     !!     involved in writing data (may be less than the total number of
00348     !!     processes that computed the data)
00349 
00350     call pmc_nc_check(nf90_put_att(ncid, NF90_GLOBAL, "title", &
00351          "PartMC output file"))
00352     call pmc_nc_check(nf90_enddef(ncid))
00353 
00354     call write_header_and_time(ncid, time, del_t, index, uuid)
00355     call pmc_nc_write_integer(ncid, i_repeat, "repeat", &
00356          description="repeat repeat number of this simulation " &
00357          // "(starting from 1)")
00358 #ifdef PMC_USE_MPI
00359     call pmc_nc_write_integer(ncid, write_rank + 1, "process", &
00360          description="the process number (starting from 1) " &
00361          // "that output this data file")
00362     call pmc_nc_write_integer(ncid, write_n_proc, "total_processes", &
00363          description="total number of processes")
00364 #endif
00365 
00366     call env_state_output_netcdf(env_state, ncid)
00367     call gas_data_output_netcdf(gas_data, ncid)
00368     call gas_state_output_netcdf(gas_state, ncid, gas_data)
00369     call aero_data_output_netcdf(aero_data, ncid)
00370     call aero_state_output_netcdf(aero_state, ncid, aero_data, &
00371          record_removals, record_optical)
00372 
00373     call pmc_nc_check(nf90_close(ncid))
00374     
00375   end subroutine output_state_to_file
00376 
00377 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
00378 
00379   !> Send the state for the "central" output method to the root process.
00380   subroutine send_output_state_central(aero_state, gas_state, env_state)
00381 
00382     !> Aerosol state.
00383     type(aero_state_t), intent(in) :: aero_state
00384     !> Gas state.
00385     type(gas_state_t), intent(in) :: gas_state
00386     !> Environment state.
00387     type(env_state_t), intent(in) :: env_state
00388 
00389 #ifdef PMC_USE_MPI
00390     integer :: buffer_size, max_buffer_size, position, ierr
00391     character, allocatable :: buffer(:)
00392 
00393     call assert(645797304, pmc_mpi_rank() /= 0)
00394 
00395     max_buffer_size = 0
00396     max_buffer_size = max_buffer_size &
00397          + pmc_mpi_pack_size_env_state(env_state)
00398     max_buffer_size = max_buffer_size &
00399          + pmc_mpi_pack_size_gas_state(gas_state)
00400     max_buffer_size = max_buffer_size &
00401          + pmc_mpi_pack_size_aero_state(aero_state)
00402     allocate(buffer(max_buffer_size))
00403     position = 0
00404     call pmc_mpi_pack_env_state(buffer, position, env_state)
00405     call pmc_mpi_pack_gas_state(buffer, position, gas_state)
00406     call pmc_mpi_pack_aero_state(buffer, position, aero_state)
00407     call assert(839343839, position <= max_buffer_size)
00408     buffer_size = position
00409     call mpi_send(buffer, buffer_size, MPI_CHARACTER, 0, &
00410          TAG_OUTPUT_STATE_CENTRAL, MPI_COMM_WORLD, ierr)
00411     call pmc_mpi_check_ierr(ierr)
00412     deallocate(buffer)
00413 #endif
00414 
00415   end subroutine send_output_state_central
00416 
00417 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
00418 
00419   !> Receive the state for the "central" output method on the root
00420   !> process.
00421   subroutine recv_output_state_central(prefix, aero_data, gas_data, index, &
00422        time, del_t, i_repeat, record_removals, record_optical, uuid, &
00423        remote_proc)
00424 
00425     !> Prefix of state file.
00426     character(len=*), intent(in) :: prefix
00427     !> Aerosol data.
00428     type(aero_data_t), intent(in) :: aero_data
00429     !> Gas data.
00430     type(gas_data_t), intent(in) :: gas_data
00431     !> Filename index.
00432     integer, intent(in) :: index
00433     !> Current time (s).
00434     real(kind=dp), intent(in) :: time
00435     !> Current timestep (s).
00436     real(kind=dp), intent(in) :: del_t
00437     !> Current repeat number.
00438     integer, intent(in) :: i_repeat
00439     !> Whether to output particle removal info.
00440     logical, intent(in) :: record_removals
00441     !> Whether to output aerosol_optical_properties.
00442     logical, intent(in) :: record_optical
00443     !> UUID of the simulation.
00444     character(len=PMC_UUID_LEN), intent(in) :: uuid
00445     !> Process number to receive from.
00446     integer, intent(in) :: remote_proc
00447 
00448 #ifdef PMC_USE_MPI
00449     type(env_state_t) :: env_state
00450     type(gas_state_t) :: gas_state
00451     type(aero_state_t) :: aero_state
00452     integer :: buffer_size, position, status(MPI_STATUS_SIZE)
00453     integer :: n_proc, ierr
00454     character, allocatable :: buffer(:)
00455 
00456     call assert(206980035, pmc_mpi_rank() == 0)
00457     call assert(291452117, remote_proc /= 0)
00458     n_proc = pmc_mpi_size()
00459 
00460     ! get buffer size
00461     call mpi_probe(remote_proc, TAG_OUTPUT_STATE_CENTRAL, MPI_COMM_WORLD, &
00462          status, ierr)
00463     call pmc_mpi_check_ierr(ierr)
00464     call mpi_get_count(status, MPI_CHARACTER, buffer_size, ierr)
00465 
00466     ! get message
00467     allocate(buffer(buffer_size))
00468     call mpi_recv(buffer, buffer_size, MPI_CHARACTER, remote_proc, &
00469          TAG_OUTPUT_STATE_CENTRAL, MPI_COMM_WORLD, status, ierr)
00470     call pmc_mpi_check_ierr(ierr)
00471 
00472     ! unpack message
00473     position = 0
00474     call env_state_allocate(env_state)
00475     call gas_state_allocate(gas_state)
00476     call aero_state_allocate(aero_state)
00477     call pmc_mpi_unpack_env_state(buffer, position, env_state)
00478     call pmc_mpi_unpack_gas_state(buffer, position, gas_state)
00479     call pmc_mpi_unpack_aero_state(buffer, position, aero_state)
00480     call assert(279581330, position == buffer_size)
00481     deallocate(buffer)
00482     
00483     call output_state_to_file(prefix, aero_data, aero_state, gas_data, &
00484          gas_state, env_state, index, time, del_t, i_repeat, &
00485          record_removals, record_optical, remote_proc, n_proc, uuid)
00486     
00487     call env_state_deallocate(env_state)
00488     call gas_state_deallocate(gas_state)
00489     call aero_state_deallocate(aero_state)
00490 #endif
00491     
00492   end subroutine recv_output_state_central
00493 
00494 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
00495 
00496   !> Read the current state.
00497   subroutine input_state(filename, index, time, del_t, i_repeat, uuid, &
00498        aero_data, aero_state, gas_data, gas_state, env_state)
00499 
00500     !> Prefix of state file.
00501     character(len=*), intent(in) :: filename
00502     !> Filename index.
00503     integer, intent(out) :: index
00504     !> Current time (s).
00505     real(kind=dp), intent(out) :: time
00506     !> Current timestep (s).
00507     real(kind=dp), intent(out) :: del_t
00508     !> Current repeat number.
00509     integer, intent(out) :: i_repeat
00510     !> UUID of the simulation.
00511     character(len=PMC_UUID_LEN), intent(out) :: uuid
00512     !> Aerosol data.
00513     type(aero_data_t), optional, intent(inout) :: aero_data
00514     !> Aerosol state.
00515     type(aero_state_t), optional, intent(inout) :: aero_state
00516     !> Gas data.
00517     type(gas_data_t), optional, intent(inout) :: gas_data
00518     !> Gas state.
00519     type(gas_state_t), optional, intent(inout) :: gas_state
00520     !> Environment state.
00521     type(env_state_t), optional, intent(inout) :: env_state
00522     
00523     integer :: ncid
00524 
00525     call assert_msg(819739354, pmc_mpi_rank() == 0, &
00526          "can only call from process 0")
00527 
00528     call pmc_nc_open_read(filename, ncid)
00529 
00530     call pmc_nc_check(nf90_get_att(ncid, NF90_GLOBAL, "UUID", uuid))
00531 
00532     call pmc_nc_read_real(ncid, time, "time")
00533     call pmc_nc_read_real(ncid, del_t, "timestep")
00534     call pmc_nc_read_integer(ncid, i_repeat, "repeat")
00535     call pmc_nc_read_integer(ncid, index, "timestep_index")
00536 
00537     if (present(aero_data)) then
00538        call aero_data_input_netcdf(aero_data, ncid)
00539        if (present(aero_state)) then
00540           call aero_state_input_netcdf(aero_state, ncid, aero_data)
00541        end if
00542     else
00543        call assert_msg(289621231, present(aero_state) .eqv. .false., &
00544             "cannot input aero_state without aero_data")
00545     end if
00546 
00547     if (present(gas_data)) then
00548        call gas_data_input_netcdf(gas_data, ncid)
00549        if (present(gas_state)) then
00550           call gas_state_input_netcdf(gas_state, ncid, gas_data)
00551        end if
00552     else
00553        call assert_msg(874298496, present(gas_state) .eqv. .false., &
00554             "cannot input gas_state without gas_data")
00555     end if
00556 
00557     if (present(env_state)) then
00558        call env_state_input_netcdf(env_state, ncid)
00559     end if
00560 
00561     call pmc_nc_close(ncid)
00562     
00563   end subroutine input_state
00564   
00565 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
00566 
00567   !> Find all NetCDF (.nc) filenames that match the given prefix.
00568   subroutine input_filename_list(prefix, filename_list)
00569 
00570     !> Filename prefix to search for.
00571     character(len=*), intent(in) :: prefix
00572     !> Filename list.
00573     character(len=*), intent(inout), allocatable :: filename_list(:)
00574 
00575     integer :: n_file, index, unit, ios
00576     character(len=len(prefix)+100) :: filename
00577     logical :: done
00578 
00579     call assert_msg(277193351, pmc_mpi_rank() == 0, &
00580          "can only call from process 0")
00581 
00582     index = 1
00583     done = .false.
00584     unit = get_unit()
00585     do while (.not. done)
00586        write(filename, '(a,a,i8.8,a)') trim(prefix), '_', index, '.nc'
00587        open(unit=unit, file=filename, status='old', iostat=ios)
00588        if (ios /= 0) then
00589           done = .true.
00590        else
00591           index = index + 1
00592           close(unit)
00593        end if
00594     end do
00595     call free_unit(unit)
00596 
00597     n_file = index - 1
00598     if (size(filename_list) /= n_file) then
00599        deallocate(filename_list)
00600        allocate(filename_list(n_file))
00601     end if
00602 
00603     do index = 1,n_file
00604        write(filename, '(a,a,i8.8,a)') trim(prefix), '_', index, '.nc'
00605        filename_list(index) = filename
00606     end do
00607 
00608   end subroutine input_filename_list
00609 
00610 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
00611 
00612   !> Write the current sectional data.
00613   subroutine output_sectional(prefix, bin_grid, aero_data, aero_binned, &
00614        gas_data, gas_state, env_state, index, time, del_t, uuid)
00615     
00616     !> Prefix of filename to write
00617     character(len=*), intent(in) :: prefix
00618     !> Bin grid.
00619     type(bin_grid_t), intent(in) :: bin_grid
00620     !> Aerosol data.
00621     type(aero_data_t), intent(in) :: aero_data
00622     !> Binned aerosol data.
00623     type(aero_binned_t), intent(in) :: aero_binned
00624     !> Gas data.
00625     type(gas_data_t), intent(in) :: gas_data
00626     !> Gas state.
00627     type(gas_state_t), intent(in) :: gas_state
00628     !> Environment state.
00629     type(env_state_t), intent(in) :: env_state
00630     !> Filename index.
00631     integer, intent(in) :: index
00632     !> Current time (s).
00633     real(kind=dp), intent(in) :: time
00634     !> Current output time-step (s).
00635     real(kind=dp), intent(in) :: del_t
00636     !> UUID of the simulation.
00637     character(len=PMC_UUID_LEN), intent(in) :: uuid
00638 
00639     integer :: ncid
00640     character(len=len(prefix)+100) :: filename
00641 
00642     write(filename, '(a,a,i8.8,a)') trim(prefix), &
00643          '_', index, '.nc'
00644     call pmc_nc_check_msg(nf90_create(filename, NF90_CLOBBER, ncid), &
00645          "opening " // trim(filename))
00646 
00647     ! write header attributes
00648     call pmc_nc_check(nf90_put_att(ncid, NF90_GLOBAL, "title", &
00649          "PartMC sectional output file"))
00650     call pmc_nc_check(nf90_enddef(ncid))
00651 
00652     call write_header_and_time(ncid, time, del_t, index, uuid)
00653 
00654     ! write data
00655     call env_state_output_netcdf(env_state, ncid)
00656     call gas_data_output_netcdf(gas_data, ncid)
00657     call gas_state_output_netcdf(gas_state, ncid, gas_data)
00658     call aero_data_output_netcdf(aero_data, ncid)
00659     call aero_binned_output_netcdf(aero_binned, ncid, bin_grid, &
00660          aero_data)
00661 
00662     call pmc_nc_check(nf90_close(ncid))
00663 
00664   end subroutine output_sectional
00665   
00666 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
00667 
00668   !> Input sectional data.
00669   subroutine input_sectional(filename, index, time, del_t, uuid, bin_grid, &
00670        aero_data, aero_binned, gas_data, gas_state, env_state)
00671 
00672     !> Filename to read.
00673     character(len=*), intent(in) :: filename
00674     !> Filename index.
00675     integer, intent(out) :: index
00676     !> Current time (s).
00677     real(kind=dp), intent(out) :: time
00678     !> Current output time-step (s).
00679     real(kind=dp), intent(out) :: del_t
00680     !> UUID of the simulation.
00681     character(len=PMC_UUID_LEN), intent(out) :: uuid
00682     !> Bin grid.
00683     type(bin_grid_t), optional, intent(inout) :: bin_grid
00684     !> Aerosol data.
00685     type(aero_data_t), optional, intent(inout) :: aero_data
00686     !> Binned aerosol data.
00687     type(aero_binned_t), optional, intent(inout) :: aero_binned
00688     !> Gas data.
00689     type(gas_data_t), optional, intent(inout) :: gas_data
00690     !> Gas state.
00691     type(gas_state_t), optional, intent(inout) :: gas_state
00692     !> Environment state.
00693     type(env_state_t), optional, intent(inout) :: env_state
00694 
00695     integer :: ncid
00696 
00697     call assert_msg(559676785, pmc_mpi_rank() == 0, &
00698          "can only call from process 0")
00699 
00700     call pmc_nc_open_read(filename, ncid)
00701 
00702     call pmc_nc_check(nf90_get_att(ncid, NF90_GLOBAL, "UUID", uuid))
00703 
00704     call pmc_nc_read_real(ncid, time, "time")
00705     call pmc_nc_read_real(ncid, del_t, "timestep")
00706     call pmc_nc_read_integer(ncid, index, "timestep_index")
00707 
00708     if (present(bin_grid)) then
00709        call bin_grid_input_netcdf(bin_grid, ncid)
00710     end if
00711     if (present(aero_data)) then
00712        call aero_data_input_netcdf(aero_data, ncid)
00713     end if
00714     if (present(aero_binned)) then
00715        call assert_msg(585353528, &
00716             present(bin_grid) .and. present(aero_data), &
00717             "cannot input aero_binned without bin_grid and aero_data")
00718        call aero_binned_input_netcdf(aero_binned, ncid, bin_grid, &
00719             aero_data)
00720     end if
00721 
00722     if (present(gas_data)) then
00723        call gas_data_input_netcdf(gas_data, ncid)
00724        if (present(gas_state)) then
00725           call gas_state_input_netcdf(gas_state, ncid, gas_data)
00726        end if
00727     else
00728        call assert_msg(214545112, present(gas_state) .eqv. .false., &
00729             "cannot input gas_state without gas_data")
00730     end if
00731 
00732     if (present(env_state)) then
00733        call env_state_input_netcdf(env_state, ncid)
00734     end if
00735 
00736     call pmc_nc_close(ncid)
00737 
00738   end subroutine input_sectional
00739 
00740 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
00741 
00742   !> Read the specification for an output type from a spec file and
00743   !> generate it.
00744   subroutine spec_file_read_output_type(file, output_type)
00745     
00746     !> Spec file.
00747     type(spec_file_t), intent(inout) :: file
00748     !> Kernel type.
00749     integer, intent(out) :: output_type
00750     
00751     character(len=SPEC_LINE_MAX_VAR_LEN) :: output_type_name
00752     
00753     !> \page input_format_output Input File Format: Output Type
00754     !!
00755     !! The output type is specified by the parameter:
00756     !!   - \b output_type (string): type of disk output --- must be
00757     !!     one of: \c central to write one file per process, but all
00758     !!     written by process 0; \c dist for every process to
00759     !!     write its own state file; or \c single to transfer all data
00760     !!     to process 0 and write a single unified output file
00761     !!
00762     !! See also:
00763     !!   - \ref spec_file_format --- the input file text format
00764 
00765     call spec_file_read_string(file, 'output_type', output_type_name)
00766     if (trim(output_type_name) == 'central') then
00767        output_type = OUTPUT_TYPE_CENTRAL
00768     elseif (trim(output_type_name) == 'dist') then
00769        output_type = OUTPUT_TYPE_DIST
00770     elseif (trim(output_type_name) == 'single') then
00771        output_type = OUTPUT_TYPE_SINGLE
00772     else
00773        call spec_file_die_msg(392313600, file, &
00774             "Unknown output type: " // trim(output_type_name))
00775     end if
00776 
00777   end subroutine spec_file_read_output_type
00778 
00779 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
00780   
00781 end module pmc_output