FV3 Bundle
diag_manifest.F90
Go to the documentation of this file.
1 !***********************************************************************
2 !* GNU Lesser General Public License
3 !*
4 !* This file is part of the GFDL Flexible Modeling System (FMS).
5 !*
6 !* FMS is free software: you can redistribute it and/or modify it under
7 !* the terms of the GNU Lesser General Public License as published by
8 !* the Free Software Foundation, either version 3 of the License, or (at
9 !* your option) any later version.
10 !*
11 !* FMS is distributed in the hope that it will be useful, but WITHOUT
12 !* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 !* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 !* for more details.
15 !*
16 !* You should have received a copy of the GNU Lesser General Public
17 !* License along with FMS. If not, see <http://www.gnu.org/licenses/>.
18 !***********************************************************************
19 
20 !> \author Seth Underwood <Seth.Underwood@noaa.gov>
21 !!
22 !! \brief diag_manifest_mod writes out a manifest file for each diagnostic output
23 !! file defined in the diag_table file.
24 !!
25 !! diag_manifest_mod writes a JSON formatted manifest file for each diagnostic
26 !! file defined in the diag_table file. The manifest file contains basic
27 !! information about each field. Although, this manifest file is for use in the
28 !! future Chaco release of the FMS Runtime Environment (FRE), others may find the
29 !! information in this file useful.
30 !!
31 !! Although some FMS components write diagnostic files separated by tiles
32 !! (Cubed-sphere atmosphere), and some models are run with multiple ensembles the
33 !! only one manifest file will be written for each. That is, although an
34 !! atmos_cubed_sphere component may write `atmos_month.tile[1-6].nc`, only one
35 !! manifest file `atmos_month.mfst` will be written. This was done as
36 !! diag_manager_mod does not allow a tile or ensemble to write out a different
37 !! set of diagnostics. All tiles, and ensemble members read the same diag_table
38 !! file.
40 
41  USE diag_data_mod, ONLY: files,& ! TYPE(file_type) --- diagnostic files
42  & output_fields,& ! TYPE(output_field_type) --- field in diagnostic file
43  & input_fields,& ! TYPE(input_field_type) --- field from diag_table
44  & prepend_date,& ! LOGICAL --- indicates if the date should be prepended to files
45  & diag_init_time ! TYPE(time_type) -- model time when diag_manager initialized
46  USE mpp_mod, ONLY: mpp_pe,&
47  & mpp_root_pe,&
48  & get_unit,& ! Get a good file unit value
49  & mpp_npes,& ! Get number of PEs in pelist
50  & mpp_gather
51  USE fms_mod, ONLY: error_mesg,&
52  & warning
54  USE time_manager_mod, ONLY: get_date
55 
56  IMPLICIT NONE
57 
58  !> \brief Assignment operator for TYPE(manifest_field_type)
59  !!
60  !! Allow the TYPE(manifest_field_type) to be assigned properly. In most cases,
61  !! this shouldn't be needed, but it is added here just in case some compiler
62  !! just doesn't want to do the correct thing.
63  INTERFACE ASSIGNMENT(=)
64  MODULE PROCEDURE manifest_field_type_assign
65  END INTERFACE ASSIGNMENT(=)
66 
67  !> \brief A type to hold the data required for the manifest file.
68  !!
69  !! The data collected in this type is directly from the other types used in
70  !! diag_manager, namely: output_fields and input_fields.
72  CHARACTER(len=128) :: output_name !< output field name in diagnostic file (from diag_table)
73  CHARACTER(len=128) :: module_name !< model module that has this field
74  CHARACTER(len=128) :: input_name !< field name in model land
75  CHARACTER(len=50) :: time_method !< string to hold the time redux method. If static, the .false.
76  INTEGER :: packing !< packing value
77  INTEGER :: ndim !< number of dimensions
78  END TYPE manifest_field_type
79 
80  !> \brief A type to hold all the fields by dimension size
81  !!
82  !! The fields in the manifest file are separated by the number of axis
83  !! dimensions (minus the time dimension). This type is to facilitate this
84  !! separation.
86  INTEGER :: num_1d = 0 !< Number of 1D fields in fields_1d
87  INTEGER :: num_2d = 0 !< Number of 2D fields in fields_2d
88  INTEGER :: num_3d = 0 !< Number of 3D fields in fields_3d
89  INTEGER :: num_4d = 0 !< Number of 4D fields in fields_4d
90  TYPE(manifest_field_type), DIMENSION(:), ALLOCATABLE :: fields_1d !< Array of 1D fields
91  TYPE(manifest_field_type), DIMENSION(:), ALLOCATABLE :: fields_2d !< Array of 2D fields
92  TYPE(manifest_field_type), DIMENSION(:), ALLOCATABLE :: fields_3d !< Array of 3D fields
93  TYPE(manifest_field_type), DIMENSION(:), ALLOCATABLE :: fields_4d !< Array of 4D fields
94  END TYPE manifest_fields_type
95 
96  PRIVATE
97  PUBLIC :: write_diag_manifest
98 
99 CONTAINS
100 
101  ! PUBLIC routines
102  !> \brief Public routine that will start the writing of the manifest file.
103  !!
104  !! This routine is written in such a way that only the root MPI process and the
105  !! master OpenMP thread will attempt to write the file.
106  SUBROUTINE write_diag_manifest(file)
107  INTEGER, INTENT(in) :: file
108 
109  INTEGER :: file_unit, ios !< Fortran file unit, and status of file open
110  INTEGER :: num_static, num_temporal !< Used to know if any fields are recorded
111  INTEGER :: year, month, day, hour, minute, second !< to hold data on current model time.
112  TYPE(manifest_fields_type) :: static_fields !< Type to hold all static fields
113  TYPE(manifest_fields_type) :: temporal_fields !< Type to hold all non-static fields
114  CHARACTER(len=128) :: manifilename !< Manifest file name
115  CHARACTER(len=32) :: filename_appendix !< to hold file name appendix from fms_io
116  CHARACTER(len=24) :: start_date !< String to hold init time of diag_manager
117 
118  ! Used to determine if the ensemble number. filename_appendix will contain an
119  ! the string ens_ if running with multiple ensembles. If running only one
120  ! ensemble, then filename_appendix will not contain that string.
121  CALL get_filename_appendix(filename_appendix)
122 
123  ! Get the file name. Do not need to worry about tiles or ensembles. Only
124  ! writing one manifest file per history file defined in diag_table.
125  manifilename = trim(files(file)%name)//".mfst"
126  ! prepend the file start date if prepend_date == .TRUE.
127  IF ( prepend_date ) THEN
128  call get_date(diag_init_time, year, month, day, hour, minute, second)
129  write (start_date, '(1I20.4, 2I2.2)') year, month, day
130 
131  manifilename = trim(adjustl(start_date))//'.'//trim(manifilename)
132  END IF
133 
134  ! Extract static and non-static fields data
135  static_fields = get_diagnostic_fields(file, static=.true.)
136  temporal_fields = get_diagnostic_fields(file, static=.false.)
137 
138  ! Get the number of fields to write to manifest file
139  ! Need to gather data from all PEs for the component/pelist
140  num_static = static_fields%num_1d + static_fields%num_2d + static_fields%num_3d + static_fields%num_4d
141  num_temporal = temporal_fields%num_1d + temporal_fields%num_2d + temporal_fields%num_3d + temporal_fields%num_4d
142 
143  ! This bulk of this routine should only be called by the rootPE, and only from
144  ! ens_01 If running a single ensemble, filename_appendix will not contain the
145  ! string ens_
146 
147 !$OMP MASTER
148  IF ( mpp_pe() .EQ. mpp_root_pe() .AND.&
149  & (index(filename_appendix,'ens_').EQ.0 .OR. index(filename_appendix,'ens_01').GT.0) ) THEN
150  ! Open the file for writing, but only if we have something to write
151  IF ( num_static + num_temporal .GT. 0 ) THEN
152  ! Get a free Fortran file unit number
153  file_unit = get_unit()
154  ! Not using mpp_open, as this routine forces to only write from the root
155  ! PE, and each root PE should have its own set of files to write.
156  OPEN(unit=file_unit, file=trim(manifilename), access='SEQUENTIAL', form='FORMATTED',&
157  & action='WRITE', position='REWIND', iostat=ios)
158  IF ( ios .NE. 0 ) THEN
159  CALL error_mesg('diag_manifest_mod::write_diag_manifest',&
160  & 'Unable to open file "'//trim(manifilename)//'". No manifest file will be created.',&
161  & warning)
162  ELSE
163  ! Open JSON
164  write(file_unit,'(A1)') '{'
165  ! Fill in other data
166  CALL write_manifest(file_unit, static_fields, static=.true.)
167  CALL write_manifest(file_unit, temporal_fields, static=.false.)
168  ! Close JSON
169  write(file_unit,'(A1)') '}'
170  !!WRITE(file_unit,'(A128,",",A128,",",A128,",",A50,",",i2,",",i2)') maniField%output_name, manifield%module_name,&
171  !! & maniField%input_name, maniField%time_method, maniField%packing, maniField%nDim
172  ! Close the file
173  CLOSE(file_unit)
174  END IF
175  END IF
176  END IF
177 !$OMP END MASTER
178  ! Free up memory used
179  CALL destroy_manifest_fields_type(static_fields)
180  CALL destroy_manifest_fields_type(temporal_fields)
181  END SUBROUTINE write_diag_manifest
182 
183  ! PRIVATE routines
184  !> \brief De-allocate arrays used in the manifest_fields_type
185  SUBROUTINE destroy_manifest_fields_type(manifest_fields)
186  TYPE(manifest_fields_type), INTENT(inout) :: manifest_fields
187 
188  ! Set all num_?d to 0
189  manifest_fields%num_1d = 0
190  manifest_fields%num_2d = 0
191  manifest_fields%num_3d = 0
192  manifest_fields%num_4d = 0
193  ! De-allocate the arrays
194  IF ( ALLOCATED(manifest_fields%fields_1d) ) DEALLOCATE(manifest_fields%fields_1d)
195  IF ( ALLOCATED(manifest_fields%fields_2d) ) DEALLOCATE(manifest_fields%fields_2d)
196  IF ( ALLOCATED(manifest_fields%fields_3d) ) DEALLOCATE(manifest_fields%fields_3d)
197  IF ( ALLOCATED(manifest_fields%fields_4d) ) DEALLOCATE(manifest_fields%fields_4d)
198  END SUBROUTINE destroy_manifest_fields_type
199 
200  !> \brief Allow ASSIGNMENT(=) operator to work on TYPE(manifest_field_type)
201  !!
202  !! Simply assign the type on the rhs to the type on the lhs of the `=`.
203  SUBROUTINE manifest_field_type_assign(lhs,rhs)
204  TYPE(manifest_field_type), INTENT(out) :: lhs !< lhs, target
205  TYPE(manifest_field_type), INTENT(in) :: rhs !< rhs, source
206 
207  lhs%output_name = rhs%output_name
208  lhs%module_name = rhs%module_name
209  lhs%input_name = rhs%input_name
210  lhs%time_method = rhs%time_method
211  lhs%packing = rhs%packing
212  lhs%nDim = rhs%nDim
213  END SUBROUTINE manifest_field_type_assign
214 
215  !> \brief Write the JSON format of the field object.
216  SUBROUTINE write_fields(unit, fields)
217  INTEGER, INTENT(in) :: unit !< File unit number. File should already be opened.
218  TYPE(manifest_field_type), DIMENSION(:), INTENT(in) :: fields !< Array of fields to write
219 
220  INTEGER :: i
221  CHARACTER(LEN=*), PARAMETER :: FMT_FLD = "(12X,'""',A,'""',': {')"
222  CHARACTER(LEN=*), PARAMETER :: FMT_MOF = "(16X,'""model_field"":','""',A,'"",')"
223  CHARACTER(LEN=*), PARAMETER :: FMT_MOD = "(16X,'""module"":','""',A,'"",')"
224  CHARACTER(LEN=*), PARAMETER :: FMT_PAK = "(16X,'""packing"":',I1,',')"
225  CHARACTER(LEN=*), PARAMETER :: FMT_TAV = "(16X,'""time_averaging"":','""',A,'""')"
226 
227  DO i=1, SIZE(fields)
228  WRITE (unit,fmt_fld) trim(fields(i)%output_name)
229  WRITE (unit,fmt_mof) trim(fields(i)%input_name)
230  WRITE (unit,fmt_mod) trim(fields(i)%module_name)
231  WRITE (unit,fmt_pak) fields(i)%packing
232  WRITE (unit,fmt_tav) trim(fields(i)%time_method)
233  IF ( i.EQ.SIZE(fields) ) THEN
234  WRITE (unit,'(12X,A1)') '}'
235  ELSE
236  WRITE (unit,'(12X,A2)') '},'
237  END IF
238  END DO
239  END SUBROUTINE write_fields
240 
241  !> \brief Write the JSON format of the static/temporal object.
242  SUBROUTINE write_manifest(unit, fields, static)
243  INTEGER, INTENT(in) :: unit !< File unit number. File should already be opened.
244  TYPE(manifest_fields_type), INTENT(in) :: fields !< All fields to be written to manifest file
245  LOGICAL, INTENT(in) :: static !< Indicate if the fields in the fields array
246  !! are static or non-static fields
247 
248  CHARACTER(len=*), PARAMETER :: FMT_DIM = "(8X,'""',A2,'""',': {')"
249  CHARACTER(len=*), PARAMETER :: FMT_STA = "(4X,'""',A6,'""',': {')"
250  CHARACTER(len=*), PARAMETER :: FMT_TEM = "(4X,'""',A8,'""',': {')"
251 
252  ! Static / Temporal
253  IF ( static ) THEN
254  WRITE (unit,fmt_sta) 'Static'
255  ELSE
256  WRITE (unit,fmt_tem) 'Temporal'
257  END IF
258 
259  ! 1D fields
260  WRITE (unit,fmt_dim) '1D'
261  CALL write_fields(unit, fields%fields_1d(1:fields%num_1d))
262  WRITE (unit,'(8X,A2)') '},'
263 
264  ! 2D fields
265  WRITE (unit,fmt_dim) '2D'
266  CALL write_fields(unit, fields%fields_2d(1:fields%num_2d))
267  WRITE (unit,'(8X,A2)') '},'
268 
269  ! 3D fields
270  WRITE (unit,fmt_dim) '3D'
271  CALL write_fields(unit, fields%fields_3d(1:fields%num_3d))
272  WRITE (unit,'(8X,A2)') '},'
273 
274  ! 4D fields
275  WRITE (unit,fmt_dim) '4D'
276  CALL write_fields(unit, fields%fields_4d(1:fields%num_4d))
277  WRITE (unit,'(8X,A1)') '}'
278 
279  ! Static / Temporal
280  IF ( static ) THEN
281  WRITE (unit,'(4X,A2)') '},'
282  ELSE
283  WRITE (unit,'(4X,A1)') '}'
284  END IF
285  END SUBROUTINE write_manifest
286 
287  !> \brief Extract the diagnostic fields, and collect the information about the
288  !! fields.
289  TYPE(manifest_fields_type) FUNCTION get_diagnostic_fields(file, static)
290  INTEGER, INTENT(in) :: file !< diagnostic file, as defined by diag_manager_mod
291  LOGICAL, INTENT(in) :: static !< Indicates if looking for static or non-static
292  !! fields. .TRUE. indicates looking only for
293  !! static files. .FALSE. indicates looking only
294  !! for non-static fields.
295 
296  INTEGER :: i, j, o
297  INTEGER :: istat
298  TYPE(manifest_field_type) :: manifield
299  CHARACTER(len=128) :: manifilename
300  LOGICAL, DIMENSION(:), ALLOCATABLE :: data_written !< Array to indicate if
301  !! field was written to file
302 
303  ! manifest file name
304  manifilename = trim(files(file)%name)//".mfst"
305 
306  ALLOCATE(data_written(mpp_npes()), stat=istat)
307  IF ( istat.NE.0 ) THEN
308  CALL error_mesg('diag_manifest_mod::get_diagnostic_fields',&
309  & 'Unable to allocate array to determine if field written to file. No manifest file will be created.',&
310  & warning)
311  ! Set all num_?d to 0, to verify they are set
312  get_diagnostic_fields%num_1d = 0
313  get_diagnostic_fields%num_2d = 0
314  get_diagnostic_fields%num_3d = 0
315  get_diagnostic_fields%num_4d = 0
316  ELSE
317  DO j=1, files(file)%num_fields
318  o = files(file)%fields(j) ! Position of this field in output_fields array
319  ! Determine if any PE has written file
320 
321  ! This is a hack for now. A future version will use a more elaborate
322  ! fix.
323  IF ( output_fields(o)%local_output ) THEN
324  ! Field is only written for specific regions. Need to mpp_gather to
325  ! know if written on any PE other than root_pe -- as only the root_pe
326  ! will write the manifest file
327  CALL mpp_gather((/output_fields(o)%written_once/), data_written)
328  ELSE
329  ! Assuming root_pe was involved in writing of the field --- if written
330  data_written = output_fields(o)%written_once
331  END IF
332 
333  IF ( any(data_written) .AND. (static.EQV.output_fields(o)%static) ) THEN
334  ! output field was written to file, and is static/non-static, whichever was requested
335  ! Gather the information to record it.
336  i = output_fields(o)%input_field ! Position of the input fields associated with this output_field
337 
338  ! this is information I currently know we want to save, and where it is:
339  manifield%output_name = output_fields(o)%output_name
340  manifield%module_name = input_fields(i)%module_name
341  manifield%input_name = input_fields(i)%field_name
342  IF ( output_fields(o)%static ) THEN
343  ! Static fields MUST have a time_method of .false.
344  manifield%time_method = ".false."
345  ELSE
346  manifield%time_method = output_fields(o)%time_method
347  END IF
348  manifield%packing = output_fields(o)%pack
349  manifield%nDim = output_fields(o)%num_axes
350 
351  ! Now that we have the information about the field, add to type based on dimensions of field
352  SELECT CASE (manifield%nDim)
353  CASE (1)
354  get_diagnostic_fields%num_1d = get_diagnostic_fields%num_1d + 1
355  IF ( .NOT.ALLOCATED(get_diagnostic_fields%fields_1d) ) THEN
356  ! Allocate to the max number of fields
357  ALLOCATE(get_diagnostic_fields%fields_1d(files(file)%num_fields), stat=istat)
358  IF ( istat.NE.0 ) THEN
359  CALL error_mesg('diag_manifest_mod::get_diagnostic_fields',&
360  & 'Unable to allocate 1d array for manifest file "'//trim(manifilename)//'". Manifest incomplete.',&
361  & warning)
362  ! Resetting count to 0 to keep from writing out
363  get_diagnostic_fields%num_1d = 0
364  cycle
365  END IF
366  END IF
367  IF ( ALLOCATED(get_diagnostic_fields%fields_1d) ) THEN
368  get_diagnostic_fields%fields_1d(get_diagnostic_fields%num_1d) = manifield
369  END IF
370  CASE (2)
371  get_diagnostic_fields%num_2d = get_diagnostic_fields%num_2d + 1
372  IF ( .NOT.ALLOCATED(get_diagnostic_fields%fields_2d) ) THEN
373  ! Allocate to the max number of fields
374  ALLOCATE(get_diagnostic_fields%fields_2d(files(file)%num_fields), stat=istat)
375  IF ( istat.NE.0 ) THEN
376  CALL error_mesg('diag_manifest_mod::get_diagnostic_fields',&
377  & 'Unable to allocate 2d array for manifest file "'//trim(manifilename)//'". Manifest incomplete.',&
378  & warning)
379  ! Resetting count to 0 to keep from writing out
380  get_diagnostic_fields%num_2d = 0
381  cycle
382  END IF
383  END IF
384  IF ( ALLOCATED(get_diagnostic_fields%fields_2d) ) THEN
385  get_diagnostic_fields%fields_2d(get_diagnostic_fields%num_2d) = manifield
386  END IF
387  CASE (3)
388  get_diagnostic_fields%num_3d = get_diagnostic_fields%num_3d + 1
389  IF ( .NOT.ALLOCATED(get_diagnostic_fields%fields_3d) ) THEN
390  ! Allocate to the max number of fields
391  ALLOCATE(get_diagnostic_fields%fields_3d(files(file)%num_fields), stat=istat)
392  IF ( istat.NE.0 ) THEN
393  CALL error_mesg('diag_manifest_mod::get_diagnostic_fields',&
394  & 'Unable to allocate 3d array for manifest file "'//trim(manifilename)//'". Manifest incomplete.',&
395  & warning)
396  ! Resetting count to 0 to keep from writing out
397  get_diagnostic_fields%num_3d = 0
398  cycle
399  END IF
400  END IF
401  IF ( ALLOCATED(get_diagnostic_fields%fields_3d) ) THEN
402  get_diagnostic_fields%fields_3d(get_diagnostic_fields%num_3d) = manifield
403  END IF
404  CASE (4)
405  get_diagnostic_fields%num_4d = get_diagnostic_fields%num_4d + 1
406  IF ( .NOT.ALLOCATED(get_diagnostic_fields%fields_4d) ) THEN
407  ! Allocate to the max number of fields
408  ALLOCATE(get_diagnostic_fields%fields_4d(files(file)%num_fields), stat=istat)
409  IF ( istat.NE.0 ) THEN
410  CALL error_mesg('diag_manifest_mod::get_diagnostic_fields',&
411  & 'Unable to allocate 4d array for manifest file "'//trim(manifilename)//'". Manifest incomplete.',&
412  & warning)
413  ! Resetting count to 0 to keep from writing out
414  get_diagnostic_fields%num_4d = 0
415  cycle
416  END IF
417  END IF
418  IF ( ALLOCATED(get_diagnostic_fields%fields_4d) ) THEN
419  get_diagnostic_fields%fields_4d(get_diagnostic_fields%num_4d) = manifield
420  END IF
421  END SELECT
422  END IF
423  END DO
424  END IF
425  ! Clean up allocated arrays
426  IF (ALLOCATED(data_written)) DEALLOCATE(data_written)
427  END FUNCTION get_diagnostic_fields
428 END MODULE diag_manifest_mod
Definition: fms.F90:20
subroutine, public get_date(time, year, month, day, hour, minute, second, tick, err_msg)
subroutine destroy_manifest_fields_type(manifest_fields)
De-allocate arrays used in the manifest_fields_type.
subroutine write_manifest(unit, fields, static)
Write the JSON format of the static/temporal object.
subroutine manifest_field_type_assign(lhs, rhs)
Allow ASSIGNMENT(=) operator to work on TYPE(manifest_field_type)
type(manifest_fields_type) function get_diagnostic_fields(file, static)
Extract the diagnostic fields, and collect the information about the fields.
A type to hold all the fields by dimension size.
subroutine write_fields(unit, fields)
Write the JSON format of the field object.
Definition: mpp.F90:39
type(file_type), dimension(:), allocatable, save files
Definition: diag_data.F90:780
subroutine, public write_diag_manifest(file)
Public routine that will start the writing of the manifest file.
A type to hold the data required for the manifest file.
subroutine, public get_filename_appendix(string_out)
Definition: fms_io.F90:8350
subroutine, public error_mesg(routine, message, level)
Definition: fms.F90:529
diag_manifest_mod writes out a manifest file for each diagnostic output file defined in the diag_tabl...