diff --git a/diag_manager/README.md b/diag_manager/README.md index ddf0dac681..c7b48af249 100644 --- a/diag_manager/README.md +++ b/diag_manager/README.md @@ -1,20 +1,161 @@ -The purpose of this document is to document the differences between the old diag manager and the new (modern) diag manager. +The purpose of this document is to document the differences between the old (legacy) diag manager and the new (modern) diag manager. + ## Contents -- [1. Diag Table Format](README.md#1-diag-table-format) -- [2. Scalar Axis](README.md#2-scalar-axis) -- [3. Average Time Variables](README.md#3-average-time-variables) -- [4. Subregional Files](README.md#4-subregional-files) -- [5. Global attributes](README.md#5-global-attributes) -- [6. Real attributes from diag_field_add_attribute calls](README.md#6-real-attributes-from-diag_field_add_attribute-calls) -- [7. History files data output "changes"](README.md#7-history-files-data-output-changes) - -### 1. Diag Table Format +- [1. Diag Manager Rewrite Overview](README.md#1-overview-of-the-diag-manager-rewrite) +- [2. Diag Table Format](README.md#2-diag-table-format) +- [3. Scalar Axis](README.md#3-scalar-axis) +- [4. Average Time Variables](README.md#4-average-time-variables) +- [5. Subregional Files](README.md#5-subregional-files) +- [6. Global attributes](README.md#6-global-attributes) +- [7. Real attributes from diag_field_add_attribute calls](README.md#7-real-attributes-from-diag_field_add_attribute-calls) +- [8. History files data output "changes"](README.md#8-history-files-data-output-changes) + +### 1. Overview of the Diag Manager Rewrite + +The diag manager was completely rewritten to support YAML-formatted diagnostic tables and provide improved performance and maintainability. The rewrite maintains backward compatibility with the legacy ASCII-format diag tables through build-time configuration, but is only able to be used if FMS is built with libyaml support. + +#### Module Organization and Naming Convention + +The diag_manager is organized into functional modules, with new modules introduced in the rewrite using the `fms_diag_` prefix convention: + +**Legacy Modules (original diag manager):** +- `diag_manager.F90` (top-level interface) +- `diag_axis.F90` +- `diag_data.F90` +- `diag_grid.F90` +- `diag_output.F90` +- `diag_table.F90` +- `diag_util.F90` +- `fms_diag_time_reduction.F90` +- `fms_diag_elem_weight_procs.F90` +- `fms_diag_fieldbuff_update.F90` + +**New Modules (modern diag manager):** +- `diag_manager.F90` (top-level interface) Same routines as the legacy, but when use_modern_diag is true routines will make calls to fms_diag_object.F90 and friends to perform all operations. +- `fms_diag_object.F90` - Core diagnostic object implementation +- `fms_diag_field_object.F90` - Field-specific diagnostic data structures +- `fms_diag_file_object.F90` - File I/O management +- `fms_diag_axis_object.F90` - Axis and domain handling +- `fms_diag_bbox.F90` - Bounding box operations for subregional output +- `fms_diag_output_buffer.F90` - Output buffering and data management +- `fms_diag_reduction_methods.F90` - Reduction method implementations +- `fms_diag_input_buffer.F90` - Input buffer management +- `fms_diag_yaml.F90` - YAML diagnostic table parsing and handling +- `fms_diag_time_utils.F90` - Time utility functions +- `diag_data.F90` - stores all namelist parameters from diag_manager_nml + +#### Enabling the Modern Diag Manager + +The modern diag manager is optionally enabled via the `use_modern_diag` flag in the `diag_manager_nml` namelist, as seen below. FMS must be compiled with the `-Duse_yaml flag`. +By default, the legacy diag manager is used to maintain backward compatibility. When `use_modern_diag = .true.`, the modern implementation is invoked while maintaining the same public interface. + +``` +&diag_manager_nml + use_modern_diag=.true. +/ +``` + +### 2. Diag Table Format The modern diag manager uses a YAML format instead of the legacy ascii table. A description of the YAML diag table can be found [here](diag_yaml_format.md). A formal specification, in the form of a JSON schema, can be found in the [gfdl_msd_schemas](https://github.com/NOAA-GFDL/gfdl_msd_schemas) repository on Github. -### 2. Scalar Axis +Options set in the file section will become the default for each variable in that file (ie. `kind: r8 below` will set all variables to use r8 kind), +unless otherwise specified. Ordering of the key-value pairs in the yaml file does not matter, as long as the appropriate sections have +matching indentation. + +This barebones example creates a single netcdf file (per tile, if using a tiled domain): +```{yaml} +title: simple_diag_table +base_date: 1 1 1 0 0 0 +diag_files: +- file_name: simple_diagnostics + freq: 225 seconds + time_units: seconds + module: atm_mod + kind: r8 + unlimdim: time + varlist: + - var_name: var1 + output_name: variable_one + reduction: average +``` + +This is a more complex example utilizing subregional output and wildcard filenames: +```{yaml} +title: test_diag_manager +base_date: 2 1 1 0 0 0 +diag_files: +- file_name: normal + freq: 24 days + time_units: hours + unlimdim: records + module: potato_mod + kind: r8 + reduction: min + varlist: + - module: atm_mod + var_name: sst + output_name: sst + reduction: average + kind: r4 + write_var: true + attributes: + - do_sst: .true. + sub_region: + - grid_type: latlon + corner1: -80, 0 + corner2: -80, 75 + corner3: -60, 0 + corner4: -60, 75 +- file_name: normal2 + freq: -1 + time_units: hours + unlimdim: records + write_file: true + module: atm_mod + reduction: none + kind: r4 + varlist: + - var_name: sstt + output_name: sstt + long_name: S S T + - var_name: sstt2 + output_name: sstt2 + long_name: S S T + write_var: false + sub_region: + - grid_type: index + tile: 1 + corner1: 10, 15 + corner2: 20, 15 + corner3: 10, 25 + corner4: 20, 25 +- file_name: normal3 + freq: -1 + time_units: hours + unlimdim: records + write_file: false +- file_name: wild_card_name%4yr%2mo%2dy%2hr + filename_time: end + freq: 6 hours + time_units: hours + unlimdim: time + new_file_freq: 6 hours + start_time: 2 1 1 0 0 0 + file_duration: 12 hours + module: ocn_mod + reduction: average + kind: r4 + varlist: + - var_name: sst + output_name: sst + global_meta: + - is_a_file: true +``` + +### 3. Scalar Axis The old diag manager was adding a `scalar_axis` dimension of size 1 for scalar variables ``` @@ -27,7 +168,7 @@ variables: ``` The new diag manager will no longer have a dummy scalar axis dimension. -### 3. Average Time Variables +### 4. Average Time Variables The old diag manager includes time bounds metadata in a non-standard convention (i.e. `average_T1`, `average_T2`, and `average_DT`) 1. `average_T1` is the start time for the averaging period (in the same time units as time) 2. `average_T2` is the end time for the averaging period @@ -69,7 +210,7 @@ This time_bounds variable is refernced as a variable attribute of time: time:bounds = "time_bnds" ; ``` -### 4. Subregional Files +### 5. Subregional Files #### A. `is_subregional` global attribute: Subregional files will have a global NetCDF attribute `is_subregional = True` set for non-global history files. This attribute will be used in PP tools. @@ -80,7 +221,7 @@ In some cases, the old diag manager was adding `sub0X` to the dimension names wh #### C. Corner and center diagnostics: In the old diag manager, if mixing variables that are corner variables, such as velocities={uo,vo,umo,vmo} and center variables, such as tracers={thetao,so,volcello} you sometimes ended up with a different number of variables per file. The extra files had duplicate data for the corner velocities because the two PEs shared the point at the edge. This happened with some grid/layouts/masks/subregion combinations and it caused problems with the combiner. The new diag manager will not have this problem. -### 5. Global attributes +### 6. Global attributes #### A. Grid type and grid tile: The old diag manager was adding the global attributes grid_type = "regular" and grid_tile = "N/A" for all files regardless of what the grid_type and the grid_title actually were. The new diag manager will no longer be doing this as they are not correct and don’t seem to be used. @@ -91,12 +232,12 @@ We were unable to reproduce the exact order of the associated_files global attri lake_area: 19790101.land_static.nc soil_area: 19790101.land_static.nc land_area: 19790101.land_static.nc <> land_area: 19790101.land_static.nc soil_area: 19790101.land_static.nc lake_area: 19790101.land_static.nc ``` -### 6. Real attributes from diag_field_add_attribute calls +### 7. Real attributes from diag_field_add_attribute calls When real attributes were added to the file via a diag_field_add_attribute call, the old diag manager is always saving it as NF90_FLOAT regardless of the precision the data was [passed in](https://github.com/NOAA-GFDL/FMS/blob/ebb32649efa395ea14598f74c8d49e74d1408579/diag_manager/diag_manager.F90#L4532-L4543) The new diag manager is going to write the attribute as it is passed in. This will cause differences when the model component was compiled with r8 as it will write the attribute as r8 instead of r4. -### 7. History files data output "changes" +### 8. History files data output "changes" When the model run time is less than then the output frequency (i.e if the module run time is 2 days and you are writing monthly diagnostics), the old diag manager was writing 9.96921e+36. The new diag manager is not going to write anything for this cases, so if you ncdump the output from the new diag manager, you will get: ``` diff --git a/diag_manager/diag_axis.F90 b/diag_manager/diag_axis.F90 index 4c123db8d0..8d3a9f81ef 100644 --- a/diag_manager/diag_axis.F90 +++ b/diag_manager/diag_axis.F90 @@ -24,6 +24,10 @@ !! !! Users first create axis ID by calling diag_axis_init, then use this axis ID in !! register_diag_field. +!! +!! @note This file is part of the legacy diag_manager. +!! If use_modern_diag is enabled, diag_axis_init will route all axis data storage through +!! the fms_diag_axis_object.F90 module instead. !> @addtogroup diag_axis_mod !> @{ diff --git a/diag_manager/diag_grid.F90 b/diag_manager/diag_grid.F90 index cf3e8ae5f7..0b0b363a05 100644 --- a/diag_manager/diag_grid.F90 +++ b/diag_manager/diag_grid.F90 @@ -22,6 +22,10 @@ !! !> @author Seth Underwood seth.underwood@noaa.gov !! +!! @note This module is part of the legacy diag_manager. If use_modern_diag is enabled, this module will be unused +!! except for the get_local_indexes procedure, which is still used by the modern diag_manager to determine the local +!! indexes for regional output in the cube sphere grid. +!! !! diag_grid_mod contains useful utilities for dealing !! with, mostly, regional output for grids other than the standard !! lat/lon grid. This module contains three public procedures @@ -38,22 +42,6 @@ MODULE diag_grid_mod use platform_mod - ! - ! - ! Multi-tile regional output in the cubed sphere. - ! - ! - ! Single grid in the tri-polar grid. - ! - ! - ! Multi-tile regional output in the tri-polar grid. - ! - ! - ! Regional output using array masking. This should allow - ! regional output to work on any current or future grid. - ! - ! - USE constants_mod, ONLY: DEG_TO_RAD, RAD_TO_DEG, RADIUS USE fms_mod, ONLY: write_version_number, error_mesg, WARNING, FATAL,& & mpp_pe diff --git a/diag_manager/diag_manager.F90 b/diag_manager/diag_manager.F90 index 40617ec19c..024daddb62 100644 --- a/diag_manager/diag_manager.F90 +++ b/diag_manager/diag_manager.F90 @@ -19,7 +19,25 @@ !> @ingroup diag_manager !! @brief diag_manager_mod is a set of simple calls for parallel diagnostics !! on distributed systems. It is geared toward the writing of data in netCDF -!! format. See @ref diag_manager for diag table information. +!! format. The diag_manager differs from the fms2_io module in that it reads in +!! a diag_table describing which fields are expected in the output file, and also +!! buffers the sent in data prior to writing. This allows users to send in data for a field multiple times for a +!! timestep and output the average,max/min, etc. based on the 'reduction' method specified in the table. +!! It also allows for setting custom frequencies for output and file creation, for example outputting every 2 hours +!! while creating a new file every 6 hours. +!! +!!

Diag Manager Implementation

+!! The diag_manager module provides a unified public interface that supports legacy (ASCII-based) and modern +!! (YAML-based) diagnostic table formats. The modern implementation was rewritten to improve performance, +!! maintainability, and support for YAML-formatted diag_tables. For more information, see the diag_manager/README.md +!! file and the documentation for the individual modern diag_manager modules. +!! +!!

Enabling the Modern Diag Manager

+!! The modern diag manager is enabled via the use_modern_diag flag in the diag_manager_nml +!! namelist. By default, the legacy diag manager is used to maintain backward compatibility. When +!! use_modern_diag = .true., the modern implementation is used while maintaining the same public interfaces. +!! FMS must be built with libyaml support via the -Duse_yaml flag to use the modern diag manager. +!! !! @author Matt Harrison, Giang Nong, Seth Underwood !! !! diag_manager_mod provides a convenient set of interfaces for @@ -35,7 +53,7 @@ !! Use of diag_manager includes the following steps: !!
    !!
  1. Create diag_table as described in the @ref diag_table_mod -!! documentation.
  2. +!! documentation or use YAML format as described in diag_yaml_format.md. !!
  3. Call @ref diag_manager_init to initialize !! diag_manager_mod.
  4. !!
  5. Call @ref register_diag_field to register the field to be @@ -144,66 +162,7 @@ MODULE diag_manager_mod use platform_mod - ! - ! - ! - ! - ! Set to .TRUE. to allow both time average and instantaneous fields in the same output file. - ! - ! - ! - ! - ! - ! - ! - ! - ! - ! - ! - ! - ! - ! - ! - ! - ! - ! - ! Let the diag_manager know if the missing value (if supplied) should be overridden to be the - ! CMOR standard value of -1.0e20. - ! - ! - ! If .TRUE., then the diag_manager will check for values outside the - ! valid range. This range is defined in - ! the model, and passed to the diag_manager_mod via the OPTIONAL variable range - ! in the register_diag_field - ! function. - ! - ! - ! If .TRUE. then diag_manager_mod will issue a FATAL error - ! if any values for the output field are - ! outside the given range. - ! - ! - ! Maximum number of user definable attributes per field. - ! - ! - ! Maximum number of user definable global attributes per file. - ! - ! - ! If .TRUE. then prepend the file start date to the output file. .TRUE. - ! is only supported if the - ! diag_manager_init routine is called with the optional time_init parameter. Note: - ! This was usually done by FRE after the - ! model run. - ! - ! - ! Will determine which value to use when checking a regional output if the region is the full axis or a sub-axis. - ! The values are defined as GLO_REG_VAL (-999) and GLO_REG_VAL_ALT - ! (-1) in diag_data_mod. - ! - ! - ! Set to true, diag_manager uses mpp_io. Default is fms2_io. - ! - ! + USE time_manager_mod, ONLY: set_time, set_date, get_time, time_type, OPERATOR(>=), OPERATOR(>),& & OPERATOR(<), OPERATOR(==), OPERATOR(/=), OPERATOR(/), OPERATOR(+), ASSIGNMENT(=), get_date, & @@ -278,7 +237,7 @@ MODULE diag_manager_mod type(time_type) :: Time_end - !> @brief Send data over to output fields. + !> @brief Sends data over to output fields. !! !> send_data is overloaded for fields having zero dimension !! (scalars) to 3 dimension. diag_field_id corresponds to the id @@ -350,7 +309,8 @@ MODULE diag_manager_mod MODULE PROCEDURE send_data_4d END INTERFACE - !> @brief Register a diagnostic field for a given module + !> @brief Register a diagnostic field for a given module, this is equivalent to creating a + !! variable in the output netcdf file. !> @ingroup diag_manager_mod INTERFACE register_diag_field MODULE PROCEDURE register_diag_field_scalar @@ -476,10 +436,10 @@ INTEGER FUNCTION register_diag_field_array(module_name, field_name, axes, init_t & long_name=long_name, units=units, missing_value=missing_value, range=range, mask_variant=mask_variant, & & standard_name=standard_name, verbose=verbose, do_not_log=do_not_log, err_msg=err_msg, & & interp_method=interp_method, tile_count=tile_count, area=area, volume=volume, realm=realm) - endif -end function register_diag_field_array + endif + end function register_diag_field_array - !> @brief Return field index for subsequent call to send_data. + !> @brief Return field index for subsequent call to send_data. !! @return field index for subsequent call to send_data. INTEGER FUNCTION register_static_field(module_name, field_name, axes, long_name, units,& & missing_value, range, mask_variant, standard_name, DYNAMIC, do_not_log, interp_method,& @@ -4581,7 +4541,7 @@ end subroutine diag_field_add_attribute_1d !! area/volume fields for the diagnostic field are defined in another module after !! the diag_field. SUBROUTINE diag_field_add_cell_measures(diag_field_id, area, volume) - INTEGER, INTENT(in) :: diag_field_id + INTEGER, INTENT(in) :: diag_field_id !< ID number for field to add attribute to, returned from register_diag_field INTEGER, INTENT(in), OPTIONAL :: area !< diag ids of area INTEGER, INTENT(in), OPTIONAL :: volume !< diag ids of volume diff --git a/diag_manager/diag_manager_nml.md b/diag_manager/diag_manager_nml.md new file mode 100644 index 0000000000..6d9c6de7bc --- /dev/null +++ b/diag_manager/diag_manager_nml.md @@ -0,0 +1,111 @@ + +# diag_manager_nml + +## Overview + +The `diag_manager_nml` namelist contains runtime configuration options for the diagnostic manager. + +Although the namelist is defined in `diag_manager_mod`, all variables belong to the `diag_data_mod` module. +## Variables + +### `append_pelist_name` +- Type: `LOGICAL` +- Default: `.FALSE.` +- Description: If true, appends the processor element list name to output filenames. Useful for distinguishing output files from different processor configurations. + +### `mix_snapshot_average_fields` +- Type: `LOGICAL` +- Default: `.FALSE.` +- Description: Controls whether snapshot (instantaneous) and time-averaged fields can coexist in the same output file. When false, snapshot and averaged fields must be separated into different files to avoid timestamp conflicts. This option is only applicable for the legacy diag manager. The modern diag manager does not allow mixing snapshot (instantaneous) and time-averaged fields. + +### `max_files` +- Type: `INTEGER` +- Default: `31` +- Description: Maximum number of output files that can be managed simultaneously by the diagnostic manager. This option is only applicable for the legacy diag manager. + +### `max_output_fields` +- Type: `INTEGER` +- Default: `300` +- Description: Maximum number of output fields that can be registered with the diagnostic manager. This option is only applicable for the legacy diag manager. + +### `max_input_fields` +- Type: `INTEGER` +- Default: `300` +- Description: Maximum number of input fields that can be processed by the diagnostic manager. This option is only applicable for the legacy diag manager. + +### `max_axes` +- Type: `INTEGER` +- Default: `60` +- Description: Maximum number of coordinate axes that can be defined for diagnostic fields. + +### `do_diag_field_log` +- Type: `LOGICAL` +- Default: `.FALSE.` +- Description: Enables logging of diagnostic field registration and data sending operations. Helpful for debugging diagnostic setup and data flow. +- Notes: Logfiles are written to `diag_field_log.out.`. The `field_log_separator` variable controls the separator character for log entries (default is `|`). + +### `write_bytes_in_file` +- Type: `LOGICAL` +- Default: `.FALSE.` +- Description: If true, writes the number of bytes written to each output file. Provides information about file sizes and I/O volume. This option is only applicable for the legacy diag manager. + +### `debug_diag_manager` +- Type: `LOGICAL` +- Default: `.FALSE.` +- Description: Enables debug mode for the diagnostic manager, providing additional diagnostic output and validation checks during execution. + +### `max_num_axis_sets` +- Type: `INTEGER` +- Default: `25` +- Description: Maximum number of axis sets that can be defined for organizing coordinate systems. This option is only applicable for the legacy diag manager. + +### `use_cmor` +- Type: `LOGICAL` +- Default: `.FALSE.` +- Description: Forces the use of CMOR (Climate Model Output Rewriter) standard missing values (`-1.0e20`) instead of user-specified missing values. Required for CMIP-compliant output. + +### `issue_oor_warnings` +- Type: `LOGICAL` +- Default: `.TRUE.` +- Description: Controls whether warnings are issued when diagnostic field values fall outside the valid range specified during field registration. + +### `oor_warnings_fatal` +- Type: `LOGICAL` +- Default: `.FALSE.` +- Description: Determines whether out-of-range warnings should be treated as fatal errors, causing the model to abort when invalid values are detected. + +### `max_field_attributes` +- Type: `INTEGER` +- Default: `4` +- Description: Maximum number of user-defined attributes that can be attached to each diagnostic field. + +### `max_file_attributes` +- Type: `INTEGER` +- Default: `2` +- Description: Maximum number of user-defined global attributes that can be attached to each output file. + +### `prepend_date` +- Type: `LOGICAL` +- Default: `.TRUE.` +- Description: Controls whether the file start date is prepended to output filenames. +- Notes: Requires that `diag_manager_init` be called with the `time_init` parameter. + +### `region_out_use_alt_value` +- Type: `LOGICAL` +- Default: `.TRUE.` +- Description: Determines which sentinel value to use when checking regional output boundaries. Uses `GLO_REG_VAL_ALT` (`-1`) when true, and `GLO_REG_VAL` (`-999`) when false. This option is only applicable for the legacy diag manager. The modern diag manager can only accept -999 as an option" + +### `use_mpp_io` +- Type: `LOGICAL` +- Default: `.FALSE.` +- Description: Selects the I/O backend: true uses `mpp_io`, false uses `fms2_io` (recommended). + +### `use_modern_diag` +- Type: `LOGICAL` +- Default: `.FALSE.` +- Description: Enables the modern diagnostic manager implementation with YAML-based diag tables. When false, uses the legacy ASCII-based diagnostic manager for backward compatibility. + +### `use_clock_average` +- Type: `LOGICAL` +- Default: `.FALSE.` +Description: Controls how averaging windows are defined. When true, averaging of variables is done based on the clock. For example, if doing daily averages and you start the simulation in day 1 hour 3, it will do the average between day 1 hour 3 to day 2 hour 0. The default behavior will do the average between day 1 hour 3 to day 2 hour 3. diff --git a/diag_manager/diag_output.F90 b/diag_manager/diag_output.F90 index 0336f86048..c458ec531e 100644 --- a/diag_manager/diag_output.F90 +++ b/diag_manager/diag_output.F90 @@ -20,7 +20,12 @@ !! @brief diag_output_mod is an integral part of !! diag_manager_mod. Its function is to write axis-meta-data, !! field-meta-data and field data. +!! !! @author Seth Underwood +!! +!! @note This file is part of the legacy diag_manager. +!! If use_modern_diag is enabled, this module will be unused, as all IO will be handled +!! by the fms_diag_object.F90 module and it's helpers. !> @addtogroup diag_output_mod !> @{ diff --git a/diag_manager/diag_table.F90 b/diag_manager/diag_table.F90 index 730102057f..971fed1c13 100644 --- a/diag_manager/diag_table.F90 +++ b/diag_manager/diag_table.F90 @@ -24,6 +24,9 @@ !! files. !! @author Seth Underwood !! +!! @note This module is part of the legacy diag_manager. If use_modern_diag is enabled, this module will be unused, +!! as all parsing and data storage will be handled by the fms_diag_object.F90 module and it's helpers. +!! !! diag_table_mod parses the diag_table file, and sets up the required arrays to hold the information !! needed for the diag_manager_mod to correctly write out the model history files. !! diff --git a/diag_manager/diag_util.F90 b/diag_manager/diag_util.F90 index 813db617f5..ec06536953 100644 --- a/diag_manager/diag_util.F90 +++ b/diag_manager/diag_util.F90 @@ -19,6 +19,8 @@ !> @ingroup diag_manager !! @brief Functions and subroutines necessary for the diag_manager_mod. !! @author Seth Underwood +!! @note This module is part of the legacy diag_manager. If use_modern_diag is enabled, this module will be unused, +!! as all parsing and data storage will be handled by the fms_diag_object.F90 module and it's helpers. MODULE diag_util_mod @@ -27,18 +29,6 @@ MODULE diag_util_mod use,intrinsic :: iso_c_binding, only: c_double,c_float,c_int64_t, & c_int32_t,c_int16_t,c_intptr_t - ! - ! Make an interface check_bounds_are_exact for the subroutines check_bounds_are_exact_static - ! and - ! check_bounds_are_exact_dynamic. - !
    -  !       INTERFACE check_bounds_are_exact
    -  !         MODULE PROCEDURE check_bounds_are_exact_static
    -  !         MODULE PROCEDURE check_bounds_are_exact_dynamic
    -  !       END INTERFACE check_bounds_are_exact
    -  !     
    - !
    - USE diag_data_mod, ONLY: output_fields, input_fields, files, do_diag_field_log, diag_log_unit,& & VERY_LARGE_AXIS_LENGTH, time_zero, VERY_LARGE_FILE_FREQ, END_OF_RUN, EVERY_TIME,& & DIAG_SECONDS, DIAG_MINUTES, DIAG_HOURS, DIAG_DAYS, DIAG_MONTHS, DIAG_YEARS, get_base_time,& diff --git a/diag_manager/fms_diag_axis_object.F90 b/diag_manager/fms_diag_axis_object.F90 index e13ae7551a..527a14abbf 100644 --- a/diag_manager/fms_diag_axis_object.F90 +++ b/diag_manager/fms_diag_axis_object.F90 @@ -18,8 +18,11 @@ !> @defgroup fms_diag_axis_object_mod fms_diag_axis_object_mod !> @ingroup diag_manager -!! @brief fms_diag_axis_object_mod stores the diag axis object, a diag domain -!! object, and a subaxis object. +!! @brief Modern object-oriented implementation of diagnostic axis management for the FMS diagnostic manager. +!! +!! This module provides type-based classes for managing diagnostic axes in FMS. +!! It serves as the modern, object-oriented replacement for @ref diag_axis_mod, utilizing Fortran 2003+ class structures +!! and polymorphism. !> @file !> @brief File for @ref diag_axis_object_mod @@ -56,10 +59,16 @@ module fms_diag_axis_object_mod !> @} - !> @brief Type to hold the domain info for an axis - !! This type was created to avoid having to send in "Domain", "Domain2", "DomainUG" as arguments into subroutines - !! and instead only 1 class(diagDomain_t) argument can be send - !> @ingroup diag_axis_object_mod + !> @brief Base type for domain information associated with an axis. + !! + !! This type was created to avoid requiring separate "Domain", "Domain2", and "DomainUG" arguments + !! in subroutines. Instead, a single polymorphic class(diagDomain_t) argument can be used, which is + !! polymorphically extended to handle different domain types. + !! + !! This base provides a unified interface for domain operations regardless of whether + !! the axis uses 1D domain decomposition, 2D domain decomposition, or an unstructured grid domain. + !! + !! @ingroup diag_axis_object_mod type diagDomain_t contains procedure :: set => set_axis_domain @@ -67,25 +76,51 @@ module fms_diag_axis_object_mod procedure :: get_ntiles end type diagDomain_t - !> @brief Type to hold the 1d domain + !> @brief Type to hold 1D domain decomposition information for an axis. + !! + !! This type extends the diagDomain_t base type and is used when an axis is + !! associated with a 1D domain (typically a vertical or time axis). + !! The 1D domain provides information about how the axis is partitioned across + !! MPI processes along a single dimension. type, extends(diagDomain_t) :: diagDomain1d_t - type(domain1d) :: Domain !< 1d Domain of the axis + type(domain1d) :: Domain !< 1D domain object describing axis decomposition end type - !> @brief Type to hold the 2d domain + !> @brief Type to hold 2D domain decomposition information for an axis. + !! + !! This type extends the diagDomain_t base type and is used when an axis is + !! part of a 2D domain (typically for horizontal "X" or "Y" axes in atmospheric models). + !! The 2D domain provides information about how the axis is partitioned across + !! MPI processes in both the X and Y dimensions. type, extends(diagDomain_t) :: diagDomain2d_t - type(domain2d) :: Domain2 !< 2d Domain of an "X" or "Y" axis + type(domain2d) :: Domain2 !< 2D domain object describing X-Y decomposition of an axis end type - !> @brief Type to hold the unstructured domain + !> @brief Type to hold unstructured grid domain information for an axis. + !! + !! This type extends the diagDomain_t base type and is used when an axis is + !! associated with an unstructured (irregular) grid domain. Unstructured grids are commonly + !! used in models with non-uniform spatial decomposition, such as icosahedral grids. + !! The unstructured domain provides information about cell connectivity and partitioning. type, extends(diagDomain_t) :: diagDomainUg_t - type(domainUG) :: DomainUG !< Domain of "U" axis + type(domainUG) :: DomainUG !< Unstructured domain object for irregular mesh decomposition end type - !> @brief Type to hold the diagnostic axis description. - !> @ingroup diag_axis_object_mod + !> @brief Base type for diagnostic axis objects. + !! + !! This is the base type for all diagnostic axis implementations. It provides + !! a unified interface for axis operations and is polymorphically extended by more specific + !! axis types: + !! - @ref fmsDiagFullAxis_type - A complete axis with coordinates + !! - @ref fmsDiagSubAxis_type - A subset of a full axis for regional output + !! - @ref fmsDiagDiurnalAxis_type - A subset of a full axis for diurnal averaging + !! + !! The base type defines a common interface for querying axis properties and writing + !! axis data to output files via the FMS2_IO library. + !! + !! @ingroup diag_axis_object_mod TYPE :: fmsDiagAxis_type - INTEGER , private :: axis_id !< ID of the axis + INTEGER , private :: axis_id !< Unique identifier for this axis contains procedure :: get_parent_axis_id @@ -100,24 +135,37 @@ module fms_diag_axis_object_mod procedure :: get_edges_id END TYPE fmsDiagAxis_type - !> @brief Type to hold the diag_axis (either subaxis or a full axis) - !> @ingroup diag_axis_object_mod + !> @brief Container type to hold a polymorphic diagnostic axis object. + !! + !! This container type allows storage of any type derived from fmsDiagAxis_type + !! (e.g., fmsDiagFullAxis_type, fmsDiagSubAxis_type, or fmsDiagDiurnalAxis_type) + !! in an array. This polymorphic storage approach enables dynamic type checking + !! and method dispatch at runtime. + !! + !! @ingroup diag_axis_object_mod type :: fmsDiagAxisContainer_type - class(fmsDiagAxis_type), allocatable :: axis + class(fmsDiagAxis_type), allocatable :: axis !< Polymorphic axis object (Full, Sub, or Diurnal) end type - !> @brief Type to hold the subaxis - !> @ingroup diag_axis_object_mod + !> @brief Type representing a subregion or subset of a parent diagnostic axis. + !! + !! A subaxis is created when a user requests output for a limited region of a full axis. + !! This can occur for regional output (e.g., a subregion of the full domain) or for + !! dimension compression (e.g., selected depth levels in a vertical axis). + !! + !! Each subaxis maintains references to its parent axis and stores the index ranges + !! that define the subregion on the current PE, as well as globally. + !! Subaxes may also store Z-axis bounds for identifying equivalent subaxes across files. + !! + !! @ingroup diag_axis_object_mod TYPE, extends(fmsDiagAxis_type) :: fmsDiagSubAxis_type - CHARACTER(len=:), ALLOCATABLE , private :: subaxis_name !< Name of the subaxis - INTEGER , private :: starting_index !< Starting index of the subaxis relative to the - !! parent axis - INTEGER , private :: ending_index !< Ending index of the subaxis relative to the - !! parent axis - INTEGER , private :: parent_axis_id !< Id of the parent_axis - INTEGER , private :: compute_idx(2) !< Starting and ending index of the compute domain - INTEGER, allocatable, private :: global_idx(:) !< Starting and ending index of the global domain - real(kind=r4_kind), allocatable, private :: zbounds(:) !< Bounds of the Z axis + CHARACTER(len=:), ALLOCATABLE , private :: subaxis_name !< Name of the subaxis (typically parent_name_subNN) + INTEGER , private :: starting_index !< First index of subregion relative to parent axis + INTEGER , private :: ending_index !< Last index of subregion relative to parent axis + INTEGER , private :: parent_axis_id !< Axis ID of the parent full axis + INTEGER , private :: compute_idx(2) !< [start, end] indices of compute domain on this PE + INTEGER, allocatable, private :: global_idx(:) !< [start, end] indices in global domain + real(kind=r4_kind), allocatable, private :: zbounds(:) !< Bounds [min, max] if this is a Z-axis subregion contains procedure :: fill_subaxis procedure :: axis_length @@ -127,54 +175,71 @@ module fms_diag_axis_object_mod procedure :: is_same_zbounds END TYPE fmsDiagSubAxis_type - !> @brief Type to hold the diurnal axis - !> @ingroup diag_axis_object_mod + !> @brief Type for diurnal (daily cycle) sampling axes. + !! + !! This specialized axis type represents time-of-day sampling for diurnal averaging. + !! Diurnal axes divide a 24-hour period into regular intervals for accumulating + !! time-averaged or instantaneous samples at each time-of-day bin. This is commonly + !! used in climate modeling to analyze the diurnal cycle of variables. + !! + !! Each diurnal axis has an associated edges axis that defines the bin boundaries, + !! and stores the actual diurnal coordinate data for output to NetCDF files. + !! + !! @ingroup diag_axis_object_mod TYPE, extends(fmsDiagAxis_type) :: fmsDiagDiurnalAxis_type - INTEGER , private :: ndiurnal_samples !< The number of diurnal samples - CHARACTER(len=:), ALLOCATABLE, private :: axis_name !< The diurnal axis name - CHARACTER(len=:), ALLOCATABLE, private :: long_name !< The longname of the diurnal axis - CHARACTER(len=:), ALLOCATABLE, private :: units !< The units - INTEGER , private :: edges_id !< The id of the diurnal edges - CHARACTER(len=:), ALLOCATABLE, private :: edges_name !< The name of the edges axis - CLASS(*), ALLOCATABLE, private :: diurnal_data(:) !< The diurnal data + INTEGER , private :: ndiurnal_samples !< Number of time-of-day samples in 24-hour period + CHARACTER(len=:), ALLOCATABLE, private :: axis_name !< Name of the diurnal axis (e.g., time_of_day_06) + CHARACTER(len=:), ALLOCATABLE, private :: long_name !< Long name for the diurnal axis + CHARACTER(len=:), ALLOCATABLE, private :: units !< Units string (hours since reference time) + INTEGER , private :: edges_id !< Axis ID of the diurnal edges axis + CHARACTER(len=:), ALLOCATABLE, private :: edges_name !< Name of the edges axis (e.g., time_of_day_edges_06) + CLASS(*), ALLOCATABLE, private :: diurnal_data(:) !< Coordinate values: times within 24-hour day contains procedure :: get_diurnal_axis_samples procedure :: write_diurnal_metadata END TYPE fmsDiagDiurnalAxis_type - !> @brief Type to hold the diagnostic axis description. - !> @ingroup diag_axis_object_mod + !> @brief Type representing a complete diagnostic axis with coordinates and metadata. + !! + !! This is the primary type for storing axis information. It contains the coordinate + !! values, metadata (name, units, long name), domain information, and optional attributes. + !! A full axis can have subaxes defined from it for regional output or selective output. + !! + !! The axis stores data in either single or double precision floating point format, + !! as determined by the input coordinate array. Domain information is stored polymorphically + !! to support 1D, 2D, and unstructured grid domains. + !! + !! @ingroup diag_axis_object_mod TYPE, extends(fmsDiagAxis_type) :: fmsDiagFullAxis_type - CHARACTER(len=:), ALLOCATABLE, private :: axis_name !< Name of the axis - CHARACTER(len=:), ALLOCATABLE, private :: units !< Units of the axis - CHARACTER(len=:), ALLOCATABLE, private :: long_name !< Long_name attribute of the axis - CHARACTER(len=1) , private :: cart_name !< Cartesian name "X", "Y", "Z", "T", "U", "N" - CLASS(*), ALLOCATABLE, private :: axis_data(:) !< Data of the axis - CHARACTER(len=:), ALLOCATABLE, private :: type_of_data !< The type of the axis_data ("float" or "double") - !< TO DO this can be a dlinked to avoid having limits - integer, ALLOCATABLE, private :: subaxis(:) !< Array of subaxis - integer , private :: nsubaxis !< Number of subaxis - class(diagDomain_t),ALLOCATABLE, private :: axis_domain !< Domain - INTEGER , private :: type_of_domain !< The type of domain ("NO_DOMAIN", "TWO_D_DOMAIN", - !! or "UG_DOMAIN") - INTEGER , private :: length !< Global axis length - INTEGER , private :: direction !< Direction of the axis 0, 1, -1 - INTEGER, ALLOCATABLE, private :: edges_id !< Axis ID for the edges axis - !! This axis will be written to the file - CHARACTER(len=:), ALLOCATABLE, private :: edges_name !< Name for the previously defined "edges axis" - !! This will be written as an attribute - CHARACTER(len=:), ALLOCATABLE, private :: aux !< Auxiliary name, can only be geolon_t - !! or geolat_t - CHARACTER(len=128) , private :: req !< Required field names. - INTEGER , private :: tile_count !< The number of tiles - TYPE(fmsDiagAttribute_type),allocatable , private :: attributes(:) !< Array to hold user definable attributes - INTEGER , private :: num_attributes !< Number of defined attibutes - INTEGER , private :: domain_position !< The position in the doman (NORTH, EAST or CENTER) - integer, allocatable , private :: structured_ids(:) !< If the axis is in the unstructured grid, - !! this is the axis ids of the structured axis - CHARACTER(len=:), ALLOCATABLE, private :: set_name !< Name of the axis set. This is to distinguish - !! two axis with the same name + CHARACTER(len=:), ALLOCATABLE, private :: axis_name !< Name identifier for the axis (e.g., "height", "time") + CHARACTER(len=:), ALLOCATABLE, private :: units !< Units string for axis values (e.g., "m", "K") + CHARACTER(len=:), ALLOCATABLE, private :: long_name !< Descriptive long name for the axis + !! (e.g., "Height above sea level") + !! written as "long_name" attribute in output file + CHARACTER(len=1) , private :: cart_name !< Cartesian classification: "X", "Y", "Z", "T", "U", "N" + CLASS(*), ALLOCATABLE, private :: axis_data(:) !< Coordinate values as single or double precision + CHARACTER(len=:), ALLOCATABLE, private :: type_of_data !< Data type: "float" (r4) or "double" (r8) + integer, ALLOCATABLE, private :: subaxis(:) !< Array of axis IDs for subaxes derived from this axis + integer , private :: nsubaxis !< Count of subaxes currently defined + class(diagDomain_t),ALLOCATABLE, private :: axis_domain !< Domain decomposition info (1D, 2D, or UG) + INTEGER , private :: type_of_domain !< Domain type: NO_DOMAIN, TWO_D_DOMAIN, or UG_DOMAIN + INTEGER , private :: length !< Total number of coordinate points globally + INTEGER , private :: direction !< Axis direction: -1 (down), 0 (none), +1 (up) + INTEGER, ALLOCATABLE, private :: edges_id !< Axis ID of edges (cell boundaries) if defined + !! Written as coordinate in output file + CHARACTER(len=:), ALLOCATABLE, private :: edges_name !< Name of the edges axis + !! Written as "edges" attribute in file + CHARACTER(len=:), ALLOCATABLE, private :: aux !< Auxiliary classification: "geolon_t" or "geolat_t" + CHARACTER(len=128) , private :: req !< Required field names (comma-separated list) + INTEGER , private :: tile_count !< Number of tiles on this axis + TYPE(fmsDiagAttribute_type),allocatable , private :: attributes(:) !< User-defined custom attributes + INTEGER , private :: num_attributes !< Number of custom attributes defined + INTEGER , private :: domain_position !< Stagger position: CENTER, NORTH, or EAST + integer, allocatable , private :: structured_ids(:) !< Structured axis IDs for unstructured grids + !! (maps unstructured to lat/lon axes) + CHARACTER(len=:), ALLOCATABLE, private :: set_name !< Axis set name to distinguish axes with same name + !! Used for multiple configurations of same grid contains @@ -192,16 +257,20 @@ module fms_diag_axis_object_mod PROCEDURE :: has_set_name PROCEDURE :: is_x_or_y_axis PROCEDURE :: get_dim_size_layout - ! TO DO: - ! Get/has/is subroutines as needed END TYPE fmsDiagFullAxis_type !> @addtogroup fms_diag_yaml_mod !> @{ contains - !!!!!!!!!!!!!!!!! DIAG AXIS PROCEDURES !!!!!!!!!!!!!!!!! - !> @brief Initialize the axis + !> @brief Initialize and register a diagnostic axis with its coordinates and metadata. + !! + !! This subroutine configures a full diagnostic axis object with coordinate data, + !! units, and optional domain decomposition. The axis must be initialized before + !! being used to register diagnostic fields or create subaxes. + !! + !! The coordinate data is stored as-is (single or double precision), and the type + !! is inferred from the input array to optimize storage and I/O. subroutine register_diag_axis_obj(this, axis_name, axis_data, units, cart_name, long_name, direction,& & set_name, Domain, Domain2, DomainU, aux, req, tile_count, domain_position, axis_length ) class(fmsDiagFullAxis_type),INTENT(inout):: this !< Diag_axis obj @@ -290,7 +359,10 @@ subroutine register_diag_axis_obj(this, axis_name, axis_data, units, cart_name, this%num_attributes = 0 end subroutine register_diag_axis_obj - !> @brief Add an attribute to an axis + !> @brief Add a user-defined attribute to this axis. + !! + !! User-defined attributes are additional metadata that can be attached to axes. + !! These attributes will be written to the NetCDF output file as metadata. subroutine add_axis_attribute(this, att_name, att_value) class(fmsDiagFullAxis_type),INTENT(INOUT) :: this !< diag_axis obj character(len=*), intent(in) :: att_name !< Name of the attribute @@ -307,7 +379,15 @@ subroutine add_axis_attribute(this, att_name, att_value) call this%attributes(j)%add(att_name, att_value) end subroutine add_axis_attribute - !> @brief Write the axis meta data to an open fileobj + !> @brief Write axis metadata (dimensions, coordinates, and attributes) to a NetCDF file. + !! + !! This subroutine registers the axis dimension and variable in an open FMS2_IO + !! file object (FmsNetcdfFile_t, FmsNetcdfDomainFile_t, FmsNetcdfUnstructuredDomainFile_t), along with all metadata + !! attributes (units, long_name, etc.). + !! It handles different axis types (full, sub, diurnal) and domain decomposition types. + !! + !! For subaxes, the parent axis information is used to determine coordinate and + !! attribute values. The domain decomposition attribute is added when applicable. subroutine write_axis_metadata(this, fms2io_fileobj, edges_in_file, parent_axis) class(fmsDiagAxis_type), target, INTENT(IN) :: this !< diag_axis obj class(FmsNetcdfFile_t), INTENT(INOUT) :: fms2io_fileobj!< Fms2_io fileobj to write the data to @@ -438,7 +518,14 @@ subroutine write_axis_metadata(this, fms2io_fileobj, edges_in_file, parent_axis) end subroutine write_axis_metadata - !> @brief Write the axis data to an open fms2io_fileobj + !> @brief Write axis coordinate data to a NetCDF file. + !! + !! This subroutine writes the actual coordinate values for an axis to the output file. + !! It handles domain decomposition automatically, writing only the portion of coordinates + !! relevant to the current MPI process's I/O domain. + !! + !! For subaxes, the parent axis data is sliced to the appropriate indices and written. + !! For diurnal axes, the time-of-day coordinate values are written. subroutine write_axis_data(this, fms2io_fileobj, parent_axis) class(fmsDiagAxis_type), target, INTENT(IN) :: this !< diag_axis obj class(FmsNetcdfFile_t), INTENT(INOUT) :: fms2io_fileobj!< Fms2_io fileobj to write the data to @@ -466,8 +553,23 @@ subroutine write_axis_data(this, fms2io_fileobj, parent_axis) end select end subroutine write_axis_data - - !> @brief Defined a new diurnal axis + !> @brief Create and registers extra axes used when performing diurnal averaging, to capture the midpoints + !! and bounds of each diurnal sample. This subroutine will be called twice for each diurnal reduction: once to + !! create the edges axis (time_of_day_edges_) and once to create the center axis (time_of_day_), N being the + !! number of diurnal samples. The number of diurnal samples is specified by the reduction method name in your + !! diag_table.yaml, ie. "diurnal3" for 3 diurnal samples, "diurnal24" for 24 diurnal samples, etc. + !! + !! The time_of_day_ axis will have the midpoint of the sampled segment and the time_of_day_edges_ will have + !! the bounds. This will be written out in hours of a day, so edges will always start at 0 and end at 24, and + !! minutes will be represented as decimals. + !! + !! For example, if n_diurnal_samples = 3, the time_of_day_03 axis will have the values [4, 12, 20], representing the + !! time at the midpoint (4:00 am, 12:00 pm, 8:00 pm) for each of the 3 samples and the time_of_day_edges_03 axis + !! will have the values [0, 8, 16, 24], representing the start/end times of each sample (12:00 am, 8:00 am, 4:00 pm, + !! 12:00 pm). + !! For hourly sampling (n_diurnal_samples = 24), the time_of_day_24 axis will have the values + !! [0.5, 1.5, 2.5, ..., 23.5] and the time_of_day_edges_24 axis will have the values + !! [0, 1, 2, ..., 23, 24]. subroutine define_diurnal_axis(diag_axis, naxis, n_diurnal_samples, is_edges) class(fmsDiagAxisContainer_type), target, intent(inout) :: diag_axis(:) !< Array of axis containers integer, intent(inout) :: naxis !< Number of axis that have @@ -856,8 +958,17 @@ subroutine get_compute_domain(this, compute_idx, need_to_define_axis, tile_numbe end subroutine get_compute_domain - !!!!!!!!!!!!!!!!!! SUB AXIS PROCEDURES !!!!!!!!!!!!!!!!! - !> @brief Fills in the information needed to define a subaxis + !!!!!!!!!!!!!!!!! SUBAXIS PROCEDURES !!!!!!!!!!!!!!!!! + + !> @brief Initialize a subaxis object with region boundaries and metadata. + !! + !! This subroutine populates a subaxis object with all necessary information about + !! the subregion it represents. The subaxis name is automatically generated from the + !! parent axis name and a sequential number (e.g., "temperature_sub01"). + !! + !! Optional parameters allow storage of global domain indices (needed for the + !! domain_decomposition NetCDF attribute) and Z-axis bounds (for identifying + !! equivalent subaxes across output files). subroutine fill_subaxis(this, starting_index, ending_index, axis_id, parent_id, parent_axis_name, compute_idx, & global_idx, zbounds, nz_subaxis) class(fmsDiagSubAxis_type) , INTENT(INOUT) :: this !< diag_sub_axis obj @@ -901,8 +1012,10 @@ subroutine fill_subaxis(this, starting_index, ending_index, axis_id, parent_id, endif end subroutine fill_subaxis - !> @brief Get the axis length of a subaxis - !> @return the axis length + !> @brief Get the number of points in a subaxis. + !! + !! Calculates the number of coordinate points in this subaxis based on its starting + !! and ending indices. function axis_length(this) & result(res) class(fmsDiagSubAxis_type) , INTENT(IN) :: this !< diag_sub_axis obj @@ -911,8 +1024,9 @@ function axis_length(this) & res = this%ending_index - this%starting_index + 1 end function - !> @brief Accesses its member starting_index - !! @return a copy of the starting_index + !> @brief Get the starting index of this subaxis. + !! + !! Returns the first index of the subregion on the current process. function get_starting_index(this) result(indx) class(fmsDiagSubAxis_type), intent(in) :: this !< diag_sub_axis object integer :: indx !< Result to return @@ -964,7 +1078,7 @@ end function get_ntiles function get_length(this, cart_axis, domain_position, global_length) & result (length) class(diagDomain_t), INTENT(IN) :: this !< diag_axis obj - character(len=*), INTENT(IN) :: cart_axis !< cart_axis of the axis + character(len=*), INTENT(IN) :: cart_axis !< cart_axis of the axis, must be "X" or "Y" integer, INTENT(IN) :: domain_position !< Domain position (CENTER, NORTH, EAST) integer, INTENT(IN) :: global_length !< global_length of the axis @@ -982,7 +1096,10 @@ end function get_length !!!!!!!!!!!!!!!!! FMS_DOMAIN PROCEDURES !!!!!!!!!!!!!!!!! - !> @brief Set the axis domain + !> @brief Assign a specific domain object to this domain wrapper. + !! + !! This subroutine assigns the appropriate domain object (1D, 2D, or unstructured) + !! to the polymorphic domain wrapper based on the type-specific argument provided. subroutine set_axis_domain(this, Domain, Domain2, DomainU) class(diagDomain_t) :: this !< fms_domain obj TYPE(domain1d), INTENT(in), OPTIONAL :: Domain !< 1d domain @@ -999,8 +1116,13 @@ subroutine set_axis_domain(this, Domain, Domain2, DomainU) end select end subroutine set_axis_domain - !< @brief Allocates the array of axis/subaxis objects - !! @return true if there the aray of axis/subaxis objects is allocated + !> @brief Allocate the module-level array of diagnostic axis containers. + !! + !! This initialization routine must be called before any axes are created. + !! It allocates a global array to hold all axis objects up to the limit + !! defined by max_axes. Returns .true. on successful allocation. + !! + !! @return .true. if allocation succeeded logical function fms_diag_axis_object_init(axis_array) class(fmsDiagAxisContainer_type) , allocatable, intent(inout) :: axis_array(:) !< Array of diag_axis @@ -1011,8 +1133,12 @@ logical function fms_diag_axis_object_init(axis_array) fms_diag_axis_object_init = .true. end function fms_diag_axis_object_init - !< @brief Deallocates the array of axis/subaxis objects - !! @return false if the aray of axis/subaxis objects was allocated + !> @brief Deallocate the module-level array of diagnostic axis containers. + !! + !! This cleanup routine should be called when axis management is no longer needed. + !! It deallocates the global axis array, freeing all associated memory. + !! + !! @return .false. after successful deallocation logical function fms_diag_axis_object_end(axis_array) class(fmsDiagAxisContainer_type) , allocatable, intent(inout) :: axis_array(:) !< Array of diag_axis @@ -1021,11 +1147,17 @@ logical function fms_diag_axis_object_end(axis_array) end function fms_diag_axis_object_end - !< @brief Determine the axis name of an axis_object - !! @return The name of the axis - !! @note This function may be called from the field object (i.e. to determine the dimension names for io), - !! The field object only contains the parent axis ids, because the subregion is defined in a per file basis, - !! so the is_regional flag is needed so that the correct axis name can be used + !> @brief Determine the name of the axis (or subaxis) represented by the given axis object. + !! + !! Returns the axis name, optionally modified for regional subsets. + !! When is_regional is .true. and this is an X or Y axis, "_sub01" is appended + !! to indicate a regional subset. + !! + !! @note This function may be called from field objects to get dimension names. + !! Field objects know only parent axis IDs; the is_regional flag distinguishes + !! whether regional subaxis naming should be applied. + !! + !! @return The axis name (possibly modified for regional output) pure function get_axis_name(this, is_regional) & result(axis_name) class(fmsDiagAxis_type), intent(in) :: this !< Axis object @@ -1057,9 +1189,13 @@ pure logical function is_z_axis(this) end select end function - !> @brief Check if a cart_name is valid and crashes if it isn't + !> @brief Validate a Cartesian axis classification name. + !! + !! Checks that the provided Cartesian axis name is one of the recognized values. + !! Valid values are: X (longitude), Y (latitude), Z (vertical), T (time), + !! U (unstructured), or N (no axis). Calls mpp_error(FATAL) if invalid. subroutine check_if_valid_cart_name(cart_name) - character(len=*), intent(in) :: cart_name + character(len=*), intent(in) :: cart_name !< axis cartesian classification name to validate select case (cart_name) case ("X", "Y", "Z", "T", "U", "N") @@ -1069,9 +1205,13 @@ subroutine check_if_valid_cart_name(cart_name) end select end subroutine check_if_valid_cart_name - !> @brief Check if a domain_position is valid and crashes if it isn't + !> @brief Validate a domain position classification. + !! + !! Checks that the provided domain position is one of the recognized values. + !! Valid values are: CENTER, NORTH, or EAST (from mpp_domains_mod). + !! Calls mpp_error(FATAL) if invalid. subroutine check_if_valid_domain_position(domain_position) - integer, INTENT(IN) :: domain_position + integer, INTENT(IN) :: domain_position !< domain position to validate (CENTER, NORTH, EAST) select case (domain_position) case (CENTER, NORTH, EAST) @@ -1081,9 +1221,13 @@ subroutine check_if_valid_domain_position(domain_position) end select end subroutine check_if_valid_domain_position - !> @brief Check if a direction is valid and crashes if it isn't + !> @brief Validate an axis direction indicator. + !! + !! Checks that the provided direction value is one of the recognized values. + !! Valid values are: -1 (down/decreasing), 0 (no direction), or 1 (up/increasing). + !! Calls mpp_error(FATAL) if invalid. subroutine check_if_valid_direction(direction) - integer, INTENT(IN) :: direction + integer, INTENT(IN) :: direction !< Direction indicator to validate (-1, 0, 1) select case(direction) case(-1, 0, 1) @@ -1093,7 +1237,12 @@ subroutine check_if_valid_direction(direction) end select end subroutine check_if_valid_direction - !> @brief Loop through a variable's axis_id to determine and return the domain type and domain to use + !> @brief Determine the domain type and domain object from a list of axis IDs. + !! + !! This utility subroutine examines all axes used by a variable and determines + !! which domain type (and domain object) should be used for output. It handles + !! the case where a variable has both domain-decomposed and non-decomposed axes + !! (e.g., X,Y and Z axes on different domain types). subroutine get_domain_and_domain_type(diag_axis, axis_id, domain_type, domain, var_name) class(fmsDiagAxisContainer_type), target, intent(in) :: diag_axis(:) !< Array of diag_axis integer, INTENT(IN) :: axis_id(:) !< Array of axis ids @@ -1110,7 +1259,7 @@ subroutine get_domain_and_domain_type(diag_axis, axis_id, domain_type, domain, v do i = 1, size(axis_id) j = axis_id(i) select type (axis => diag_axis(j)%axis) - type is (fmsDiagFullAxis_type) + type is (fmsdiagfullaxis_type) !< Check that all the axis are in the same domain if (domain_type .ne. axis%type_of_domain) then !< If they are different domains, one of them can be NO_DOMAIN @@ -1130,7 +1279,13 @@ subroutine get_domain_and_domain_type(diag_axis, axis_id, domain_type, domain, v enddo end subroutine get_domain_and_domain_type - !> @brief Fill in the subaxis object for a subRegion defined by index + !> @brief Create a subaxis for a subregion defined by explicit index bounds. + !! + !! This subroutine creates a new subaxis for output of a limited region specified + !! by starting and ending indices (corners). It determines which processes + !! contribute data to the subregion and allocates the subaxis accordingly. + !! + !! @note Only processes with compute domains overlapping the subregion will have write_on_this_pe = .true. subroutine define_new_subaxis_index(parent_axis, subRegion, diag_axis, naxis, is_x_or_y, write_on_this_pe) class(fmsDiagAxisContainer_type), target, intent(inout) :: diag_axis(:) !< Diag_axis object type(fmsDiagFullAxis_type), intent(inout) :: parent_axis !< axis object of the parent @@ -1167,7 +1322,13 @@ subroutine define_new_subaxis_index(parent_axis, subRegion, diag_axis, naxis, is end subroutine define_new_subaxis_index - !> @brief Fill in the subaxis object for a subRegion defined by lat lon + !> @brief Create subaxes for a subregion defined by latitude/longitude bounds. + !! + !! This subroutine creates new X and Y subaxes for output of a geographic region + !! specified by lat/lon bounds. It handles both cubesphere and regular lat/lon grids, + !! determining the corresponding index ranges for each grid type. + !! + !! @note Uses cubesphere index functions if is_cube_sphere is .true., otherwise nearest_index subroutine define_new_subaxis_latlon(diag_axis, axis_ids, naxis, subRegion, is_cube_sphere, write_on_this_pe) class(fmsDiagAxisContainer_type), target, intent(inout) :: diag_axis(:) !< Diag_axis object integer, INTENT(in) :: axis_ids(:) !< Array of axes_ids @@ -1298,7 +1459,11 @@ subroutine define_new_subaxis_latlon(diag_axis, axis_ids, naxis, subRegion, is_c end subroutine define_new_subaxis_latlon - !> @brief Creates a new subaxis and fills it will all the information it needs + !> @brief Create a new subaxis and initialize it with index ranges. + !! + !! This is the core subroutine for creating subaxes. It allocates a new fmsDiagSubAxis_type + !! object, fills it with the provided index information, and registers it with the parent axis. + !! The parent axis's subaxis list is updated to include the new subaxis ID. subroutine define_new_axis(diag_axis, parent_axis, naxis, parent_id, & starting_index, ending_index, compute_idx, global_idx, new_axis_id, zbounds, & nz_subaxis) @@ -1337,8 +1502,12 @@ subroutine define_new_axis(diag_axis, parent_axis, naxis, parent_id, & end select end subroutine define_new_axis - !< @brief Determine the parent_axis_id of a subaxis - !! @return parent_axis_id if it is a subaxis and diag_null if is not a subaxis + !> @brief Get the ID of the parent axis if this is a subaxis. + !! + !! For subaxis objects, returns the axis ID of the parent full axis. + !! For non-subaxis objects (full axes), returns diag_null. + !! + !! @return Parent axis ID if this is a subaxis; diag_null otherwise pure function get_parent_axis_id(this) & result(parent_axis_id) @@ -1356,8 +1525,12 @@ pure function get_parent_axis_id(this) & end function - !< @brief Determine the most recent subaxis id in a diag_axis object - !! @return the most recent subaxis id in a diag_axis object + !> @brief Get the ID of the most recently defined subaxis of this axis. + !! + !! For non-Z axes with subaxes defined, returns the ID of the latest subaxis. + !! For Z axes or axes without subaxes, returns the axis's own ID. + !! + !! @return ID of the most recent subaxis, or this%axis_id if no subaxes pure function get_subaxes_id(this) & result(sub_axis_id) @@ -1372,8 +1545,13 @@ pure function get_subaxes_id(this) & end function - !< @brief Parses the "compress" attribute to get the names of the two axis - !! @return the names of the structured axis + !> @brief Parse the "compress" dimension specification into structured axis names. + !! + !! The "compress" attribute defines a compressed (packed) dimension by listing + !! the names of two or more axes that are combined into a single dimension. + !! This function extracts those axis names from the attribute value. + !! + !! @return Array of axis names pure function parse_compress_att(compress_att) & result(axis_names) class(*), intent(in) :: compress_att(:) !< The compress attribute to parse @@ -1390,8 +1568,13 @@ pure function parse_compress_att(compress_att) & end select end function parse_compress_att - !< @brief Determine the axis id of a axis - !! @return Axis id + !> @brief Look up an axis ID by name and optional axis set name. + !! + !! Searches the registered axes for one matching the given name and set name. + !! The set_name parameter allows disambiguation when multiple axis configurations + !! with the same name are defined (e.g., different resolutions on the same grid). + !! + !! @return Axis ID if found; diag_null if not found pure function get_axis_id_from_name(axis_name, diag_axis, naxis, set_name) & result(axis_id) class(fmsDiagAxisContainer_type), intent(in) :: diag_axis(:) !< Array of axis object @@ -1417,8 +1600,7 @@ pure function get_axis_id_from_name(axis_name, diag_axis, naxis, set_name) & end function get_axis_id_from_name - !< @brief Get the number of diurnal samples for a diurnal axis - !! @return The number of diurnal samples + !> @brief Get the number of time-of-day samples for a diurnal axis. pure function get_diurnal_axis_samples(this) & result(n_diurnal_samples) @@ -1428,7 +1610,10 @@ pure function get_diurnal_axis_samples(this) & n_diurnal_samples = this%ndiurnal_samples end function get_diurnal_axis_samples - !< @brief Writes out the metadata for a diurnal axis + !> @brief Write diurnal axis metadata to a NetCDF file. + !! + !! Registers the diurnal axis dimension and variable in the file object, + !! along with associated attributes (units, long_name, edges reference). subroutine write_diurnal_metadata(this, fms2io_fileobj) class(fmsDiagDiurnalAxis_type), intent(in) :: this !< Diurnal axis Object class(FmsNetcdfFile_t), intent(inout) :: fms2io_fileobj !< Fms2_io fileobj to write the data to @@ -1444,7 +1629,13 @@ subroutine write_diurnal_metadata(this, fms2io_fileobj) &trim(this%edges_name), str_len=len_trim(this%edges_name)) end subroutine write_diurnal_metadata - !> @brief Creates a new z subaxis to use + !> @brief Create or reuse a Z-axis subaxis for the specified Z bounds. + !! + !! This subroutine manages Z-axis compression by creating subaxes for specific + !! depth ranges. If an identical Z subaxis already exists in the file, that + !! existing subaxis is reused. Otherwise, a new subaxis is created. + !! + !! This deduplication avoids creating multiple subaxes with identical ranges. subroutine create_new_z_subaxis(zbounds, var_axis_ids, diag_axis, naxis, file_axis_id, nfile_axis, nz_subaxis, & error_mseg) real(kind=r4_kind), intent(in) :: zbounds(2) !< Bounds of the Z axis @@ -1523,8 +1714,10 @@ subroutine create_new_z_subaxis(zbounds, var_axis_ids, diag_axis, naxis, file_ax end subroutine - !> @brief Determine if the diag_axis(parent_axis_id) is the parent of diag_axis(axis_id) - !! @return .True. if diag_axis(parent_axis_id) is the parent of diag_axis(axis_id) + !> @brief Check if one axis is the parent of another. + !! + !! Determines parent-child relationships between axes by checking if axis_id + !! is a subaxis of parent_axis_id. function is_parent_axis(axis_id, parent_axis_id, diag_axis) & result(rslt) integer, intent(in) :: axis_id !< Axis id to check @@ -1540,8 +1733,10 @@ function is_parent_axis(axis_id, parent_axis_id, diag_axis) & end select end function is_parent_axis - !> @brief Determine the name of the z subaxis by matching the parent axis id and the zbounds - !! in the diag table yaml + !> @brief Find the name of a Z-axis subaxis by matching parent and bounds. + !! + !! Searches the file's registered axes to find a Z subaxis with the specified + !! parent axis and Z bounds. The axis name is returned in dim_name. subroutine find_z_sub_axis_name(dim_name, parent_axis_id, file_axis_id, field_yaml, diag_axis) character(len=*), intent(inout) :: dim_name !< Name of z subaxis integer, intent(in) :: parent_axis_id !< Axis id of the parent diff --git a/diag_manager/fms_diag_bbox.F90 b/diag_manager/fms_diag_bbox.F90 index 7f4e672b20..38df6b2961 100644 --- a/diag_manager/fms_diag_bbox.F90 +++ b/diag_manager/fms_diag_bbox.F90 @@ -33,7 +33,7 @@ MODULE fms_diag_bbox_mod implicit none -!> @brief Data structure holding a 3D bounding box. It is commonlyused to +!> @brief Data structure holding a 3D bounding box. It is commonly used to !! represent the interval bounds or limits of a 3D sub-array such as the !! array index bounds of the spatial component a diag_manager field output !! buffer array. @@ -92,45 +92,45 @@ MODULE fms_diag_bbox_mod integer, parameter :: ydimension = 2 !< Parameter defining the y dimension integer, parameter :: zdimension = 3 !< Parameter defininf the z dimension -CONTAINS - -!> @brief The PEs grid points are divided further into "blocks". This function determines if a block -! has data for a given subregion and dimension -!! @return .true. if the a subergion is inside a block -logical pure function determine_if_block_is_in_region(subregion_start, subregion_end, bounds, dim) - integer, intent(in) :: subregion_start !< Begining of the subregion - integer, intent(in) :: subregion_end !< Ending of the subregion - type(fmsDiagIbounds_type), intent(in) :: bounds !< Starting and ending of the subregion - integer, intent(in) :: dim !< Dimension to check - - integer :: block_start !< Begining index of the block - integer :: block_end !< Ending index of the block - - determine_if_block_is_in_region = .true. - select case (dim) - case (xdimension) - block_start = bounds%imin - block_end = bounds%imax - case (ydimension) - block_start = bounds%jmin - block_end = bounds%jmax - case (zdimension) - block_start = bounds%kmin - block_end = bounds%kmax - end select - - if (block_start < subregion_start .and. block_end < subregion_start) then - determine_if_block_is_in_region = .false. - return - endif - - if (block_start > subregion_end .and. block_end > subregion_end) then - determine_if_block_is_in_region = .false. - return - endif - - determine_if_block_is_in_region = .true. -end function determine_if_block_is_in_region + CONTAINS + + !> @brief The PEs grid points are divided further into "blocks". This function determines if a block + ! has data for a given subregion and dimension + !! @return .true. if the a subergion is inside a block + logical pure function determine_if_block_is_in_region(subregion_start, subregion_end, bounds, dim) + integer, intent(in) :: subregion_start !< Begining of the subregion + integer, intent(in) :: subregion_end !< Ending of the subregion + type(fmsDiagIbounds_type), intent(in) :: bounds !< Starting and ending of the subregion + integer, intent(in) :: dim !< Dimension to check + + integer :: block_start !< Begining index of the block + integer :: block_end !< Ending index of the block + + determine_if_block_is_in_region = .true. + select case (dim) + case (xdimension) + block_start = bounds%imin + block_end = bounds%imax + case (ydimension) + block_start = bounds%jmin + block_end = bounds%jmax + case (zdimension) + block_start = bounds%kmin + block_end = bounds%kmax + end select + + if (block_start < subregion_start .and. block_end < subregion_start) then + determine_if_block_is_in_region = .false. + return + endif + + if (block_start > subregion_end .and. block_end > subregion_end) then + determine_if_block_is_in_region = .false. + return + endif + + determine_if_block_is_in_region = .true. + end function determine_if_block_is_in_region !> @brief Gets imin of fmsDiagIbounds_type !! @return copy of integer member imin diff --git a/diag_manager/fms_diag_field_object.F90 b/diag_manager/fms_diag_field_object.F90 index b8924a23ff..7e0e88fae9 100644 --- a/diag_manager/fms_diag_field_object.F90 +++ b/diag_manager/fms_diag_field_object.F90 @@ -16,14 +16,25 @@ !* governing permissions and limitations under the License. !*********************************************************************** module fms_diag_field_object_mod -!> \author Tom Robinson -!> \email thomas.robinson@noaa.gov -!! \brief Contains routines for the diag_objects +!> @defgroup fms_diag_field_object_mod fms_diag_field_object_mod +!> @ingroup diag_manager +!> @addtogroup fms_diag_field_object_mod +!> @{ +!! @brief Modern object-oriented implementation of diagnostic field management for FMS. +!! @author Tom Robinson !! -!! \description The diag_manager passes an object back and forth between the diag routines and the users. -!! The procedures of this object and the types are all in this module. The fms_dag_object is a type -!! that contains all of the information of the variable. It is extended by a type that holds the -!! appropriate buffer for the data for manipulation. +!! This module defines @ref fmsDiagField_type, the object that encapsulates metadata, +!! buffering state, masking, and output registration for a single diagnostic field. +!! It provides helper methods for field registration, data buffering, metadata queries, +!! and NetCDF I/O support used by the diag_manager. +!! +!! The diagnostic field object is the central representation of a variable in +!! FMS diagnostics. Each instance stores YAML metadata, file IDs, axis associations, +!! buffer allocation state, attributes, missing-value handling, and the input buffer +!! used to assemble data before reduction or output. +!! +!! @file +!! @brief File for @ref fms_diag_field_object_mod #ifdef use_yaml use diag_data_mod, only: prepend_date, diag_null, CMOR_MISSING_VALUE, diag_null_string, MAX_STR_LEN use diag_data_mod, only: r8, r4, i8, i4, string, null_type_int, NO_DOMAIN @@ -52,7 +63,15 @@ module fms_diag_field_object_mod private -!> \brief Object that holds all variable information +!> @brief Object that holds all metadata and buffering state for a diagnostic field. +!! +!! The fmsDiagField_type represents a single diagnostic variable in the FMS +!! diagnostic manager. It stores the YAML field metadata, registration state, +!! axis associations, mask and missing-value handling, data buffering state, +!! and helper procedures used to prepare and write the field to output files. +!! +!! This object is passed through diagnostic routines and serves as the primary +!! per-variable container for the diag_manager. type fmsDiagField_type type (diagYamlFilesVar_type), allocatable, dimension(:) :: diag_field !< info from diag_table for this variable integer, allocatable, dimension(:) :: file_ids !< Ids of the FMS_diag_files the variable diff --git a/diag_manager/fms_diag_fieldbuff_update.F90 b/diag_manager/fms_diag_fieldbuff_update.F90 index f25b8d8088..d9ff3580a7 100644 --- a/diag_manager/fms_diag_fieldbuff_update.F90 +++ b/diag_manager/fms_diag_fieldbuff_update.F90 @@ -27,6 +27,10 @@ !!(array) of field data statistics (e.g. average, rms) with new field data. These !! routines are called by the send_data routines in the diag_manager. !! +!! @note This module is part of the legacy diag_manager. If use_modern_diag is enabled, this module will be unused. +!! See fms_diag_output_buffer.F90 and fms_diag_input_buffer.F90 for the modern +!! diag_manager implementation of field buffer updates. +!! !> @file !> @brief File for @ref fms_diag_fieldbuff_update_mod !> @addtogroup fms_diag_fieldbuff_update_mod diff --git a/diag_manager/fms_diag_file_object.F90 b/diag_manager/fms_diag_file_object.F90 index 92f09e3e61..67bc4489e5 100644 --- a/diag_manager/fms_diag_file_object.F90 +++ b/diag_manager/fms_diag_file_object.F90 @@ -15,13 +15,24 @@ !* PARTICULAR PURPOSE. See the License for the specific language !* governing permissions and limitations under the License. !*********************************************************************** -!> @defgroup fms_diag_output_yaml_mod fms_diag_output_yaml_mod + +!> @defgroup fms_diag_file_object_mod fms_diag_file_object_mod !> @ingroup diag_manager -!! @brief fms_diag_file_object_mod handles the file objects data, functions, and subroutines. +!! @brief Modern object-oriented implementation of diagnostic file management for FMS. +!! +!! This module defines @ref fmsDiagFile_type, the object that encapsulates metadata, +!! axis lists, field registrations, and output buffering for a single diagnostic output file. +!! It manages file-level properties, domain information, and time tracking for NetCDF I/O. +!! !! @author Tom Robinson !! @description The fmsDiagFile_type contains the information for each history file to be written. It has !! a pointer to the information from the diag yaml, additional metadata that comes from the model, and a !! list of the variables and their variable IDs that are in the file. + +!! @file +!! @brief File for @ref fms_diag_file_object_mod +!> @addtogroup fms_diag_file_object_mod +!> @{ module fms_diag_file_object_mod #ifdef use_yaml use fms2_io_mod, only: FmsNetcdfFile_t, FmsNetcdfUnstructuredDomainFile_t, FmsNetcdfDomainFile_t, & @@ -57,15 +68,23 @@ module fms_diag_file_object_mod public :: fmsDiagFileContainer_type public :: fmsDiagFile_type, fms_diag_files_object_init, fms_diag_files_object_initialized +!> @} + logical :: fms_diag_files_object_initialized = .false. integer, parameter :: var_string_len = 25 +!> @brief Object that holds all metadata and state for a diagnostic output file. +!! +!! The fmsDiagFile_type represents a single output file in the FMS diagnostic manager. +!! It stores YAML file metadata, domain information, associated fields and axes, +!! buffer references, and output timing state needed to coordinate data writes across +!! multiple send_data calls and model time steps. type :: fmsDiagFile_type private - integer :: id !< The number associated with this file in the larger array of files - TYPE(time_type) :: model_time !< The last time data was sent for any of the buffers in this file object + integer :: id !< Unique identifier for this file in the larger array of files + TYPE(time_type) :: model_time !< Most recent model time at which data was sent to any buffer in this file TYPE(time_type) :: start_time !< The start time for the file TYPE(time_type) :: last_output !< Time of the last time output was writen TYPE(time_type) :: next_output !< Time of the next write diff --git a/diag_manager/fms_diag_input_buffer.F90 b/diag_manager/fms_diag_input_buffer.F90 index 4422ffa319..8ebe8b80b5 100644 --- a/diag_manager/fms_diag_input_buffer.F90 +++ b/diag_manager/fms_diag_input_buffer.F90 @@ -17,7 +17,12 @@ !*********************************************************************** !> @defgroup fms_diag_input_buffer_mod fms_diag_input_buffer_mod !> @ingroup diag_manager -!! @brief +!! @brief This module defines the fmsDiagInputBuffer_t type, which is used to hold the input buffer for a field when +!! send_data is called from an openmp region with multiple threads. +!! This is necessary to ensure that the data passed into send_data is not overwritten +!! by another thread before the reduction method is applied to it. Each thread saves their portion +!! of the data into this type and the reduction method is applied in the diag_send_complete call. + !> @addtogroup fms_diag_input_buffer_mod !> @{ module fms_diag_input_buffer_mod diff --git a/diag_manager/fms_diag_object.F90 b/diag_manager/fms_diag_object.F90 index 9f61f5efb9..82986c70fb 100644 --- a/diag_manager/fms_diag_object.F90 +++ b/diag_manager/fms_diag_object.F90 @@ -1314,7 +1314,7 @@ type(domain2d) FUNCTION fms_get_domain2d(this, ids) call get_domain_and_domain_type(fms_diag_object%diag_axis, ids, type_of_domain, domain, "get_domain2d") if (type_of_domain .ne. TWO_D_DOMAIN) & - call mpp_error(FATAL, 'diag_axis_mod::get_domain2d- The axis do not correspond to a 2d Domain') + call mpp_error(FATAL, 'fms_diag_object_mod::fms_get_domain2d- The axis do not correspond to a 2d Domain') select type(domain) type is (diagDomain2d_t) fms_get_domain2d = domain%domain2 diff --git a/diag_manager/fms_diag_outfield.F90 b/diag_manager/fms_diag_outfield.F90 index 83ddae0f3b..efeb1b4e4a 100644 --- a/diag_manager/fms_diag_outfield.F90 +++ b/diag_manager/fms_diag_outfield.F90 @@ -23,6 +23,10 @@ !! !> @author Miguel Zuniga !! +!! @note This module is part of the legacy diag_manager. If use_modern_diag is enabled, this module will be unused. +!! See fms_diag_output_buffer.F90 and fms_diag_input_buffer.F90 for the modern +!! diag_manager implementation of field buffer updates. +!! !! fms_diag_outfield_mod The output buffer updating routines are passed configuration !! and control data with types defined in this module; and some utility functions called by the !! updating routines are diff --git a/diag_manager/fms_diag_output_buffer.F90 b/diag_manager/fms_diag_output_buffer.F90 index a47e8d144d..33ddac0d1a 100644 --- a/diag_manager/fms_diag_output_buffer.F90 +++ b/diag_manager/fms_diag_output_buffer.F90 @@ -15,13 +15,15 @@ !* PARTICULAR PURPOSE. See the License for the specific language !* governing permissions and limitations under the License. !*********************************************************************** +!> @defgroup fms_diag_output_buffer_mod fms_diag_output_buffer_mod +!> @ingroup diag_manager !> @author Ryan Mulhall !> @email ryan.mulhall@noaa.gov -!! @brief Contains buffer types and routines for the diag manager -!! -!! @description Holds buffered data for fmsDiagVars_type objects -!! buffer0-5d types extend fmsDiagBuffer_class, and upon allocation -!! are added to the module's buffer_lists depending on it's dimension +!! @brief Defines a type for the output buffer objects used in the modern diag_manager, +!! which hold the buffered data for fmsDiagVars_type objects prior to writing to file. + +!> @addtogroup fms_diag_output_buffer_mod +!> @{ module fms_diag_output_buffer_mod #ifdef use_yaml use platform_mod @@ -928,3 +930,4 @@ subroutine set_send_data_called(this) end subroutine set_send_data_called #endif end module fms_diag_output_buffer_mod +!> @} \ No newline at end of file diff --git a/diag_manager/fms_diag_reduction_methods.F90 b/diag_manager/fms_diag_reduction_methods.F90 index e0a45bc855..12ab3c968f 100644 --- a/diag_manager/fms_diag_reduction_methods.F90 +++ b/diag_manager/fms_diag_reduction_methods.F90 @@ -15,14 +15,10 @@ !* PARTICULAR PURPOSE. See the License for the specific language !* governing permissions and limitations under the License. !*********************************************************************** - !> @defgroup fms_diag_reduction_methods_mod fms_diag_reduction_methods_mod !> @ingroup diag_manager -!! @brief fms_diag_reduction_methods_mod contains routines that are meant to be used for -!! error checking and setting up to do the reduction methods - -!> @file -!> @brief File for @ref fms_diag_reduction_methods_mod +!! @brief fms_diag_reduction_methods_mod contains routines that are used to perform diagnostic reduction methods, +!! such as max/min, average, rms, etc. These routines are called by the send_data routines in the diag_manager. !> @addtogroup fms_diag_reduction_methods_mod !> @{ @@ -186,4 +182,5 @@ end function set_weight end module fms_diag_reduction_methods_mod !> @} -! close documentation grouping \ No newline at end of file +! close documentation grouping + diff --git a/diag_manager/fms_diag_yaml.F90 b/diag_manager/fms_diag_yaml.F90 index 69e39f9381..f9889cd07e 100644 --- a/diag_manager/fms_diag_yaml.F90 +++ b/diag_manager/fms_diag_yaml.F90 @@ -15,12 +15,11 @@ !* PARTICULAR PURPOSE. See the License for the specific language !* governing permissions and limitations under the License. !*********************************************************************** - !> @defgroup fms_diag_yaml_mod fms_diag_yaml_mod !> @ingroup diag_manager -!! @brief fms_diag_yaml_mod is an integral part of -!! diag_manager_mod. Its function is to read the diag_table.yaml to fill in -!! the diag_yaml_object +!! @brief This module defines the diagYamlObject_type, which is the object that holds +!! the information from the diag_table.yaml file. +!! It also contains routines for reading in the diag_table.yaml file and filling in the diagYamlObject_type object. !> @file !> @brief File for @ref diag_yaml_mod diff --git a/diag_manager/include/fms_diag_reduction_methods.inc b/diag_manager/include/fms_diag_reduction_methods.inc index 74b829e668..aedf8a8f3b 100644 --- a/diag_manager/include/fms_diag_reduction_methods.inc +++ b/diag_manager/include/fms_diag_reduction_methods.inc @@ -16,11 +16,6 @@ !* governing permissions and limitations under the License. !*********************************************************************** -! for any debug prints -#ifndef DEBUG_REDUCT -#define DEBUG_REDUCT .false. -#endif - !> @brief Do the time_none reduction method (i.e copy the correct portion of the input data) subroutine DO_TIME_NONE_ (data_out, data_in, mask, is_masked, bounds_in, bounds_out, missing_value) real(FMS_TRM_KIND_), intent(inout) :: data_out(:,:,:,:,:) !< output data