FMS 2025.01-dev
Flexible Modeling System
Loading...
Searching...
No Matches
field_manager.F90
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!> @defgroup field_manager_mod field_manager_mod
20!> @ingroup field_manager
21!> @brief Reads entries from a field table and stores this
22!! information along with the type of field it belongs to.
23!!
24!> This allows the component models to query the field manager to see if non-default
25!! methods of operation are desired. In essence the field table is a
26!! powerful type of namelist. Default values can be provided for all the
27!! fields through a namelist, individual fields can be modified through
28!! the field table however.
29!!
30!> @author William Cooke
31!!
32!! An example of field table entries could be
33!! <PRE>
34!! "tracer","atmos_mod","sphum"
35!!
36!! "tracer","atmos_mod","sf6"
37!! "longname","sulf_hex"
38!! "advection_scheme_horiz","2nd_order"
39!! "Profile_type","Fixed","surface_value = 0.0E+00"/
40!!
41!! "prog_tracers","ocean_mod","age_global"
42!! horizontal-advection-scheme = mdfl_sweby
43!! vertical-advection-scheme = mdfl_sweby
44!! restart_file = ocean_age.res.nc
45!! </PRE>
46!!
47!! The field table consists of entries in the following format.
48!!
49!! The first line of an entry should consist of three quoted strings.
50!!
51!! The first quoted string will tell the field manager what type of
52!! field it is.
53!!
54!! The second quoted string will tell the field manager which model the
55!! field is being applied to.
56!! The supported types at present are
57!!<PRE>
58!! "coupler_mod" for the coupler,
59!! "atmos_mod" for the atmosphere model,
60!! "ocean_mod" for the ocean model,
61!! "land_mod" for the land model, and,
62!! "ice_mod" for the ice model.
63!!</PRE>
64!! The third quoted string should be a unique name that can be used as a
65!! query.
66!!
67!! The second and following lines of each entry are called methods in
68!! this context. Methods can be developed within any module and these
69!! modules can query the field manager to find any methods that are
70!! supplied in the field table.
71!!
72!! These lines can be coded quite flexibly.
73!!
74!! The line can consist of two or three quoted strings or a simple unquoted
75!! string.
76!!
77!! If the line consists two or three quoted strings, then the first string will
78!! be an identifier that the querying module will ask for.
79!!
80!! The second string will be a name that the querying module can use to
81!! set up values for the module.
82!!
83!! The third string, if present, can supply parameters to the calling module that can be
84!! parsed and used to further modify values.
85!!
86!! If the line consists of a simple unquoted string then quotes are not allowed
87!! in any part of the line.
88!!
89!! An entry is ended with a backslash (/) as the final character in a
90!! row.
91!!
92!! Comments can be inserted in the field table by having a # as the
93!! first character in the line.
94!!
95!! In the example above we have three field entries.
96!!
97!! The first is a simple declaration of a tracer called "sphum".
98!!
99!! The second is for a tracer called "sf6". In this case a field named
100!! "longname" will be given the value "sulf_hex". A field named
101!! "advection_scheme_horiz" will be given the value "2nd_order". Finally a field
102!! name "Profile_type" will be given a child field called "Fixed", and that field
103!! will be given a field called "surface_value" with a real value of 0.0E+00.
104!!
105!! The third entry is an example of a oceanic age tracer. Note that the
106!! method lines are formatted differently here. This is the flexibility mentioned
107!! above.
108!!
109!! With these formats, a number of restrictions are required.
110!!
111!! The following formats are equally valid.
112!!<PRE>
113!! "longname","sulf_hex"
114!! "longname = sulf_hex"
115!! longname = sulf_hex
116!!</PRE>
117!! However the following is not valid.
118!!<PRE>
119!! longname = "sulf_hex"
120!!</PRE>
121!!
122!! In the SF6 example above the last line of the entry could be written in the
123!! following ways.
124!!<PRE>
125!! "Profile_type","Fixed","surface_value = 0.0E+00"/
126!! Profile_type/Fixed/surface_value = 0.0E+00/
127!!</PRE>
128!!
129!! Values supplied with fields are converted to the various types with the
130!! following assumptions.
131!!<PRE>
132!! Real values : These values contain a decimal point or are in exponential format.
133!! These values only support e or E format for exponentials.
134!! e.g. 10.0, 1e10 and 1E10 are considered to be real numbers.
135!!
136!! Integer values : These values only contain numbers.
137!! e.g 10 is an integer. 10.0 and 1e10 are not.
138!!
139!! Logical values : These values are supplied as one of the following formats.
140!! T, .T., TRUE, .TRUE.
141!! t, .t., true, .true.
142!! F, .F., FALSE, .FALSE.
143!! f, .f., false, .false.
144!! These will be converted to T or F in a dump of the field.
145!!
146!! Character strings : These values are assumed to be strings if a character
147!! other than an e (or E) is in the value. Numbers can be suppled in the value.
148!! If the value does not meet the criteria for a real, integer or logical type,
149!! it is assumed to be a character type.
150!!</PRE>
151!! The entries within the field table can be designed by the individual
152!! authors of code to allow modification of their routines.
153!!
154
155!> @addtogroup field_manager_mod
156!> @{
157module field_manager_mod
158!TODO this variable can be removed when the legacy table is no longer used
159#ifndef MAXFIELDS_
160#define MAXFIELDS_ 250
161#endif
162
163!TODO this variable can be removed when the legacy table is not longer used
164#ifndef MAXFIELDMETHODS_
165#define MAXFIELDMETHODS_ 250
166#endif
167
168!
169! <CONTACT EMAIL="William.Cooke@noaa.gov"> William Cooke
170! </CONTACT>
171!
172! <REVIEWER EMAIL="Richard.Slater@noaa.gov"> Richard D. Slater
173! </REVIEWER>
174!
175! <REVIEWER EMAIL="Matthew.Harrison@noaa.gov"> Matthew Harrison
176! </REVIEWER>
177!
178! <REVIEWER EMAIL="John.Dunne@noaa.gov"> John P. Dunne
179! </REVIEWER>
180
181use mpp_mod, only : mpp_error, &
182 fatal, &
183 note, &
184 warning, &
185 mpp_pe, &
186 mpp_root_pe, &
187 stdlog, &
188 stdout, &
189 input_nml_file
190use fms_mod, only : lowercase, &
191 write_version_number, &
192 check_nml_error
193use fms2_io_mod, only: file_exists, get_instance_filename
194use platform_mod, only: r4_kind, r8_kind, fms_path_len, fms_file_len
195#ifdef use_yaml
196use fm_yaml_mod
197#endif
198
199implicit none
200private
201
202#include<file_version.h>
203logical :: module_is_initialized = .false.
204
205public :: field_manager_init !< (nfields, [table_name]) returns number of fields
206public :: field_manager_end !< ()
207public :: find_field_index !< (model, field_name) or (list_path)
208public :: find_field_index_old !< (model, field_name) returns index of field_name in
209public :: find_field_index_new
210public :: get_field_info !< (n,fld_type,fld_name,model,num_methods)
211 !! Returns parameters relating to field n.
212public :: get_field_method !< (n, m, method) Returns the m-th method of field n
213public :: get_field_methods !< (n, methods) Returns the methods related to field n
214public :: parse !< (text, label, values) Overloaded function to parse integer,
215 !! real or character. Parse returns the number of values
216 !! decoded (> 1 => an array of values)
217public :: fm_change_list !< (list) return success
218public :: fm_change_root !< (list) return success
219public :: fm_dump_list !< (list [, recursive]) return success
220public :: fm_exists !< (field) return success
221public :: fm_get_index !< (field) return index
222public :: fm_get_current_list !< () return path
223public :: fm_get_length !< (list) return length
224public :: fm_get_type !< (field) return string
225public :: fm_get_value !< (entry, value [, index]) return success !! generic
226public :: fm_get_value_integer !< as above (overloaded function)
227public :: fm_get_value_logical !< as above (overloaded function)
228public :: fm_get_value_real_r4 !< as above (overloaded function)
229public :: fm_get_value_real_r8 !< as above (overloaded function)
230public :: fm_get_value_string !< as above (overloaded function)
231public :: fm_init_loop !< (list, iter)
232public :: fm_loop_over_list !< (list, name, type, index) return success
233 !! (iter, name, type, index) return success
234public :: fm_new_list !< (list [, create] [, keep]) return index
235public :: fm_new_value !< (entry, value [, create] [, index]) return index !! generic
236public :: fm_new_value_integer !< as above (overloaded function)
237public :: fm_new_value_logical !< as above (overloaded function)
238public :: fm_new_value_real_r4 !< as above (overloaded function)
239public :: fm_new_value_real_r8 !< as above (overloaded function)
240public :: fm_new_value_string !< as above (overloaded function)
241public :: fm_reset_loop !< ()
242public :: fm_return_root !< () return success
243public :: fm_modify_name !< (oldname, newname) return success
244public :: fm_query_method !< (name, method_name, method_control) return success and
245 !! name and control strings
246public :: fm_find_methods !< (list, methods, control) return success and name and
247 !! control strings.
248public :: fm_copy_list !< (list, suffix, [create]) return index
249private :: create_field ! (list_p, name) return field pointer
250private :: dump_list ! (list_p, recursive, depth) return success
251private :: find_base ! (field, path, base)
252private :: find_field ! (field, list_p) return field pointer
253private :: find_head ! (field, head, rest)
254private :: find_list ! (list, list_p, create) return field pointer
255private :: get_field ! (field, list_p) return field pointer
256private :: initialize_module_variables ! ()
257private :: make_list ! (list_p, name) return field pointer
258
259!> The length of a character string representing the field name.
260integer, parameter, public :: fm_field_name_len = 48
261!! TODO this should be removed in favor of the global FMS_PATH_LEN
262!! when possible, currently used in ocean_BGC and land_lad2
263!> The length of a character string representing the field path.
264integer, parameter, public :: fm_path_name_len = fms_path_len
265!> The length of a character string representing character values for the field.
266integer, parameter, public :: fm_string_len = 1024
267!> The length of a character string representing the various types that the values of the field can take.
268integer, parameter, public :: fm_type_name_len = 8
269!> Number of models (ATMOS, OCEAN, LAND, ICE, COUPLER).
270integer, parameter, public :: num_models = 5
271!> The value returned if a field is not defined.
272integer, parameter, public :: no_field = -1
273!> Atmospheric model.
274integer, parameter, public :: model_atmos = 1
275!> Ocean model.
276integer, parameter, public :: model_ocean = 2
277!> Land model.
278integer, parameter, public :: model_land = 3
279!> Ice model.
280integer, parameter, public :: model_ice = 4
281!> Ice model.
282integer, parameter, public :: model_coupler = 5
283!> Model names, e.g. MODEL_NAMES(MODEL_OCEAN) is 'oceanic'
284character(len=11), parameter, public, dimension(NUM_MODELS) :: &
285 model_names=(/'atmospheric','oceanic ','land ','ice ','coupler '/)
286
287!> @}
288
289!> @brief This method_type is a way to allow a component module to alter the parameters it needs
290!! for various tracers.
291!!
292!> In essence this is a way to modify a namelist. A namelist can supply
293!! default parameters for all tracers. This method will allow the user to modify these
294!! default parameters for an individual tracer. An example could be that the user wishes to
295!! use second order advection on a tracer and also use fourth order advection on a second
296!! tracer within the same model run. The default advection could be second order and the
297!! field table would then indicate that the second tracer requires fourth order advection.
298!! This would be parsed by the advection routine.
299!> @ingroup field_manager_mod
300type, public :: method_type
301
302 character(len=fm_string_len) :: method_type !< This string represents a tag that a module
303 !! using this method can key on. Typically this should
304 !! contain some reference to the module that is calling it.
305 character(len=fm_string_len) :: method_name !< This is the name of a method which the module
306 !! can parse and use to assign different default values to
307 !! a field method.
308 character(len=fm_string_len) :: method_control !< This is the string containing parameters that
309 !! the module can use as values for a field method. These should
310 !! override default values within the module.
311end type
312
313!> This method_type is the same as method_type except that the
314!! method_control string is not present. This is used when you wish to
315!! change to a scheme within a module but do not need to pass
316!! parameters. See @ref method_type for member information.
317!> @ingroup field_manager_mod
318type, public :: method_type_short
319 character(len=fm_string_len) :: method_type
320 character(len=fm_string_len) :: method_name
321end type
322
323!> This is the same as method_type except that the
324!! method_control and method_name strings are not present. This is used
325!! when you wish to change to a scheme within a module but do not need
326!! to pass parameters.
327!> @ingroup field_manager_mod
329 character(len=fm_string_len) :: method_type
330end type
331
332!> Iterator over the field manager list
333!> @ingroup field_manager_mod
334type, public :: fm_list_iter_type
335 type(field_def), pointer :: ptr => null() !< pointer to the current field
336end type fm_list_iter_type
337
338!> @ingroup field_manager_mod
339type(method_type), public :: default_method
340
341!> @brief Returns an index corresponding to the given field name.
342!!
343!> Model number can be given for old method.
344!! <br>Example usage:
345!! @code{.F90}
346!! value=find_field_index( model, field_name )
347!! value=find_field_index( field_name )
348!! @endcode
349!> @ingroup field_manager_mod
351 module procedure find_field_index_old
352 module procedure find_field_index_new
353end interface
354
355!> @brief A function to parse an integer or an array of integers,
356!! a real or an array of reals, a string or an array of strings.
357!!
358!> Parse is an integer function that decodes values from a text string.
359!! The text string has the form: "label=list" where "label" is an
360!! arbitrary user defined label describing the values being decoded,
361!! and "list" is a list of one or more values separated by commas.
362!! The values may be integer, real, or character.
363!! Parse returns the number of values decoded.
364!! <br>Example usage:
365!! @code{.F90}
366!! number = parse(text, label, value)
367!! @endcode
368!> @ingroup field_manager_mod
369interface parse
370 module procedure parse_real_r4
371 module procedure parse_real_r8
372 module procedure parse_reals_r4
373 module procedure parse_reals_r8
374 module procedure parse_integer
375 module procedure parse_integers
376 module procedure parse_string
377 module procedure parse_strings
378end interface
379
380!> @brief An overloaded function to assign a value to a field.
381!!
382!> Allocate and initialize a new value and return the index.
383!! If an error condition occurs the parameter NO_FIELD is returned.
384!!
385!! If the type of the field is changing (e.g. real values being transformed to
386!! integers), then any previous values for the field are removed and replaced
387!! by the value passed in the present call to this function.
388!!
389!! If append is present and .true., then index cannot be greater than 0 if
390!! it is present.
391!! <br> Example usage:
392!! @code{.F90}
393!! field_index= fm_new_value(name, value, [create], [index], [append])
394!! @endcode
395!> @ingroup field_manager_mod
397 module procedure fm_new_value_integer
398 module procedure fm_new_value_logical
399 module procedure fm_new_value_real_r4
400 module procedure fm_new_value_real_r8
401 module procedure fm_new_value_string
402end interface
403
404!> @brief An overloaded function to find and extract a value for a named field.
405!!
406!> Find and extract the value for name. The value may be of type real,
407!! integer, logical or character. If a single value from an array of values
408!! is required, an optional index can be supplied.
409!! Return true for success and false for failure
410!! <br> Example usage:
411!! @code{.F90}
412!! success = fm_get_value(name, value, index)
413!! @endcode
414!> @ingroup field_manager_mod
416 module procedure fm_get_value_integer
417 module procedure fm_get_value_logical
418 module procedure fm_get_value_real_r4
419 module procedure fm_get_value_real_r8
420 module procedure fm_get_value_string
421end interface
422
423!> @brief A function for looping over a list.
424!!
425!> Loop over the list, setting the name, type and index
426!! of the next field. Return false at the end of the loop.
427!! <br> Example usage:
428!! @code{.F90}
429!! success = fm_loop_over_list(list, name, field_type, index)
430!! @endcode
431!> @ingroup field_manager_mod
433 module procedure fm_loop_over_list_new
434 module procedure fm_loop_over_list_old
435end interface
436
437character(len=17), parameter :: module_name = 'field_manager_mod'
438character(len=33), parameter :: error_header = '==>Error from '//trim(module_name)//': '
439character(len=35), parameter :: warn_header = '==>Warning from '//trim(module_name)//': '
440character(len=32), parameter :: note_header = '==>Note from '//trim(module_name)//': '
441character(len=1), parameter :: comma = ","
442character(len=1), parameter :: list_sep = '/'
443!TODO these variable can be removed when the legacy table is no longer used
444character(len=1), parameter :: comment = '#'
445character(len=1), parameter :: dquote = '"'
446character(len=1), parameter :: equal = '='
447character(len=1), parameter :: squote = "'"
448!
449integer, parameter :: null_type = 0
450integer, parameter :: integer_type = 1
451integer, parameter :: list_type = 2
452integer, parameter :: logical_type = 3
453integer, parameter :: real_type = 4
454integer, parameter :: string_type = 5
455integer, parameter :: num_types = 5
456integer, parameter :: array_increment = 10
457!TODO these variable can be removed when the legacy table is no longer used
458integer, parameter :: MAX_FIELDS = maxfields_
459integer, parameter :: MAX_FIELD_METHODS = maxfieldmethods_
460!
461
462!> @brief Private type for internal use
463!> @ingroup field_manager_mod
464type, private :: field_mgr_type
465 character(len=fm_field_name_len) :: field_type
466 character(len=fm_string_len) :: field_name
467 integer :: model, num_methods
468 type(method_type), dimension(:), allocatable :: methods !< methods associated with this field name
469end type field_mgr_type
470
471!TODO These two types: field_names_type and field_names_type_short
472!! will no longer be needed when the legacy field table is not used
473!> @brief Private type for internal use
474!> @ingroup field_manager_mod
475type, private :: field_names_type
476 character(len=fm_field_name_len) :: fld_type
477 character(len=fm_field_name_len) :: mod_name
478 character(len=fm_string_len) :: fld_name
479end type field_names_type
480
481!> @brief Private type for internal use
482!> @ingroup field_manager_mod
484 character(len=fm_field_name_len) :: fld_type
485 character(len=fm_field_name_len) :: mod_name
487
488!> @brief Private type for internal use
489!> @ingroup field_manager_mod
490type, private :: field_def
491 character (len=fm_field_name_len) :: name
492 integer :: index
493 type (field_def), pointer :: parent => null()
494 integer :: field_type
495 integer :: length
496 integer :: array_dim
497 integer :: max_index
498 type (field_def), pointer :: first_field => null()
499 type (field_def), pointer :: last_field => null()
500 integer, allocatable, dimension(:) :: i_value
501 logical, allocatable, dimension(:) :: l_value
502 real(r8_kind), allocatable, dimension(:) :: r_value !< string to real conversion will be done at r8;
503 !! all real values will be stored as r8_kind.
504 character(len=fm_string_len), allocatable, dimension(:) :: s_value
505 type (field_def), pointer :: next => null()
506 type (field_def), pointer :: prev => null()
507end type field_def
508
509!> @addtogroup field_manager_mod
510!> @{
511
512type(field_mgr_type), dimension(:), allocatable, private :: fields !< fields of field_mgr_type
513
514character(len=FMS_PATH_LEN) :: loop_list
515character(len=fm_type_name_len) :: field_type_name(num_types)
516character(len=fm_field_name_len) :: save_root_name
517! The string set is the set of characters.
518character(len=52) :: set = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
519! If a character in the string being parsed matches a character within
520! the string set_nonexp then the string being parsed cannot be a number.
521character(len=50) :: set_nonexp = "ABCDFGHIJKLMNOPQRSTUVWXYZabcdfghijklmnopqrstuvwxyz"
522! If a character in the string being parsed matches a character within
523! the string setnum then the string may be a number.
524character(len=13) :: setnum = "0123456789+-."
525integer :: num_fields = 0
526type (field_def), pointer :: loop_list_p => null()
527type (field_def), pointer :: current_list_p => null()
528type (field_def), pointer :: root_p => null()
529type (field_def), pointer :: save_root_parent_p => null()
530type (field_def), target, save :: root
531
532logical :: use_field_table_yaml = .false. !< .True. if using the field_table.yaml,
533 !! .false. if using the legacy field_table
534
535namelist /field_manager_nml/ use_field_table_yaml
536
537contains
538
539!> @brief Routine to initialize the field manager.
540!!
541!> This routine reads from a file containing yaml paramaters.
542!! These yaml parameters contain information on which schemes are
543!! needed within various modules. The field manager does not
544!! initialize any of those schemes however. It simply holds the
545!! information and is queried by the appropriate module.
546!!
547!! The routine has two loops. The first loop initializes the my_table object
548!! and counts the number of fields contained therein. The second loop is the
549!! main loop that acts on each field in the my_table object, defining a list
550!! object (in the field_manager definition) from which various fm routines may be
551!! called, as well as populating the "fields" object and the "methods" objects
552!! within each field object. The "fields" and "methods" objects are then used
553!! with the subroutine new_name to append various characteristics to the list
554!! object. Note that the "fields" and "methods" objects are also used with other
555!! fm routines in a bit of a parallel system.
556subroutine field_manager_init(nfields, table_name)
557integer, intent(out), optional :: nfields !< number of fields
558character(len=fm_string_len), intent(in), optional :: table_name !< Name of the field table, default
559
560if (module_is_initialized) then
561 if(present(nfields)) nfields = num_fields
562 return
563endif
564
566
567!TODO the use_field_table_yaml namelist can be removed when the legacy table is no longer in used
568if (use_field_table_yaml) then
569 !Crash if you are not compiling with -Duse_yaml or if the field_table is present
570#ifndef use_yaml
571 call mpp_error(fatal, "You cannot have use_field_table_yaml=.true. without compiling with -Duse_yaml")
572#else
573 if (file_exists("field_table")) &
574 call mpp_error(fatal, "You cannot have the legacy field_table if use_field_table_yaml=.true.")
575
576 call mpp_error(note, "You are using the yaml version of the field_table")
577 call read_field_table_yaml(nfields, table_name)
578#endif
579else
580 if (file_exists("field_table.yaml")) &
581 call mpp_error(fatal, "You cannot have the yaml field_table if use_field_table_yaml=.false.")
582 call mpp_error(note, "You are using the legacy version of the field_table")
583 call read_field_table_legacy(nfields, table_name)
584endif
585
586end subroutine field_manager_init
587
588#ifdef use_yaml
589
590!> @brief Routine to read and parse the field table yaml
591subroutine read_field_table_yaml(nfields, table_name)
592integer, intent(out), optional :: nfields !< number of fields
593character(len=*), intent(in), optional :: table_name !< Name of the field table file, default is 'field_table.yaml'
594
595character(len=FMS_FILE_LEN) :: tbl_name !< field_table yaml file
596character(len=fm_string_len) :: method_control !< field_table yaml file
597integer :: h, i, j, k, l, m !< dummy integer buffer
598type (fmTable_t) :: my_table !< the field table
599integer :: model !< model assocaited with the current field
600character(len=FMS_PATH_LEN) :: list_name !< field_manager list name
601character(len=fm_string_len) :: subparamvalue !< subparam value to be used when defining new name
602character(len=fm_string_len) :: fm_yaml_null !< useful hack when OG subparam does not contain an equals sign
603integer :: current_field !< field index within loop
604integer :: index_list_name !< integer used as check for "no field"
605integer :: subparamindex !< index to identify whether subparams exist for this field
606logical :: fm_success !< logical for whether fm_change_list was a success
607logical :: subparams !< logical whether subparams exist in this iteration
608
609character(len=FMS_FILE_LEN) :: filename !< Name of the expected field_table.yaml
610
611if (.not.PRESENT(table_name)) then
612 tbl_name = 'field_table.yaml'
613else
614 tbl_name = trim(table_name)
615endif
616
617call get_instance_filename(tbl_name, filename)
618if (index(trim(filename), "ens_") .ne. 0) then
619 if (file_exists(filename) .and. file_exists(tbl_name)) &
620 call mpp_error(fatal, "Both "//trim(tbl_name)//" and "//trim(filename)//" exists, pick one!")
621
622 !< If the end_* file does not exist, revert back to tbl_name
623 !! where every ensemble is using the same yaml
624 if (.not. file_exists(filename)) filename = tbl_name
625endif
626
627if (.not. file_exists(trim(filename))) then
628 if(present(nfields)) nfields = 0
629 return
630endif
631
632! Construct my_table object
633call build_fmtable(my_table, trim(filename))
634
635do h=1,size(my_table%types)
636 do i=1,size(my_table%types(h)%models)
637 do j=1,size(my_table%types(h)%models(i)%variables)
638 num_fields = num_fields + 1
639 end do
640 end do
641end do
642
643allocate(fields(num_fields))
644
645current_field = 0
646do h=1,size(my_table%types)
647 do i=1,size(my_table%types(h)%models)
648 select case (my_table%types(h)%models(i)%name)
649 case ('coupler_mod')
650 model = model_coupler
651 case ('atmos_mod')
652 model = model_atmos
653 case ('ocean_mod')
654 model = model_ocean
655 case ('land_mod')
656 model = model_land
657 case ('ice_mod')
658 model = model_ice
659 case default
660 call mpp_error(fatal, trim(error_header)//'The model name is unrecognised : &
661 &'//trim(my_table%types(h)%models(i)%name))
662 end select
663 do j=1,size(my_table%types(h)%models(i)%variables)
664 current_field = current_field + 1
665 list_name = list_sep//lowercase(trim(my_table%types(h)%models(i)%name))//list_sep//&
666 lowercase(trim(my_table%types(h)%name))//list_sep//&
667 lowercase(trim(my_table%types(h)%models(i)%variables(j)%name))
668 index_list_name = fm_new_list(list_name, create = .true.)
669 if ( index_list_name == no_field ) &
670 call mpp_error(fatal, trim(error_header)//'Could not set field list for '//trim(list_name))
671 fm_success = fm_change_list(list_name)
672 fields(current_field)%model = model
673 fields(current_field)%field_name = lowercase(trim(my_table%types(h)%models(i)%variables(j)%name))
674 fields(current_field)%field_type = lowercase(trim(my_table%types(h)%name))
675 fields(current_field)%num_methods = size(my_table%types(h)%models(i)%variables(j)%keys)
676 allocate(fields(current_field)%methods(fields(current_field)%num_methods))
677 if(fields(current_field)%num_methods.gt.0) then
678 subparams = (size(my_table%types(h)%models(i)%variables(j)%attributes) .gt. 0)
679 do k=1,size(my_table%types(h)%models(i)%variables(j)%keys)
680 fields(current_field)%methods(k)%method_type = &
681 lowercase(trim(my_table%types(h)%models(i)%variables(j)%keys(k)))
682 fields(current_field)%methods(k)%method_name = &
683 lowercase(trim(my_table%types(h)%models(i)%variables(j)%values(k)))
684 if (.not.subparams) then
685 call new_name(list_name, my_table%types(h)%models(i)%variables(j)%keys(k),&
686 my_table%types(h)%models(i)%variables(j)%values(k) )
687 else
688 subparamindex=-1
689 do l=1,size(my_table%types(h)%models(i)%variables(j)%attributes)
690 if(lowercase(trim(my_table%types(h)%models(i)%variables(j)%attributes(l)%paramname)).eq.&
691 lowercase(trim(fields(current_field)%methods(k)%method_type))) then
692 subparamindex = l
693 exit
694 end if
695 end do
696 if (subparamindex.eq.-1) then
697 call new_name(list_name, my_table%types(h)%models(i)%variables(j)%keys(k),&
698 my_table%types(h)%models(i)%variables(j)%values(k) )
699 else
700 do m=1,size(my_table%types(h)%models(i)%variables(j)%attributes(subparamindex)%keys)
701 method_control = " "
702 subparamvalue = " "
703 if (trim(my_table%types(h)%models(i)%variables(j)%values(k)).eq.'fm_yaml_null') then
704 fm_yaml_null = ''
705 else
706 fm_yaml_null = trim(my_table%types(h)%models(i)%variables(j)%values(k))//'/'
707 end if
708 method_control = trim(my_table%types(h)%models(i)%variables(j)%keys(k))//"/"//&
709 &trim(fm_yaml_null)//&
710 &trim(my_table%types(h)%models(i)%variables(j)%attributes(subparamindex)%keys(m))
711 subparamvalue = trim(my_table%types(h)%models(i)%variables(j)%attributes(subparamindex)%values(m))
712 call new_name(list_name, method_control, subparamvalue)
713 end do
714 end if
715 end if
716 end do
717 end if
718 end do
719 end do
720end do
721
722if (present(nfields)) nfields = num_fields
723end subroutine read_field_table_yaml
724
725!> @brief Subroutine to add new values to list parameters.
726!!
727!> This subroutine uses input strings list_name, method_name
728!! and val_name_in to add new values to the list. Given
729!! list_name a new list item is created that is named
730!! method_name and is given the value or values in
731!! val_name_in. If there is more than 1 value in
732!! val_name_in, these values should be comma-separated.
733subroutine new_name_yaml ( list_name, method_name_in , val_name_in)
734character(len=*), intent(in) :: list_name !< The name of the field that is of interest here.
735character(len=*), intent(in) :: method_name_in !< The name of the method that values are
736 !! being supplied for.
737character(len=*), intent(inout) :: val_name_in !< The value or values that will be parsed and
738 !! used as the value when creating a new field or fields.
739
740character(len=fm_string_len) :: method_name !< name of method to be attached to new list
741character(len=fm_string_len) :: val_name !< value name (to be converted to appropriate type)
742integer, dimension(:), allocatable :: end_val !< end values in comma separated list
743integer, dimension(:), allocatable :: start_val !< start values in comma separated list
744integer :: i !< loop index
745integer :: index_t !< appending index
746integer :: num_elem !< number of elements in comma list
747integer :: val_int !< value when converted to integer
748integer :: val_type !< value type represented as integer for use in select case
749logical :: append_new !< whether or not to append to existing list structure
750logical :: val_logic !< value when converted to logical
751real(r8_kind) :: val_real !< value when converted to real.
752 !! All strings will be converted to r8_kind reals.
753
754call strip_front_blanks(val_name_in)
755method_name = trim(method_name_in)
756call strip_front_blanks(method_name)
757
758index_t = 1
759num_elem = 1
760append_new = .false.
761
762! If the array of values being passed in is a comma delimited list then count
763! the number of elements.
764
765do i = 1, len_trim(val_name_in)
766 if ( val_name_in(i:i) == comma ) then
767 num_elem = num_elem + 1
768 endif
769enddo
770
771allocate(start_val(num_elem))
772allocate(end_val(num_elem))
773start_val(1) = 1
774end_val(:) = len_trim(val_name_in)
775
776num_elem = 1
777do i = 1, len_trim(val_name_in)
778 if ( val_name_in(i:i) == comma ) then
779 end_val(num_elem) = i-1
780 start_val(num_elem+1) = i+1
781 num_elem = num_elem + 1
782 endif
783enddo
784
785do i = 1, num_elem
786
787 if ( i .gt. 1 .or. index_t .eq. 0 ) then
788 append_new = .true.
789 index_t = 0 ! If append is true then index must be <= 0
790 endif
791 val_type = string_type ! Assume it is a string
792 val_name = val_name_in(start_val(i):end_val(i))
793 call strip_front_blanks(val_name)
794
795 if ( scan(val_name(1:1), setnum ) > 0 ) then
796 if ( scan(val_name, set_nonexp ) .le. 0 ) then
797 if ( scan(val_name, '.') > 0 .or. scan(val_name, 'e') > 0 .or. scan(val_name, 'E') > 0) then
798 read(val_name, *) val_real
799 val_type = real_type
800 else
801 read(val_name, *) val_int
802 val_type = integer_type
803 endif
804 endif
805 endif
806
807 if ( len_trim(val_name) == 1 .or. len_trim(val_name) == 3) then
808 if ( val_name == 't' .or. val_name == 'T' .or. val_name == '.t.' .or. val_name == '.T.' ) then
809 val_logic = .true.
810 val_type = logical_type
811 endif
812 if ( val_name == 'f' .or. val_name == 'F' .or. val_name == '.f.' .or. val_name == '.F.' ) then
813 val_logic = .false.
814 val_type = logical_type
815 endif
816 endif
817 if ( trim(lowercase(val_name)) == 'true' .or. trim(lowercase(val_name)) == '.true.' ) then
818 val_logic = .true.
819 val_type = logical_type
820 endif
821 if ( trim(lowercase(val_name)) == 'false' .or. trim(lowercase(val_name)) == '.false.' ) then
822 val_logic = .false.
823 val_type = logical_type
824 endif
825
826 select case(val_type)
827
828 case (integer_type)
829 if ( fm_new_value( method_name, val_int, create = .true., index = index_t, append = append_new ) < 0 ) &
830 call mpp_error(fatal, trim(error_header)//'Could not set "' // trim(val_name) // '" for '//trim(method_name)//&
831 ' (I) for '//trim(list_name))
832
833 case (logical_type)
834 if ( fm_new_value( method_name, val_logic, create = .true., index = index_t, append = append_new) < 0 ) &
835 call mpp_error(fatal, trim(error_header)//'Could not set "' // trim(val_name) // '" for '//trim(method_name)//&
836 ' (L) for '//trim(list_name))
837
838 case (real_type)
839 if ( fm_new_value( method_name, val_real, create = .true., index = index_t, append = append_new) < 0 ) &
840 call mpp_error(fatal, trim(error_header)//'Could not set "' // trim(val_name) // '" for '//trim(method_name)//&
841 ' (R) for '//trim(list_name))
842
843 case (string_type)
844 if ( fm_new_value( method_name, val_name, create = .true., index = index_t, append = append_new) < 0 ) &
845 call mpp_error(fatal, trim(error_header)//'Could not set "' // trim(val_name) // '" for '//trim(method_name)//&
846 ' (S) for '//trim(list_name))
847 case default
848 call mpp_error(fatal, trim(error_header)//'Could not find a valid type to set the '//trim(method_name)//&
849 ' for '//trim(list_name))
850
851 end select
852
853enddo
854 deallocate(start_val)
855 deallocate(end_val)
856
857end subroutine new_name_yaml
858#endif
859
860!> @brief Routine to read and parse the field table yaml
861!!
862!> This routine reads from a file containing formatted strings.
863!! These formatted strings contain information on which schemes are
864!! needed within various modules. The field manager does not
865!! initialize any of those schemes however. It simply holds the
866!! information and is queried by the appropriate module.
867subroutine read_field_table_legacy(nfields, table_name)
868
869integer, intent(out), optional :: nfields !< number of fields
870character(len=fm_string_len), intent(in), optional :: table_name !< Name of the field table, default
871 !! is 'field_table'
872
873character(len=1024) :: record
874character(len=fm_string_len) :: control_str
875character(len=FMS_PATH_LEN) :: list_name
876character(len=fm_string_len) :: method_name
877character(len=fm_string_len) :: name_str
878character(len=fm_string_len) :: type_str
879character(len=fm_string_len) :: val_name
880character(len=fm_string_len) :: tbl_name
881integer :: control_array(MAX_FIELDS,3)
882integer :: endcont
883integer :: icount
884integer :: index_list_name
885integer :: iunit
886integer :: l
887integer :: log_unit
888integer :: ltrec
889integer :: m
890integer :: midcont
891integer :: model
892integer :: startcont
893integer :: io_status
894logical :: flag_method
895logical :: fm_success
896type(field_names_type_short) :: text_names_short
897type(field_names_type) :: text_names
898type(method_type_short) :: text_method_short
899type(method_type) :: text_method
900type(method_type_very_short) :: text_method_very_short
901
902if (.not.PRESENT(table_name)) then
903 tbl_name = 'field_table'
904else
905 tbl_name = trim(table_name)
906endif
907if (.not. file_exists(trim(tbl_name))) then
908 if(present(nfields)) nfields = 0
909 return
910endif
911
912allocate(fields(max_fields))
913
914open(newunit=iunit, file=trim(tbl_name), action='READ', iostat=io_status)
915if(io_status/=0) call mpp_error(fatal, 'field_manager_mod: Error in opening file '//trim(tbl_name))
916!write_version_number should precede all writes to stdlog from field_manager
917call write_version_number("FIELD_MANAGER_MOD", version)
918log_unit = stdlog()
919do while (.true.)
920 read(iunit,'(a)',end=89,err=99) record
921 write( log_unit,'(a)' )record
922 if (record(1:1) == "#" ) cycle
923 ltrec = len_trim(record)
924 if (ltrec .le. 0 ) cycle ! Blank line
925
926
927 icount = 0
928 do l= 1, ltrec
929 if (record(l:l) == '"' ) then
930 icount = icount + 1
931 endif
932 enddo
933 if (icount > 6 ) then
934 call mpp_error(fatal,trim(error_header)//'Too many fields in field table header entry.'//trim(record))
935 endif
936
937 select case (icount)
938 case (6)
939 read(record,*,end=79,err=79) text_names
940 text_names%fld_type = lowercase(trim(text_names%fld_type))
941 text_names%mod_name = lowercase(trim(text_names%mod_name))
942 text_names%fld_name = lowercase(trim(text_names%fld_name))
943 case(4)
944! If there is no control string then the last string can be omitted and there are only 4 '"' in the record.
945 read(record,*,end=79,err=79) text_names_short
946 text_names%fld_type = lowercase(trim(text_names_short%fld_type))
947 text_names%mod_name = lowercase(trim(text_names_short%mod_name))
948 text_names%fld_name = lowercase(trim(text_names_short%mod_name))
949 case(2)
950! If there is only the method_type string then the last 2 strings need to be blank and there
951! are only 2 '"' in the record.
952 read(record,*,end=79,err=79) text_names_short
953 text_names%fld_type = lowercase(trim(text_names_short%fld_type))
954 text_names%mod_name = lowercase(trim(text_names_short%mod_name))
955 text_names%fld_name = lowercase(trim(text_names_short%mod_name))
956 case default
957! There is an unterminated or unquoted string in the field table entry.
958 text_names%fld_type = " "
959 text_names%mod_name = lowercase(trim(record))
960 text_names%fld_name = " "
961 end select
962
963! Create a list with Rick Slaters field manager code
964
965 list_name = list_sep//trim(text_names%mod_name)//list_sep//trim(text_names%fld_type)//&
966 list_sep//trim(text_names%fld_name)
967
968 index_list_name = fm_new_list(list_name, create = .true.)
969 if ( index_list_name == no_field ) &
970 call mpp_error(fatal, trim(error_header)//'Could not set field list for '//trim(list_name))
971
972 fm_success = fm_change_list(list_name)
973 select case (text_names%mod_name)
974 case ('coupler_mod')
975 model = model_coupler
976 case ('atmos_mod')
977 model = model_atmos
978 case ('ocean_mod')
979 model = model_ocean
980 case ('land_mod')
981 model = model_land
982 case ('ice_mod')
983 model = model_ice
984 case default
985 call mpp_error(fatal, trim(error_header)//'The model name is unrecognised : '//trim(text_names%mod_name))
986 end select
987 if (find_field_index(list_name) > 0) then
988 num_fields = num_fields + 1
989
990 if (num_fields > max_fields) call mpp_error(fatal,trim(error_header)//'max fields exceeded')
991 fields(num_fields)%model = model
992 fields(num_fields)%field_name = lowercase(trim(text_names%fld_name))
993 fields(num_fields)%field_type = lowercase(trim(text_names%fld_type))
994 fields(num_fields)%num_methods = 0
995 allocate(fields(num_fields)%methods(max_field_methods))
996 call check_for_name_duplication
997
998! Check to see that the first line is not the only line
999 if ( record(len_trim(record):len_trim(record)) == list_sep) cycle
1000
1001 flag_method = .true.
1002 m = 1
1003 do while (flag_method)
1004 read(iunit,'(a)',end=99,err=99) record
1005! If the line is blank then fetch the next line.
1006 if (len_trim(record) .le. 0) cycle
1007! If the last character in the line is / then this is the end of the field methods
1008 if ( record(len_trim(record):len_trim(record)) == list_sep) then
1009 flag_method = .false.
1010 if (len_trim(record) == 1) cycle
1011 record = record(:len_trim(record)-1) ! Remove the end of field method marker
1012 endif
1013! If the line is now blank, after removing the field separator marker, then fetch the next line.
1014 if (len_trim(record) .le. 0) cycle
1015! If the first character in the line is # then it is treated as a comment
1016 if (record(1:1) == comment ) cycle
1017
1018 icount = 0
1019 do l= 1, len_trim(record)
1020 if (record(l:l) == dquote ) then
1021 icount = icount + 1
1022 endif
1023 enddo
1024 if (icount > 6 ) call mpp_error(fatal,trim(error_header)//'Too many fields in field entry.'//trim(record))
1025
1026 if (.not. fm_change_list( list_name)) &
1027 call mpp_error(fatal, trim(error_header)//'Could not change to '//trim(list_name)//' list')
1028
1029 select case (icount)
1030 case (6)
1031 read(record,*,end=99,err=99) text_method
1032 fields(num_fields)%methods(m)%method_type = lowercase(trim(text_method%method_type))
1033 fields(num_fields)%methods(m)%method_name = lowercase(trim(text_method%method_name))
1034 fields(num_fields)%methods(m)%method_control = lowercase(trim(text_method%method_control))
1035
1036 type_str = text_method%method_type
1037 name_str = text_method%method_name
1038 control_str = text_method%method_control
1039
1040 case(4)
1041! If there is no control string then the last string can be omitted and there are only 4 '"' in the record.
1042 read(record,*,end=99,err=99) text_method_short
1043 fields(num_fields)%methods(m)%method_type =&
1044 & lowercase(trim(text_method_short%method_type))
1045 fields(num_fields)%methods(m)%method_name =&
1046 & lowercase(trim(text_method_short%method_name))
1047 fields(num_fields)%methods(m)%method_control = " "
1048
1049 type_str = text_method_short%method_type
1050 name_str = ""
1051 control_str = text_method_short%method_name
1052
1053 case(2)
1054! If there is only the method_type string then the last 2 strings need to be blank and there
1055! are only 2 '"' in the record.
1056 read(record,*,end=99,err=99) text_method_very_short
1057 fields(num_fields)%methods(m)%method_type = lowercase(trim(text_method_very_short%method_type))
1058 fields(num_fields)%methods(m)%method_name = " "
1059 fields(num_fields)%methods(m)%method_control = " "
1060
1061 type_str = ""
1062 name_str = ""
1063 control_str = text_method_very_short%method_type
1064
1065 case(0)
1066 read(record,'(A)',end=99,err=99) control_str
1067 type_str = ""
1068 name_str = ""
1069
1070 case default
1071 call mpp_error(fatal,trim(error_header)//'Unterminated field in field entry.'//trim(record))
1072 end select
1073
1074! This section of code breaks the control string into separate strings.
1075! The array control_array contains the following parameters.
1076! control_array(:,1) = index within control_str of the first character of the name.
1077! control_array(:,2) = index within control_str of the equal sign
1078! control_array(:,3) = index within control_str of the last character of the value.
1079!
1080! control_array(:,1) -> control_array(:,2) -1 = name of the parameter.
1081! control_array(:,2)+1 -> control_array(:,3) = value of the parameter.
1082
1083 ltrec= len_trim(control_str)
1084 control_array(:,1) = 1
1085 control_array(:,2:3) = ltrec
1086 icount = 0
1087 do l= 1, ltrec
1088 if (control_str(l:l) == equal ) then
1089 icount = icount + 1
1090 control_array(icount,2) = l ! Middle of string
1091 elseif (control_str(l:l) == comma ) then
1092 if (icount .eq. 0) then
1093 call mpp_error(fatal,trim(error_header) // &
1094 ' Bad format for field entry (comma without equals sign): ''' // &
1095 trim(control_str) // '''')
1096 elseif (icount .gt. max_fields) then
1097 call mpp_error(fatal,trim(error_header) // &
1098 ' Too many fields in field entry: ''' // &
1099 trim(control_str) // '''')
1100 else
1101 control_array(icount,3) = l-1 !End of previous string
1102 control_array(min(max_fields,icount+1),1) = l+1 !Start of next string
1103 endif
1104 endif
1105 enddo
1106
1107 ! Make sure that we point to the end of the string (minus any trailing comma)
1108 ! for the last set of values. This fixes the case where the last set of values
1109 ! is a comma separated list
1110
1111 if (control_str(ltrec:ltrec) .ne. comma) then
1112 control_array(max(1,icount),3) = ltrec
1113 endif
1114
1115 if ( icount == 0 ) then
1116 method_name = type_str
1117 if (len_trim(method_name) > 0 ) then
1118 method_name = trim(method_name)//list_sep// trim(name_str)
1119 else
1120 method_name = trim(name_str)
1121 endif
1122 val_name = control_str
1123
1124 call new_name(list_name, method_name, val_name )
1125
1126 else
1127
1128 do l = 1,icount
1129 startcont = control_array(l,1)
1130 midcont = control_array(l,2)
1131 endcont = control_array(l,3)
1132
1133 method_name = trim(type_str)
1134 if (len_trim(method_name) > 0 ) then
1135 method_name = trim(method_name)//list_sep// trim(name_str)
1136 else
1137 method_name = trim(name_str)
1138 endif
1139
1140 if (len_trim(method_name) > 0 ) then
1141 method_name = trim(method_name)//list_sep//&
1142 trim(control_str(startcont:midcont-1))
1143 else
1144 method_name = trim(control_str(startcont:midcont-1))
1145 endif
1146 val_name = trim(control_str(midcont+1:endcont))
1147
1148 call new_name(list_name, method_name, val_name )
1149 enddo
1150
1151 endif
1152
1153 fields(num_fields)%num_methods = fields(num_fields)%num_methods + 1
1154 if (fields(num_fields)%num_methods > max_field_methods) &
1155 call mpp_error(fatal,trim(error_header)//'Maximum number of methods for field exceeded')
1156 m = m + 1
1157 enddo
1158 else
1159 flag_method = .true.
1160 do while (flag_method)
1161 read(iunit,'(A)',end=99,err=99) record
1162 if ( record(len_trim(record):len_trim(record)) == list_sep) then
1163 flag_method = .false.
1164 endif
1165 enddo
1166 endif
116779 continue
1168enddo
1169
117089 continue
1171close(iunit, iostat=io_status)
1172if(io_status/=0) call mpp_error(fatal, 'field_manager_mod: Error in closing file '//trim(tbl_name))
1173
1174
1175if(present(nfields)) nfields = num_fields
1176
1177default_method%method_type = 'none'
1178default_method%method_name = 'none'
1179default_method%method_control = 'none'
1180return
1181
118299 continue
1183
1184call mpp_error(fatal,trim(error_header)//' Error reading field table. Record = '//trim(record))
1185
1186end subroutine read_field_table_legacy
1187
1188subroutine check_for_name_duplication
1189integer :: i
1190
1191! Check that name is unique amoung fields of the same field_type and model.
1192do i=1,num_fields-1
1193 if ( fields(i)%field_type == fields(num_fields)%field_type .and. &
1194 fields(i)%model == fields(num_fields)%model .and. &
1195 fields(i)%field_name == fields(num_fields)%field_name ) then
1196 if (mpp_pe() .eq. mpp_root_pe()) then
1197 call mpp_error(warning,'Error in field_manager_mod. Duplicate field name: Field type='//&
1198 trim(fields(i)%field_type)// &
1199 ', Model='//trim(model_names(fields(i)%model))// &
1200 ', Duplicated name='//trim(fields(i)%field_name))
1201 endif
1202 endif
1203enddo
1204
1205end subroutine check_for_name_duplication
1206
1207!> @brief Subroutine to add new values to list parameters.
1208!!
1209!> This subroutine uses input strings list_name, method_name
1210!! and val_name_in to add new values to the list. Given
1211!! list_name a new list item is created that is named
1212!! method_name and is given the value or values in
1213!! val_name_in. If there is more than 1 value in
1214!! val_name_in, these values should be comma-separated.
1215subroutine new_name ( list_name, method_name_in , val_name_in)
1216character(len=*), intent(in) :: list_name !< The name of the field that is of interest here.
1217character(len=*), intent(in) :: method_name_in !< The name of the method that values are
1218 !! being supplied for.
1219character(len=*), intent(inout) :: val_name_in !< The value or values that will be parsed and
1220 !! used as the value when creating a new field or fields.
1221
1222character(len=fm_string_len) :: method_name
1223character(len=fm_string_len) :: val_name
1224integer, dimension(MAX_FIELDS) :: end_val
1225integer, dimension(MAX_FIELDS) :: start_val
1226integer :: i
1227integer :: index_t
1228integer :: left_br
1229integer :: num_elem
1230integer :: out_unit
1231integer :: right_br
1232integer :: val_int
1233integer :: val_type
1234logical :: append_new
1235logical :: val_logic
1236real(r8_kind) :: val_real !< all reals converted from string will be in r8_kind precision
1237integer :: length
1238
1239call strip_front_blanks(val_name_in)
1240method_name = trim(method_name_in)
1241call strip_front_blanks(method_name)
1242
1243index_t = 1
1244num_elem = 1
1245append_new = .false.
1246start_val(1) = 1
1247end_val(:) = len_trim(val_name_in)
1248
1249! If the array of values being passed in is a comma delimited list then count
1250! the number of elements.
1251
1252do i = 1, len_trim(val_name_in)
1253 if ( val_name_in(i:i) == comma ) then
1254 end_val(num_elem) = i-1
1255 start_val(num_elem+1) = i+1
1256 num_elem = num_elem + 1
1257 endif
1258enddo
1259
1260! Check to see if this is an array element of form array[x] = value
1261left_br = scan(method_name,'[')
1262right_br = scan(method_name,']')
1263if ( num_elem .eq. 1 ) then
1264 if ( left_br > 0 .and. right_br == 0 ) &
1265 call mpp_error(fatal, trim(error_header)//"Left bracket present without right bracket in "//trim(method_name))
1266 if ( left_br== 0 .and. right_br > 0 ) &
1267 call mpp_error(fatal, trim(error_header)//"Right bracket present without left bracket in "//trim(method_name))
1268 if ( left_br > 0 .and. right_br > 0 ) then
1269 if ( scan( method_name(left_br+1:right_br -1), set ) > 0 ) &
1270 call mpp_error(fatal, trim(error_header)//"Using a non-numeric value for index in "//trim(method_name))
1271 read(method_name(left_br+1:right_br -1), *) index_t
1272 method_name = method_name(:left_br -1)
1273 endif
1274else
1275! If there are multiple values then there cannot be a bracket in method_name.
1276 if ( left_br > 0 .or. right_br > 0 ) &
1277 call mpp_error(fatal, &
1278 trim(error_header)//"Using a comma delimited list with an indexed array element in "//trim(method_name))
1279endif
1280
1281do i = 1, num_elem
1282
1283 if ( i .gt. 1 .or. index_t .eq. 0 ) then
1284 append_new = .true.
1285 index_t = 0 ! If append is true then index must be <= 0
1286 endif
1287 val_type = string_type ! Assume it is a string
1288 val_name = val_name_in(start_val(i):end_val(i))
1289 call strip_front_blanks(val_name)
1290
1291!
1292! if the string starts and ends with matching single quotes, then this is a string
1293! if there are quotes which do not match, then this is an error
1294!
1295
1296 length = len_trim(val_name)
1297 if (val_name(1:1) .eq. squote) then
1298
1299 if (val_name(length:length) .eq. squote) then
1300 val_name = val_name(2:length-1)//repeat(" ",len(val_name)-length+2)
1301 val_type = string_type
1302 elseif (val_name(length:length) .eq. dquote) then
1303 call mpp_error(fatal, trim(error_header) // ' Quotes do not match in ' // trim(val_name) // &
1304 ' for ' // trim(method_name) // ' of ' // trim(list_name))
1305 else
1306 call mpp_error(fatal, trim(error_header) // ' No trailing quote in ' // trim(val_name) // &
1307 ' for ' // trim(method_name) // ' of ' // trim(list_name))
1308 endif
1309
1310 elseif (val_name(1:1) .eq. dquote .or. val_name(length:length) .eq. dquote) then
1311
1312 call mpp_error(fatal, trim(error_header) // ' Double quotes not allowed in ' // trim(val_name) // &
1313 ' for ' // trim(method_name) // ' of ' // trim(list_name))
1314
1315 elseif (val_name(length:length) .eq. squote) then
1316
1317 call mpp_error(fatal, trim(error_header) // ' No leading quote in ' // trim(val_name) // &
1318 ' for ' // trim(method_name) // ' of ' // trim(list_name))
1319
1320 else
1321! If the string to be parsed is a real then all the characters must be numeric,
1322! be a plus/minus, be a decimal point or, for exponentials, be e or E.
1323
1324! If a string is an integer, then all the characters must be numeric.
1325
1326 if ( scan(val_name(1:1), setnum ) > 0 ) then
1327
1328! If there is a letter in the name it may only be e or E
1329
1330 if ( scan(val_name, set_nonexp ) .le. 0 ) then
1331! It is real if there is a . in the name or the value appears exponential
1332 if ( scan(val_name, '.') > 0 .or. scan(val_name, 'e') > 0 .or. scan(val_name, 'E') > 0) then
1333 read(val_name, *) val_real
1334 val_type = real_type
1335 else
1336 read(val_name, *) val_int
1337 val_type = integer_type
1338 endif
1339 endif
1340
1341 endif
1342
1343! If val_name is t/T or f/F then this is a logical flag.
1344 if ( len_trim(val_name) == 1 .or. len_trim(val_name) == 3) then
1345 if ( val_name == 't' .or. val_name == 'T' .or. val_name == '.t.' .or. val_name == '.T.' ) then
1346 val_logic = .true.
1347 val_type = logical_type
1348 endif
1349 if ( val_name == 'f' .or. val_name == 'F' .or. val_name == '.f.' .or. val_name == '.F.' ) then
1350 val_logic = .false.
1351 val_type = logical_type
1352 endif
1353 endif
1354 if ( trim(lowercase(val_name)) == 'true' .or. trim(lowercase(val_name)) == '.true.' ) then
1355 val_logic = .true.
1356 val_type = logical_type
1357 endif
1358 if ( trim(lowercase(val_name)) == 'false' .or. trim(lowercase(val_name)) == '.false.' ) then
1359 val_logic = .false.
1360 val_type = logical_type
1361 endif
1362 endif
1363
1364 select case(val_type)
1365
1366 case (integer_type)
1367 if ( fm_new_value( method_name, val_int, create = .true., index = index_t, append = append_new ) < 0 ) &
1368 call mpp_error(fatal, trim(error_header)//'Could not set "' // trim(val_name) // '" for '//trim(method_name)//&
1369 ' (I) for '//trim(list_name))
1370
1371 case (logical_type)
1372 if ( fm_new_value( method_name, val_logic, create = .true., index = index_t, append = append_new) < 0 ) &
1373 call mpp_error(fatal, trim(error_header)//'Could not set "' // trim(val_name) // '" for '//trim(method_name)//&
1374 ' (L) for '//trim(list_name))
1375
1376 case (real_type)
1377 if ( fm_new_value( method_name, val_real, create = .true., index = index_t, append = append_new) < 0 ) &
1378 call mpp_error(fatal, trim(error_header)//'Could not set "' // trim(val_name) // '" for '//trim(method_name)//&
1379 ' (R) for '//trim(list_name))
1380
1381 case (string_type)
1382 if ( fm_new_value( method_name, val_name, create = .true., index = index_t, append = append_new) < 0 ) &
1383 call mpp_error(fatal, trim(error_header)//'Could not set "' // trim(val_name) // '" for '//trim(method_name)//&
1384 ' (S) for '//trim(list_name))
1385 case default
1386 call mpp_error(fatal, trim(error_header)//'Could not find a valid type to set the '//trim(method_name)//&
1387 ' for '//trim(list_name))
1388
1389 end select
1390
1391enddo
1392
1393end subroutine new_name
1394
1395!> @brief Destructor for field manager.
1396!!
1397!> This subroutine deallocates allocated variables (if allocated) and
1398!! changes the initialized flag to false.
1400integer :: j
1401
1402module_is_initialized = .false.
1403
1404do j=1,size(fields)
1405 if(allocated(fields(j)%methods)) deallocate(fields(j)%methods)
1406end do
1407if(allocated(fields)) deallocate(fields)
1408
1409end subroutine field_manager_end
1410
1411!> @brief A routine to strip whitespace from the start of character strings.
1412!!
1413!> This subroutine removes spaces and tabs from the start of a character string.
1414subroutine strip_front_blanks(name)
1415
1416character(len=*), intent(inout) :: name !< name to remove whitespace from
1417
1418name = trim(adjustl(name))
1419end subroutine strip_front_blanks
1420
1421!> @brief Function to return the index of the field
1422!!
1423!> This function when passed a model number and a field name will
1424!! return the index of the field within the field manager. This index
1425!! can be used to access other information from the field manager.
1426!! @returns The index of the field corresponding to field_name.
1427function find_field_index_old(model, field_name)
1428
1429integer :: find_field_index_old
1430integer, intent(in) :: model !< The number indicating which model is used.
1431character(len=*), intent(in) :: field_name !< The name of the field that an index is being requested for.
1432
1433integer :: i
1434
1436
1437do i=1,num_fields
1438 if (fields(i)%model == model .and. fields(i)%field_name == lowercase(field_name)) then
1440 return
1441 endif
1442enddo
1443
1444end function find_field_index_old
1445
1446!> @returns index of the field corresponding to field_name
1447function find_field_index_new(field_name)
1448
1449integer :: find_field_index_new
1450character(len=*), intent(in) :: field_name !< The path to the name of the field that an index is
1451 !! being requested for.
1452
1454
1456
1457end function find_field_index_new
1458
1459!> @brief This routine allows access to field information given an index.
1460!!
1461!> When passed an index, this routine will return the type of field,
1462!! the name of the field, the model which the field is associated and
1463!! the number of methods associated with the field.
1464!! <br>Example usage:
1465!! @code{.F90}
1466!! call get_field_info( n,fld_type,fld_name,model,num_methods )
1467!! @endcode
1468subroutine get_field_info(n,fld_type,fld_name,model,num_methods)
1469integer, intent(in) :: n !< index of field
1470character (len=*),intent(out) :: fld_type !< field type
1471character (len=*),intent(out) :: fld_name !< name of the field
1472integer, intent(out) :: model !< number indicating which model is used
1473integer, intent(out) :: num_methods !< number of methods
1474
1475if (n < 1 .or. n > num_fields) call mpp_error(fatal,trim(error_header)//'Invalid field index')
1476
1477fld_type = fields(n)%field_type
1478fld_name = fields(n)%field_name
1479model = fields(n)%model
1480num_methods = fields(n)%num_methods
1481
1482end subroutine get_field_info
1483
1484!> @brief A routine to get a specified method
1485!!
1486!> This routine, when passed a field index and a method index will
1487!! return the method text associated with the field(n) method(m).
1488subroutine get_field_method(n,m,method)
1489
1490integer, intent(in) :: n !< index of field
1491integer, intent(in) :: m !< index of method
1492type(method_type) ,intent(inout) :: method !< the m-th method of field with index n
1493
1494if (n < 1 .or. n > num_fields) call mpp_error(fatal,trim(error_header)//'Invalid field index')
1495if (m < 1 .or. m > fields(n)%num_methods) call mpp_error(fatal,trim(error_header)//'Invalid method index')
1496
1497 method = fields(n)%methods(m)
1498
1499end subroutine get_field_method
1500
1501!> @brief A routine to obtain all the methods associated with a field.
1502!!
1503!> When passed a field index, this routine will return the text
1504!! associated with all the methods attached to the field.
1505subroutine get_field_methods(n,methods)
1506
1507integer, intent(in) :: n !< field index
1508type(method_type),intent(inout) :: methods(:) !< an array of methods for field with index n
1509
1510 if (n < 1 .or. n > num_fields) &
1511 call mpp_error(fatal,trim(error_header)//'Invalid field index')
1512
1513 if (size(methods(:)) < fields(n)%num_methods) &
1514 call mpp_error(fatal,trim(error_header)//'Method array too small')
1515
1516 methods = default_method
1517 methods(1:fields(n)%num_methods) = fields(n)%methods(1:fields(n)%num_methods)
1518
1519end subroutine get_field_methods
1520
1521!> @returns The number of values that have been decoded. This allows
1522!! a user to define a large array and fill it partially with
1523!! values from a list. This should be the size of the value array.
1524function parse_integers ( text, label, values ) result (parse)
1525character(len=*), intent(in) :: text !< The text string from which the values will be parsed.
1526character(len=*), intent(in) :: label !< A label which describes the values being decoded.
1527integer, intent(out) :: values(:) !< The value or values that have been decoded.
1528
1529include 'parse.inc'
1530end function parse_integers
1531
1532function parse_strings ( text, label, values ) result (parse)
1533character(len=*), intent(in) :: text !< The text string from which the values will be parsed.
1534character(len=*), intent(in) :: label !< A label which describes the values being decoded.
1535character(len=*), intent(out) :: values(:) !< The value or values that have been decoded.
1536
1537include 'parse.inc'
1538end function parse_strings
1539
1540function parse_integer ( text, label, parse_ival ) result (parse)
1541character(len=*), intent(in) :: text !< The text string from which the values will be parsed.
1542character(len=*), intent(in) :: label !< A label which describes the values being decoded.
1543integer, intent(out) :: parse_ival !< The value or values that have been decoded.
1544integer :: parse
1545
1546integer :: values(1)
1547
1548 parse = parse_integers( text, label, values )
1549 if (parse > 0) parse_ival = values(1)
1550end function parse_integer
1551
1552function parse_string ( text, label, parse_sval ) result (parse)
1553character(len=*), intent(in) :: text !< The text string from which the values will be parsed.
1554character(len=*), intent(in) :: label !< A label which describes the values being decoded.
1555character(len=*), intent(out) :: parse_sval !< The value or values that have been decoded.
1556integer :: parse
1557
1558character(len=len(parse_sval)) :: values(1)
1559
1560 parse = parse_strings( text, label, values )
1561 if (parse > 0) parse_sval = values(1)
1562end function parse_string
1563
1564!> @brief A function to create a field as a child of parent_p. This will return
1565!! a pointer to a field_def type.
1566!!
1567!> Allocate and initialize a new field in parent_p list.
1568!! Return a pointer to the field on success, or a null pointer
1569!! on failure.
1570!! <br>Example usage:
1571!! @code{.F90}
1572!! list_p => create_field(parent_p, name)
1573!! @endcode
1574function create_field(parent_p, name) &
1575 result(list_p)
1576type (field_def), pointer :: list_p
1577type (field_def), pointer :: parent_p !< A pointer to the parent of the field that is to be created
1578character(len=*), intent(in) :: name !< The name of the field that is to be created
1579
1580integer :: error, out_unit
1581! Check for fatal errors which should never arise
1582out_unit = stdout()
1583if (.not. associated(parent_p) .or. name .eq. ' ') then
1584 nullify(list_p)
1585 return
1586endif
1587
1588! Allocate space for the new list
1589allocate(list_p, stat = error)
1590if (error .ne. 0) then
1591 write (out_unit,*) trim(error_header), 'Error ', error, &
1592 ' allocating memory for list ', trim(name)
1593 nullify(list_p)
1594 return
1595endif
1596! Initialize the new field
1597list_p%name = name
1598
1599nullify(list_p%next)
1600list_p%prev => parent_p%last_field
1601nullify(list_p%first_field)
1602nullify(list_p%last_field)
1603list_p%length = 0
1604list_p%field_type = null_type
1605list_p%max_index = 0
1606list_p%array_dim = 0
1607if (allocated(list_p%i_value)) deallocate(list_p%i_value)
1608if (allocated(list_p%l_value)) deallocate(list_p%l_value)
1609if (allocated(list_p%r_value)) deallocate(list_p%r_value)
1610if (allocated(list_p%s_value)) deallocate(list_p%s_value)
1611! If this is the first field in the parent, then set the pointer
1612! to it, otherwise, update the "next" pointer for the last list
1613if (parent_p%length .le. 0) then
1614 parent_p%first_field => list_p
1615else
1616 parent_p%last_field%next => list_p
1617endif
1618! Update the pointer for the last list in the parent
1619parent_p%last_field => list_p
1620! Update the length for the parent
1621parent_p%length = parent_p%length + 1
1622! Set the new index as the return value
1623list_p%index = parent_p%length
1624! set the pointer to the parent list
1625list_p%parent => parent_p
1626
1627end function create_field
1628
1629!> @brief This is a function that lists the parameters of a field.
1630!!
1631!> Given a pointer to a list, this function prints out the fields, and
1632!! subfields, if recursive is true, associated with the list.
1633!!
1634!! This is most likely to be used through fm_dump_list.
1635!! <br> Example usage:
1636!! @code{.F90}
1637!! success = dump_list(list_p, recursive=.true., depth=0)
1638!! @endcode
1639logical recursive function dump_list(list_p, recursive, depth, out_unit) result(success)
1640
1641 type (field_def), pointer :: list_p !< pointer to the field to be printed out
1642 logical, intent(in) :: recursive !< flag to make function recursively print subfields
1643 integer, intent(in) :: depth !< Listing will be padded so that 'depth' spaces appear before
1644 !! the field being printed
1645 integer, intent(in) :: out_unit !< unit number to print to
1646
1647 integer :: depthp1
1648 integer :: j
1649 character(len=fm_field_name_len) :: num, scratch
1650 type (field_def), pointer :: this_field_p
1651 character(len=depth+fm_field_name_len) :: blank
1652
1653 blank = ' ' ! initialize blank string
1654
1655 ! Check for a valid list
1656 success = .false.
1657 if (.not. associated(list_p)) then
1658 return
1659 elseif (list_p%field_type .ne. list_type) then
1660 return
1661 endif
1662
1663 ! set the default return value
1664 success = .true.
1665
1666 ! Print the name of this list
1667 write (out_unit,'(a,a,a)') blank(1:depth), trim(list_p%name), list_sep
1668
1669 ! Increment the indentation depth
1670 ! The following max function is to work around an error in the IBM compiler for len_trim
1671 ! depthp1 = depth + max(len_trim(list_p%name),0) + len_trim(list_sep)
1672 depthp1 = depth + 6
1673
1674 this_field_p => list_p%first_field
1675
1676 do while (associated(this_field_p))
1677
1678 select case(this_field_p%field_type)
1679 case(list_type)
1680 ! If this is a list, then call dump_list
1681 if (recursive) then
1682 ! If recursive is true, then this routine will find and dump sub-fields.
1683 success = dump_list(this_field_p, .true., depthp1, out_unit)
1684 if (.not.success) exit ! quit immediately in case of error
1685 else ! Otherwise it will print out the name of this field.
1686 write (out_unit,'(a,a,a)') blank(1:depthp1), trim(this_field_p%name), list_sep
1687 endif
1688
1689 case(integer_type)
1690 if (this_field_p%max_index .eq. 0) then
1691 write (out_unit,'(a,a,a)') blank(1:depthp1), trim(this_field_p%name), ' = NULL'
1692 elseif (this_field_p%max_index .eq. 1) then
1693 write (scratch,*) this_field_p%i_value(1)
1694 write (out_unit,'(a,a,a,a)') blank(1:depthp1), trim(this_field_p%name), ' = ', &
1695 trim(adjustl(scratch))
1696 else ! Write out the array of values for this field.
1697 do j = 1, this_field_p%max_index
1698 write (scratch,*) this_field_p%i_value(j)
1699 write (num,*) j
1700 write (out_unit,'(a,a,a,a,a,a)') blank(1:depthp1), trim(this_field_p%name), &
1701 '[', trim(adjustl(num)), '] = ', trim(adjustl(scratch))
1702 enddo
1703 endif
1704
1705 case(logical_type)
1706 if (this_field_p%max_index .eq. 0) then
1707 write (out_unit,'(a,a,a)') blank(1:depthp1), trim(this_field_p%name), ' = NULL'
1708 elseif (this_field_p%max_index .eq. 1) then
1709 write (scratch,'(l1)') this_field_p%l_value(1)
1710 write (out_unit,'(a,a,a,a)') blank(1:depthp1), trim(this_field_p%name), ' = ', &
1711 trim(adjustl(scratch))
1712 else ! Write out the array of values for this field.
1713 do j = 1, this_field_p%max_index
1714 write (scratch,'(l1)') this_field_p%l_value(j)
1715 write (num,*) j
1716 write (out_unit,'(a,a,a,a,a,a)') blank(1:depthp1), trim(this_field_p%name), &
1717 '[', trim(adjustl(num)), '] = ', trim(adjustl(scratch))
1718 enddo
1719 endif
1720
1721 case(real_type)
1722 if (this_field_p%max_index .eq. 0) then
1723 write (out_unit,'(a,a,a)') blank(1:depthp1), trim(this_field_p%name), ' = NULL'
1724 elseif (this_field_p%max_index .eq. 1) then
1725 write (scratch,*) this_field_p%r_value(1)
1726 write (out_unit,'(a,a,a,a)') blank(1:depthp1), trim(this_field_p%name), ' = ', &
1727 trim(adjustl(scratch))
1728 else ! Write out the array of values for this field.
1729 do j = 1, this_field_p%max_index
1730 write (scratch,*) this_field_p%r_value(j)
1731 write (num,*) j
1732 write (out_unit,'(a,a,a,a,a,a)') blank(1:depthp1), trim(this_field_p%name), &
1733 '[', trim(adjustl(num)), '] = ', trim(adjustl(scratch))
1734 end do
1735 endif
1736
1737 case(string_type)
1738 if (this_field_p%max_index .eq. 0) then
1739 write (out_unit,'(a,a,a)') blank(1:depthp1), trim(this_field_p%name), ' = NULL'
1740 elseif (this_field_p%max_index .eq. 1) then
1741 write (out_unit,'(a,a,a,a)') blank(1:depthp1), trim(this_field_p%name), ' = ', &
1742 ''''//trim(this_field_p%s_value(1))//''''
1743 else ! Write out the array of values for this field.
1744 do j = 1, this_field_p%max_index
1745 write (num,*) j
1746 write (out_unit,'(a,a,a,a,a,a)') blank(1:depthp1), trim(this_field_p%name), &
1747 '[', trim(adjustl(num)), '] = ', ''''//trim(this_field_p%s_value(j))//''''
1748 enddo
1749 endif
1750
1751 case default
1752 success = .false.
1753 exit
1754
1755 end select
1756
1757 this_field_p => this_field_p%next
1758 enddo
1759
1760end function dump_list
1761
1762!> @brief A subroutine that splits a listname into a path and a base.
1763!!
1764!> Find the base name for a list by splitting the list name into
1765!! a path and base. The base is the last field within name, while the
1766!! path is the preceding section of name. The base string can then be
1767!! used to query for values associated with name.
1768subroutine find_base(name, path, base)
1769
1770character(len=*), intent(in) :: name !< list name for a field
1771character(len=*), intent(out) :: path !< path of the base field
1772character(len=*), intent(out) :: base !< A string which can be used to query for values associated with name
1773
1774integer :: i
1775integer :: length
1776
1777! Check for the last occurrence of the list separator in name
1778! The following max function is to work around an error in the IBM compiler for len_trim
1779length = max(len_trim(name),0)
1780if (length .eq. 0) then
1781
1782 ! Empty name, so return empty path and base
1783 path = ' '
1784 base = ' '
1785else
1786 ! Remove trailing list separators
1787 do while (name(length:length) .eq. list_sep)
1788 length = length - 1
1789 if (length .eq. 0) then
1790 exit
1791 endif
1792 enddo
1793 if (length .eq. 0) then
1794
1795 ! Name only list separators, so return empty path and base
1796 path = ' '
1797 base = ' '
1798 else
1799 ! Check for the last occurrence of the list separator in name
1800 i = index(name(1:length), list_sep, back = .true.)
1801 if (i .eq. 0) then
1802 ! no list separators in the path, so return an empty path
1803 ! and name as the base
1804 path = ' '
1805 base = name(1:length)
1806 else
1807 ! Found a list separator, so return the part up to the last
1808 ! list separator in path, and the remainder in base
1809 path = name(1:i)
1810 base = name(i+1:length)
1811 endif
1812 endif
1813endif
1814
1815end subroutine find_base
1816
1817!> @brief Find and return a pointer to the field in the specified
1818!! list. Return a null pointer on error.
1819!!
1820!> Find and return a pointer to the field in the specified
1821!! list. Return a null pointer on error. Given a pointer to a field,
1822!! this function searchs for "name" as a sub field.
1823!> @returns A pointer to the field corresponding to "name" or an unassociated pointer if the field
1824!! name does not exist.
1825function find_field(name, this_list_p) &
1826 result(field_p)
1827type (field_def), pointer :: field_p
1828character(len=*), intent(in) :: name !< The name of a field that the user wishes to find
1829type (field_def), pointer :: this_list_p !< A pointer to a list which the user wishes to search
1830 !! for a field "name".
1831
1832type (field_def), pointer, save :: temp_p
1833
1834
1835nullify (field_p)
1836
1837if (name .eq. '.') then
1838
1839! If the field is '.' then return this list
1840 field_p => this_list_p
1841elseif (name .eq. '..') then
1842! If the field is '..' then return the parent list
1843 field_p => this_list_p%parent
1844else
1845! Loop over each field in this list
1846 temp_p => this_list_p%first_field
1847
1848 do while (associated(temp_p))
1849! If the name matches, then set the return pointer and exit
1850! the loop
1851 if (temp_p%name .eq. name) then
1852 field_p => temp_p
1853 exit
1854 endif
1855
1856 temp_p => temp_p%next
1857
1858 enddo
1859endif
1860
1861end function find_field
1862
1863!> @brief Find the first list for a name by splitting the name into
1864!! a head and the rest.
1865!!
1866!> Find the first list for a name by splitting the name into a head and the
1867!! rest. The head is the first field within name, while rest is the remaining
1868!! section of name. The head string can then be used to find other fields that
1869!! may be associated with name.
1870subroutine find_head(name, head, rest)
1871
1872character(len=*), intent(in) :: name !< The name of a field of interest
1873character(len=*), intent(out) :: head !< the first field within name
1874character(len=*), intent(out) :: rest !< the remaining section of name
1875
1876integer :: i
1877! Check for the first occurrence of the list separator in name
1878i = index(name, list_sep)
1879! Check for additional consecutive list separators and return
1880! those also
1881do while (i .le. len(name))
1882 if (name(i+1:i+1) .eq. list_sep) then
1883 i = i + 1
1884 else
1885 exit
1886 endif
1887enddo
1888
1889if (i .eq. 0) then
1890! no list separators in the path, so return an empty head and
1891! name as the rest
1892 head = ' '
1893 rest = name
1894elseif (i .eq. len(name)) then
1895! The last character in name is a list separator, so return name
1896! as head and an empty rest
1897 head = name
1898 rest = ' '
1899else
1900! Found a list separator, so return the part up to the list
1901! separator in head, and the remainder in rest
1902 head = name(1:i)
1903 rest = name(i+1:)
1904endif
1905
1906end subroutine find_head
1907
1908!> @brief Find and return a pointer to the specified list, relative to
1909!! relative_p. Return a null pointer on error.
1910!!
1911!> This function, when supplied a pointer to a field and a name of a second
1912!! field relative to that pointer, will find a list and return the pointer to
1913!! the second field. If create is .true. and the second field does not exist,
1914!! it will be created.
1915!> @returns A pointer to the list to be returned
1916function find_list(path, relative_p, create) &
1917 result(list_p)
1918type (field_def), pointer :: list_p
1919character(len=*), intent(in) :: path !< path to the list of interest
1920type (field_def), pointer :: relative_p !< pointer to the list to which "path" is relative to
1921logical, intent(in) :: create !< If the list does not exist, it will be created if set to true
1922
1923character(len=FMS_PATH_LEN) :: working_path
1924character(len=FMS_PATH_LEN) :: rest
1925character(len=fm_field_name_len) :: this_list
1926integer :: i, out_unit
1927type (field_def), pointer, save :: working_path_p
1928type (field_def), pointer, save :: this_list_p
1929
1930out_unit = stdout()
1931nullify(list_p)
1932! If the path is empty, then return the relative list
1933if (path .eq. ' ') then
1934
1935 list_p => relative_p
1936
1937else
1938! If a fully qualified path is given (i.e., starts with the
1939! list separator) then do everything relative to root,
1940! otherwise, do everything relative to relative list.
1941 if (path(1:1) .eq. list_sep) then
1942 working_path_p => root_p
1943 working_path = path(2:)
1944 else
1945 working_path_p => relative_p
1946 working_path = path
1947 endif
1948! Loop over each field in the path
1949 do while (working_path .ne. ' ')
1950! Get the first list in the working path
1951 call find_head(working_path, this_list, rest)
1952! If the first list is empty, then the 'rest' should hold the
1953! final field in the path
1954 if (this_list .eq. ' ') then
1955 this_list = rest
1956 rest = ' '
1957 endif
1958! Strip off trailing list separators
1959 i = len_trim(this_list)
1960 do while (i .gt. 0 .and. this_list(i:i) .eq. list_sep)
1961 this_list(i:i) = ' '
1962 i = i - 1
1963 enddo
1964! Find a pointer to this field in the working list
1965 this_list_p => find_field(this_list, working_path_p)
1966
1967 if (.not. associated(this_list_p)) then
1968 if (create) then
1969! Create the list if so requested
1970 this_list_p => make_list(working_path_p, this_list)
1971 if (.not. associated(this_list_p)) then
1972 nullify(list_p)
1973 return
1974 endif
1975 else
1976! Otherwise, return an error
1977
1978 nullify(list_p)
1979 return
1980 endif
1981 endif
1982! Make sure that the field found is a list, and if so, proceed to
1983! the next field in the path, otherwise, return an error
1984 if (this_list_p%field_type .eq. list_type) then
1985 working_path_p => this_list_p
1986 working_path = rest
1987 else
1988 nullify(list_p)
1989 return
1990 endif
1991 enddo
1992 list_p => working_path_p
1993endif
1994
1995end function find_list
1996
1997!> @brief Change the current list. Return true on success, false otherwise
1998!!
1999!> This function changes the currect list to correspond to the list named name.
2000!! If the first character of name is the list separator (/) then the list will
2001!! search for "name" starting from the root of the field tree. Otherwise it
2002!! will search for name starting from the current list.
2003!! @return A flag to indicate operation success, true = no errors
2004function fm_change_list(name) &
2005 result(success)
2006logical :: success
2007character(len=*), intent(in) :: name !< name of a list to change to
2008
2009type (field_def), pointer, save :: temp_p
2010! Initialize the field manager if needed
2011if (.not. module_is_initialized) then
2013endif
2014! Find the list if path is not empty
2015temp_p => find_list(name, current_list_p, .false.)
2016
2017if (associated(temp_p)) then
2018 current_list_p => temp_p
2019 success = .true.
2020else
2021 success = .false.
2022endif
2023
2024end function fm_change_list
2025
2026!> @brief Change the root list
2027!!
2028!> This function changes the root of the field tree to correspond to the
2029!! field named name. An example of a use of this would be if code is
2030!! interested in a subset of fields with a common base. This common base
2031!! could be set using fm_change_root and fields could be referenced using
2032!! this root.
2033!!
2034!! This function should be used in conjunction with fm_return_root.
2035!! @return A flag to indicate operation success, true = no errors
2036function fm_change_root(name) &
2037 result(success)
2038logical :: success
2039character(len=*), intent(in) :: name !< name of the field which the user wishes to become the root.
2040
2041type (field_def), pointer, save :: temp_list_p
2042integer :: out_unit
2043! Initialize the field manager if needed
2044if (.not. module_is_initialized) then
2046endif
2047out_unit = stdout()
2048! Must supply a field field name
2049if (name .eq. ' ') then
2050 success = .false.
2051 return
2052endif
2053! Get a pointer to the list
2054temp_list_p => find_list(name, current_list_p, .false.)
2055
2056if (associated(temp_list_p)) then
2057! restore the saved root values if we've already changed root
2058 if (save_root_name .ne. ' ') then
2059 root_p%name = save_root_name
2060 root_p%parent => save_root_parent_p
2061 endif
2062! set the pointer for the new root field
2063 root_p => temp_list_p
2064! save the new root field's name and parent
2065 save_root_name = root_p%name
2066 save_root_parent_p => root_p%parent
2067! set the new root name and parent fields to appropriate values
2068 root_p%name = ' '
2069 nullify(root_p%parent)
2070! set the current list to the new root as it likely is not
2071! going to be meaningful anymore
2072 current_list_p => root_p
2073 success = .true.
2074else
2075! Couldn't find the list
2076 success = .false.
2077endif
2078
2079end function fm_change_root
2080
2081!> @brief A function to list properties associated with a field.
2082!!
2083!> This function writes the contents of the field named "name" to stdout.
2084!! If recursive is present and .true., then this function writes out the
2085!! contents of any subfields associated with the field named "name".
2086!! @return A flag to indicate operation success, true = no errors
2087logical function fm_dump_list(name, recursive, unit) result (success)
2088 character(len=*), intent(in) :: name !< The name of the field for which output is requested.
2089 logical, intent(in), optional :: recursive !< If present and .true., then a recursive listing of
2090 !! fields will be performed.
2091 integer, intent(in), optional :: unit !< file to print to
2092
2093 logical :: recursive_t
2094 type (field_def), pointer, save :: temp_list_p
2095 integer :: out_unit
2096
2097 if (present(unit)) then
2098 out_unit = unit
2099 else
2100 out_unit = stdout()
2101 endif
2102
2103 recursive_t = .false.
2104 if (present(recursive)) recursive_t = recursive
2105 if (.not. module_is_initialized) call initialize_module_variables()
2106
2107 if (name .eq. ' ') then
2108 ! If list is empty, then dump the current list
2109 temp_list_p => current_list_p
2110 success = .true.
2111 else
2112 ! Get a pointer to the list
2113 temp_list_p => find_list(name, current_list_p, .false.)
2114 if (associated(temp_list_p)) then
2115 success = .true.
2116 else
2117 success = .false.
2118 endif
2119 endif
2120 ! Dump the list
2121 if (success) then
2122 success = dump_list(temp_list_p, recursive_t, 0, out_unit)
2123 endif
2124end function fm_dump_list
2125
2126!> @brief A function to test whether a named field exists.
2127!!
2128!> This function determines is a field exists, relative to the current list,
2129!! and returns true if the list exists, false otherwise.
2130!! @return A flag to indicate operation success, true = no errors
2131function fm_exists(name) &
2132 result(success)
2133logical :: success
2134character(len=*), intent(in) :: name !< The name of the field that is being queried
2135
2136type (field_def), pointer, save :: dummy_p
2137! Initialize the field manager if needed
2138if (.not. module_is_initialized) then
2140endif
2141! Determine whether the field exists
2142dummy_p => get_field(name, current_list_p)
2143success = associated(dummy_p)
2144
2145end function fm_exists
2146
2147!> @brief A function to return the index of a named field.
2148!!
2149!> Returns the index for name, returns the parameter NO_FIELD if it does not
2150!! exist. If the first character of the named field is the list peparator,
2151!! then the named field will be relative to the root of the field tree.
2152!! Otherwise the named field will be relative to the current list.
2153!> @returns index of the named field if it exists, otherwise the parameter NO_FIELD
2154function fm_get_index(name) &
2155 result(index)
2156integer :: index
2157character(len=*), intent(in) :: name !< The name of a field that the user wishes to get an index for
2158
2159type (field_def), pointer, save :: temp_field_p
2160integer :: out_unit
2161
2162out_unit = stdout()
2163! Initialize the field manager if needed
2164if (.not. module_is_initialized) then
2166endif
2167! Must supply a field field name
2168if (name .eq. ' ') then
2169 index = no_field
2170 return
2171endif
2172! Get a pointer to the field
2173temp_field_p => get_field(name, current_list_p)
2174if (associated(temp_field_p)) then
2175! Set the index
2176 index = temp_field_p%index
2177else
2178 index = no_field
2179endif
2180
2181end function fm_get_index
2182
2183!> @brief A function to return the full path of the current list.
2184!!
2185!> This function returns the full path for the current list. A blank
2186!! path indicates an error condition has occurred.
2187!> @returns The path corresponding to the current list
2189 result(path)
2190character(len=FMS_PATH_LEN) :: path
2191
2192type (field_def), pointer, save :: temp_list_p
2193! Initialize the field manager if needed
2194if (.not. module_is_initialized) then
2196endif
2197! Set a pointer to the current list and proceed
2198! up the tree, filling in the name as we go
2199temp_list_p => current_list_p
2200path = ' '
2201
2202do while (associated(temp_list_p))
2203! Check whether we are at the root field--it is the
2204! only field with a blank name
2205 if (temp_list_p%name .eq. ' ') then
2206 exit
2207 endif
2208! Append the name to the path
2209 path = list_sep // trim(temp_list_p%name) // path
2210! Point to the next field
2211 temp_list_p => temp_list_p%parent
2212enddo
2213
2214if (.not. associated(temp_list_p)) then
2215! The pointer is not associated, indicating an error has
2216! occurred, so set the path accordingly
2217 path = ' '
2218elseif (path .eq. ' ') then
2219! If path is empty, then the current list must be root,
2220! so set path accordingly
2221 path = list_sep
2222endif
2223
2224end function fm_get_current_list
2225
2226!> @brief A function to return how many elements are contained within the named
2227!! list or entry.
2228!!
2229!> This function returns the list or entry length for the named list or entry.
2230!! If the named field or entry does not exist, a value of 0 is returned.
2231!> @returns The number of elements that the field name has.
2232function fm_get_length(name) &
2233 result(length)
2234integer :: length
2235character(len=*), intent(in) :: name !< The name of a list or entry that the user wishes to get the length of
2236
2237type (field_def), pointer, save :: temp_field_p
2238integer :: out_unit
2239
2240out_unit = stdout()
2241! Initialize the field manager if needed
2242if (.not. module_is_initialized) then
2244endif
2245! Must supply a field name
2246if (name .eq. ' ') then
2247 length = 0
2248 return
2249endif
2250! Get a pointer to the field
2251temp_field_p => get_field(name, current_list_p)
2252
2253if (associated(temp_field_p)) then
2254! Set the field length
2255 if (temp_field_p%field_type .eq. list_type) then
2256 length = temp_field_p%length
2257 else
2258 length = temp_field_p%max_index
2259 endif
2260else
2261 length = 0
2262endif
2263
2264end function fm_get_length
2265
2266!> @brief A function to return the type of the named field
2267!!
2268!> This function returns the type of the field for name.
2269!! This indicates whether the named field is a "list" (has children fields),
2270!! or has values of type "integer", "real", "logical" or "string".
2271!! If it does not exist it returns a blank string.
2272!> @returns A string containing the type of the named field
2273function fm_get_type(name) &
2274 result(name_field_type)
2275character(len=8) :: name_field_type
2276character(len=*), intent(in) :: name !< The name of a field that the user wishes to find the type of
2277
2278type (field_def), pointer, save :: temp_field_p
2279integer :: out_unit
2280
2281out_unit = stdout()
2282! Initialize the field manager if needed
2283if (.not. module_is_initialized) then
2285endif
2286! Must supply a field name
2287if (name .eq. ' ') then
2288 name_field_type = ' '
2289 return
2290endif
2291! Get a pointer to the field
2292temp_field_p => get_field(name, current_list_p)
2293
2294if (associated(temp_field_p)) then
2295! Set the field type
2296 name_field_type = field_type_name(temp_field_p%field_type)
2297else
2298 name_field_type = ' '
2299endif
2300
2301end function fm_get_type
2302
2303!> @returns A flag to indicate whether the function operated with (false) or without
2304!! (true) errors.
2305function fm_get_value_integer(name, get_ival, index) &
2306 result(success)
2307logical :: success
2308character(len=*), intent(in) :: name !< The name of a field that the user wishes to get a value for.
2309integer, intent(out) :: get_ival !< The value associated with the named field.
2310integer, intent(in), optional :: index !< An optional index to retrieve a single value from an array.
2311
2312integer :: index_t
2313type (field_def), pointer, save :: temp_field_p
2314integer :: out_unit
2315
2316out_unit = stdout()
2317! Initialize the field manager if needed
2318if (.not. module_is_initialized) then
2320endif
2321! Must supply a field field name
2322if (name .eq. ' ') then
2323 get_ival = 0
2324 success = .false.
2325 return
2326endif
2327! Set index to retrieve
2328if (present(index)) then
2329 index_t = index
2330else
2331 index_t = 1
2332endif
2333! Get a pointer to the field
2334temp_field_p => get_field(name, current_list_p)
2335
2336if (associated(temp_field_p)) then
2337! check that the field is the correct type
2338 if (temp_field_p%field_type .eq. integer_type) then
2339 if (index_t .lt. 1 .or. index_t .gt. temp_field_p%max_index) then
2340! Index is not positive or index too large
2341 get_ival = 0
2342 success = .false.
2343 else
2344! extract the value
2345 get_ival = temp_field_p%i_value(index_t)
2346 success = .true.
2347 endif
2348 else
2349! Field not corrcet type
2350 get_ival = 0
2351 success = .false.
2352 endif
2353else
2354 get_ival = 0
2355 success = .false.
2356endif
2357
2358end function fm_get_value_integer
2359
2360!> @returns A flag to indicate whether the function operated with (false) or without
2361!! (true) errors.
2362function fm_get_value_logical(name, get_lval, index) &
2363 result(success)
2364logical :: success
2365character(len=*), intent(in) :: name !< The name of a field that the user wishes to get a value for.
2366logical, intent(out) :: get_lval !< The value associated with the named field
2367integer, intent(in), optional :: index !< An optional index to retrieve a single value from an array.
2368
2369integer :: index_t
2370type (field_def), pointer, save :: temp_field_p
2371integer :: out_unit
2372
2373out_unit = stdout()
2374! Initialize the field manager if needed
2375if (.not. module_is_initialized) then
2377endif
2378! Must supply a field field name
2379if (name .eq. ' ') then
2380 get_lval = .false.
2381 success = .false.
2382 return
2383endif
2384! Set index to retrieve
2385if (present(index)) then
2386 index_t = index
2387else
2388 index_t = 1
2389endif
2390! Get a pointer to the field
2391temp_field_p => get_field(name, current_list_p)
2392
2393if (associated(temp_field_p)) then
2394! check that the field is the correct type
2395 if (temp_field_p%field_type .eq. logical_type) then
2396
2397 if (index_t .lt. 1 .or. index_t .gt. temp_field_p%max_index) then
2398! Index is not positive or too large
2399 get_lval = .false.
2400 success = .false.
2401 else
2402! extract the value
2403 get_lval = temp_field_p%l_value(index_t)
2404 success = .true.
2405 endif
2406 else
2407! Field not correct type
2408 get_lval = .false.
2409 success = .false.
2410 endif
2411else
2412 get_lval = .false.
2413 success = .false.
2414endif
2415
2416end function fm_get_value_logical
2417
2418!> @returns A flag to indicate whether the function operated with (false) or without
2419!! (true) errors.
2420function fm_get_value_string(name, get_sval, index) &
2421 result(success)
2422logical :: success
2423character(len=*), intent(in) :: name !< The name of a field that the user wishes to get a value for.
2424character(len=*), intent(out) :: get_sval !< The value associated with the named field
2425integer, intent(in), optional :: index !< An optional index to retrieve a single value from an array.
2426
2427integer :: index_t
2428type (field_def), pointer, save :: temp_field_p
2429integer :: out_unit
2430
2431out_unit = stdout()
2432! Initialize the field manager if needed
2433if (.not. module_is_initialized) then
2435endif
2436! Must supply a field field name
2437if (name .eq. ' ') then
2438 get_sval = ''
2439 success = .false.
2440 return
2441endif
2442! Set index to retrieve
2443if (present(index)) then
2444 index_t = index
2445else
2446 index_t = 1
2447endif
2448! Get a pointer to the field
2449temp_field_p => get_field(name, current_list_p)
2450
2451if (associated(temp_field_p)) then
2452! check that the field is the correct type
2453 if (temp_field_p%field_type .eq. string_type) then
2454 if (index_t .lt. 1 .or. index_t .gt. temp_field_p%max_index) then
2455! Index is not positive or is too large
2456 get_sval = ''
2457 success = .false.
2458 else
2459! extract the value
2460 get_sval = temp_field_p%s_value(index_t)
2461 success = .true.
2462 endif
2463 else
2464! Field not correct type
2465 get_sval = ''
2466 success = .false.
2467 endif
2468else
2469 get_sval = ''
2470 success = .false.
2471endif
2472
2473end function fm_get_value_string
2474
2475!> Iterates through the given list
2476!> @returns A flag to indicate whether the function operated with (FALSE)
2477!! or without (TRUE) errors
2478function fm_loop_over_list_old(list, name, field_type, index) &
2479 result(success)
2480logical :: success
2481character(len=*), intent(in) :: list !< Name of a list to loop over
2482character(len=*), intent(out) :: name !< name of a field from list
2483character(len=fm_type_name_len), intent(out) :: field_type !< type of a list entry
2484integer, intent(out) :: index !< index of the field within the list
2485
2486integer :: out_unit
2487
2488out_unit = stdout()
2489! Initialize the field manager if needed
2490if (.not. module_is_initialized) then
2492endif
2493
2494if (list .eq. loop_list .and. associated(loop_list_p)) then
2495! We've already started this loop, so continue on
2496 loop_list_p => loop_list_p%next
2497 success = set_list_stuff()
2498elseif (list .eq. ' ') then
2499! If list is empty, then loop over the current list
2500 loop_list = ' '
2501 loop_list_p => current_list_p%first_field
2502 success = set_list_stuff()
2503else
2504! Get a pointer to the list
2505 loop_list = list
2506 loop_list_p => find_list(loop_list, current_list_p, .false.)
2507 if (associated(loop_list_p)) then
2508 loop_list_p => loop_list_p%first_field
2509 success = set_list_stuff()
2510 else
2511 success = .false.
2512 endif
2513endif
2514
2515return
2516
2517contains
2518
2519!> If the the pointer matches to the right list,
2520!! extract the field information. Used in fm_loop_over_list
2521!> @returns A flag to indicate whether the function operated with (FALSE)
2522!! or without (TRUE) errors
2523function set_list_stuff() &
2524 result(success)
2525
2526 logical :: success
2527
2528 if (associated(loop_list_p)) then
2529 name = loop_list_p%name
2530 field_type = field_type_name(loop_list_p%field_type)
2531 index = loop_list_p%index
2532 success = .true.
2533 else
2534 name = ' '
2535 field_type = ' '
2536 index = 0
2537 success = .false.
2538 loop_list = ' '
2539 endif
2540
2541end function set_list_stuff
2542
2543end function fm_loop_over_list_old
2544
2545!> given a name of the list, prepares an iterator over the list content.
2546!! If the name of the given list is blank, then the current list is used
2547subroutine fm_init_loop(loop_list, iter)
2548 character(len=*) , intent(in) :: loop_list !< name of the list to iterate over
2549 type(fm_list_iter_type), intent(out) :: iter !< loop iterator
2550
2551 if (.not.module_is_initialized) call initialize_module_variables
2552
2553 if (loop_list==' ') then ! looping over current list
2554 iter%ptr => current_list_p%first_field
2555 else
2556 iter%ptr => find_list(loop_list,current_list_p,.false.)
2557 if (associated(iter%ptr)) iter%ptr => iter%ptr%first_field
2558 endif
2559end subroutine fm_init_loop
2560
2561!> given a list iterator, returns information about curren list element
2562!! and advances the iterator to the next list element. At the end of the
2563!! list, returns FALSE
2564function fm_loop_over_list_new(iter, name, field_type, index) &
2565 result(success) ; logical success
2566 type (fm_list_iter_type), intent(inout) :: iter !< list iterator
2567 character(len=*), intent(out) :: name !< name of the current list item
2568 character(len=*), intent(out) :: field_type !< type of the field
2569 integer , intent(out) :: index !< index in the list
2570
2571 if (.not.module_is_initialized) call initialize_module_variables
2572 if (associated(iter%ptr)) then
2573 name = iter%ptr%name
2574 field_type = field_type_name(iter%ptr%field_type)
2575 index = iter%ptr%index
2576 success = .true.
2577 iter%ptr => iter%ptr%next
2578 else
2579 name = ' '
2580 field_type = ' '
2581 index = 0
2582 success = .false.
2583 endif
2584end function fm_loop_over_list_new
2585
2586!> @brief A function to create a new list
2587!!
2588!> Allocate and initialize a new list and return the index of the list. If an
2589!! error occurs return the parameter NO_FIELD.
2590!> @return integer index of the newly created list
2591function fm_new_list(name, create, keep) &
2592 result(index)
2593integer :: index
2594character(len=*), intent(in) :: name !< Name of a list that user wishes to create
2595logical, intent(in), optional :: create !< If present and true, create the list if it does not exist
2596logical, intent(in), optional :: keep !< If present and true, make this list the current list
2597
2598logical :: create_t
2599logical :: keep_t
2600character(len=FMS_PATH_LEN) :: path
2601character(len=fm_field_name_len) :: base
2602type (field_def), pointer, save :: temp_list_p
2603integer :: out_unit
2604
2605out_unit = stdout()
2606! Initialize the field manager if needed
2607if (.not. module_is_initialized) then
2609endif
2610! Must supply a field list name
2611if (name .eq. ' ') then
2612 index = no_field
2613 return
2614endif
2615! Check for optional arguments
2616if (present(create)) then
2617 create_t = create
2618else
2619 create_t = .false.
2620endif
2621
2622if (present(keep)) then
2623 keep_t = keep
2624else
2625 keep_t = .false.
2626endif
2627! Get a pointer to the parent list
2628call find_base(name, path, base)
2629
2630temp_list_p => find_list(path, current_list_p, create_t)
2631
2632if (associated(temp_list_p)) then
2633! Create the list
2634 temp_list_p => make_list(temp_list_p, base)
2635 if (associated(temp_list_p)) then
2636! Make this list the current list, if requested
2637 if (keep_t) then
2638 current_list_p => temp_list_p
2639 endif
2640 index = temp_list_p%index
2641 else
2642 index = no_field
2643 endif
2644else
2645 index = no_field
2646endif
2647
2648end function fm_new_list
2649
2650!> @brief Assigns a given value to a given field
2651!> @returns An index for the named field
2652function fm_new_value_integer(name, new_ival, create, index, append) &
2653 result(field_index)
2654integer :: field_index
2655character(len=*), intent(in) :: name !< The name of a field that the user wishes to create
2656 !! a value for.
2657integer, intent(in) :: new_ival !< The value that the user wishes to apply to the
2658 !! named field.
2659logical, intent(in), optional :: create !< If present and .true., then a value for this
2660 !! field will be created.
2661integer, intent(in), optional :: index !< The index to an array of values that the user
2662 !! wishes to apply a new value.
2663logical, intent(in), optional :: append !< If present and .true., then append the value to
2664 !! an array of the present values. If present and .true., then index cannot be greater than 0.
2665
2666logical :: create_t
2667integer :: i
2668integer :: index_t
2669integer, pointer, dimension(:) :: temp_i_value
2670character(len=FMS_PATH_LEN) :: path
2671character(len=fm_field_name_len) :: base
2672type (field_def), pointer, save :: temp_list_p
2673type (field_def), pointer, save :: temp_field_p
2674integer :: out_unit
2675
2676out_unit = stdout()
2677! Initialize the field manager if needed
2678if (.not. module_is_initialized) then
2680endif
2681! Must supply a field name
2682if (name .eq. ' ') then
2683 field_index = no_field
2684 return
2685endif
2686! Check for optional arguments
2687if (present(create)) then
2688 create_t = create
2689else
2690 create_t = .false.
2691endif
2692! Check that append is not true and index non-positive
2693if (present(index) .and. present(append)) then
2694 if (append .and. index .gt. 0) then
2695 field_index = no_field
2696 return
2697 endif
2698endif
2699! Set index to define
2700if (present(index)) then
2701 index_t = index
2702 if (index_t .lt. 0) then
2703! Index is negative
2704 field_index = no_field
2705 return
2706 endif
2707else
2708 index_t = 1
2709endif
2710! Get a pointer to the parent list
2711call find_base(name, path, base)
2712temp_list_p => find_list(path, current_list_p, create_t)
2713
2714if (associated(temp_list_p)) then
2715 temp_field_p => find_field(base, temp_list_p)
2716 if (.not. associated(temp_field_p)) then
2717! Create the field if it doesn't exist
2718 temp_field_p => create_field(temp_list_p, base)
2719 endif
2720 if (associated(temp_field_p)) then
2721! Check if the field_type is the same as previously
2722! If not then reset max_index to 0
2723 if (temp_field_p%field_type == real_type ) then
2724 ! promote integer input to real
2725 ! all real field values are stored as r8_kind
2726 field_index = fm_new_value(name, real(new_ival,r8_kind), create, index, append)
2727 return
2728 else if (temp_field_p%field_type /= integer_type ) then
2729 ! slm: why would we reset index? Is it not an error to have a "list" defined
2730 ! with different types in more than one place?
2731 temp_field_p%max_index = 0
2732 endif
2733! Assign the type
2734 temp_field_p%field_type = integer_type
2735! Set the index if appending
2736 if (present(append)) then
2737 if (append) then
2738 index_t = temp_field_p%max_index + 1
2739 endif
2740 endif
2741
2742 if (index_t .gt. temp_field_p%max_index + 1) then
2743! Index too large
2744 field_index = no_field
2745 return
2746
2747 elseif (index_t .eq. 0 .and. &
2748 temp_field_p%max_index .gt. 0) then
2749! Can't set non-null field to null
2750 field_index = no_field
2751 return
2752
2753 elseif (.not. allocated(temp_field_p%i_value) .and. &
2754 index_t .gt. 0) then
2755! Array undefined, so allocate the array
2756 allocate(temp_field_p%i_value(1))
2757 temp_field_p%max_index = 1
2758 temp_field_p%array_dim = 1
2759 elseif (index_t .gt. temp_field_p%array_dim) then
2760! Array is too small, so allocate new array and copy over
2761! old values
2762 temp_field_p%array_dim = temp_field_p%array_dim + array_increment
2763 allocate (temp_i_value(temp_field_p%array_dim))
2764 do i = 1, temp_field_p%max_index
2765 temp_i_value(i) = temp_field_p%i_value(i)
2766 enddo
2767 if (allocated(temp_field_p%i_value)) deallocate(temp_field_p%i_value)
2768 temp_field_p%i_value = temp_i_value
2769 temp_field_p%max_index = index_t
2770 endif
2771! Assign the value and set the field_index for return
2772! for non-null fields (index_t > 0)
2773 if (index_t .gt. 0) then
2774 temp_field_p%i_value(index_t) = new_ival
2775 if (index_t .gt. temp_field_p%max_index) then
2776 temp_field_p%max_index = index_t
2777 endif
2778 endif
2779 field_index = temp_field_p%index
2780
2781 else
2782 field_index = no_field
2783 endif
2784else
2785 field_index = no_field
2786endif
2787
2788end function fm_new_value_integer
2789
2790!> @brief Assigns a given value to a given field
2791!> @returns An index for the named field
2792function fm_new_value_logical(name, new_lval, create, index, append) &
2793 result(field_index)
2794integer :: field_index
2795character(len=*), intent(in) :: name !< The name of a field that the user wishes to create
2796 !! a value for.
2797logical, intent(in) :: new_lval !< The value that the user wishes to apply to the
2798 !! named field.
2799logical, intent(in), optional :: create !< If present and .true., then a value for this
2800 !! field will be created.
2801integer, intent(in), optional :: index !< The index to an array of values that the user
2802 !! wishes to apply a new value.
2803logical, intent(in), optional :: append !< If present and .true., then append the value to
2804 !! an array of the present values. If present and .true., then index cannot be greater than 0.
2805
2806character(len=FMS_PATH_LEN) :: path
2807character(len=fm_field_name_len) :: base
2808integer :: i
2809integer :: index_t
2810logical :: create_t
2811logical, dimension(:), pointer :: temp_l_value
2812type (field_def), pointer, save :: temp_list_p
2813type (field_def), pointer, save :: temp_field_p
2814integer :: out_unit
2815
2816out_unit = stdout()
2817! Initialize the field manager if needed
2818if (.not. module_is_initialized) then
2820endif
2821! Must supply a field name
2822if (name .eq. ' ') then
2823 field_index = no_field
2824 return
2825endif
2826! Check for optional arguments
2827if (present(create)) then
2828 create_t = create
2829else
2830 create_t = .false.
2831endif
2832! Check that append is not true and index greater than 0
2833if (present(index) .and. present(append)) then
2834 if (append .and. index .gt. 0) then
2835 field_index = no_field
2836 return
2837 endif
2838endif
2839! Set index to define
2840if (present(index)) then
2841 index_t = index
2842 if (index_t .lt. 0) then
2843! Index is negative
2844 field_index = no_field
2845 return
2846 endif
2847else
2848 index_t = 1
2849endif
2850! Get a pointer to the parent list
2851call find_base(name, path, base)
2852temp_list_p => find_list(path, current_list_p, create_t)
2853
2854if (associated(temp_list_p)) then
2855 temp_field_p => find_field(base, temp_list_p)
2856 if (.not. associated(temp_field_p)) then
2857! Create the field if it doesn't exist
2858 temp_field_p => create_field(temp_list_p, base)
2859 endif
2860 if (associated(temp_field_p)) then
2861! Check if the field_type is the same as previously
2862! If not then reset max_index to 0
2863 if (temp_field_p%field_type /= logical_type ) then
2864 temp_field_p%max_index = 0
2865 endif
2866! Assign the type
2867 temp_field_p%field_type = logical_type
2868! Set the index if appending
2869 if (present(append)) then
2870 if (append) then
2871 index_t = temp_field_p%max_index + 1
2872 endif
2873 endif
2874
2875 if (index_t .gt. temp_field_p%max_index + 1) then
2876! Index too large
2877 field_index = no_field
2878 return
2879
2880 elseif (index_t .eq. 0 .and. &
2881 temp_field_p%max_index .gt. 0) then
2882! Can't set non-null field to null
2883 field_index = no_field
2884 return
2885
2886 elseif (.not. allocated(temp_field_p%l_value) .and. &
2887 index_t .gt. 0) then
2888! Array undefined, so allocate the array
2889 allocate(temp_field_p%l_value(1))
2890 temp_field_p%max_index = 1
2891 temp_field_p%array_dim = 1
2892
2893 elseif (index_t .gt. temp_field_p%array_dim) then
2894! Array is too small, so allocate new array and copy over
2895! old values
2896 temp_field_p%array_dim = temp_field_p%array_dim + array_increment
2897 allocate (temp_l_value(temp_field_p%array_dim))
2898 do i = 1, temp_field_p%max_index
2899 temp_l_value(i) = temp_field_p%l_value(i)
2900 enddo
2901 if (allocated(temp_field_p%l_value)) deallocate(temp_field_p%l_value)
2902 temp_field_p%l_value = temp_l_value
2903 temp_field_p%max_index = index_t
2904
2905 endif
2906! Assign the value and set the field_index for return
2907! for non-null fields (index_t > 0)
2908 if (index_t .gt. 0) then
2909 temp_field_p%l_value(index_t) = new_lval
2910 if (index_t .gt. temp_field_p%max_index) then
2911 temp_field_p%max_index = index_t
2912 endif
2913 endif
2914 field_index = temp_field_p%index
2915 else
2916 field_index = no_field
2917 endif
2918else
2919 field_index = no_field
2920endif
2921
2922end function fm_new_value_logical
2923
2924!> @brief Assigns a given value to a given field
2925!> @returns An index for the named field
2926function fm_new_value_string(name, new_sval, create, index, append) &
2927 result(field_index)
2928integer :: field_index
2929character(len=*), intent(in) :: name !< The name of a field that the user wishes to create
2930 !! a value for.
2931character(len=*), intent(in) :: new_sval !< The value that the user wishes to apply to the
2932 !! named field.
2933logical, intent(in), optional :: create !< If present and .true., then a value for this
2934 !! field will be created.
2935integer, intent(in), optional :: index !< The index to an array of values that the user
2936 !! wishes to apply a new value.
2937logical, intent(in), optional :: append !< If present and .true., then append the value to
2938
2939character(len=fm_string_len), dimension(:), pointer :: temp_s_value
2940character(len=FMS_PATH_LEN) :: path
2941character(len=fm_field_name_len) :: base
2942integer :: i
2943integer :: index_t
2944logical :: create_t
2945type (field_def), save, pointer :: temp_list_p
2946type (field_def), save, pointer :: temp_field_p
2947integer :: out_unit
2948
2949out_unit = stdout()
2950! Initialize the field manager if needed
2951if (.not. module_is_initialized) then
2953endif
2954! Must supply a field name
2955if (name .eq. ' ') then
2956 field_index = no_field
2957 return
2958endif
2959! Check for optional arguments
2960if (present(create)) then
2961 create_t = create
2962else
2963 create_t = .false.
2964endif
2965! Check that append is not true and index greater than 0
2966if (present(index) .and. present(append)) then
2967 if (append .and. index .gt. 0) then
2968 field_index = no_field
2969 return
2970 endif
2971endif
2972! Set index to define
2973if (present(index)) then
2974 index_t = index
2975 if (index_t .lt. 0) then
2976! Index is negative
2977 field_index = no_field
2978 return
2979 endif
2980else
2981 index_t = 1
2982endif
2983! Get a pointer to the parent list
2984call find_base(name, path, base)
2985temp_list_p => find_list(path, current_list_p, create_t)
2986
2987if (associated(temp_list_p)) then
2988 temp_field_p => find_field(base, temp_list_p)
2989 if (.not. associated(temp_field_p)) then
2990! Create the field if it doesn't exist
2991 temp_field_p => create_field(temp_list_p, base)
2992 endif
2993 if (associated(temp_field_p)) then
2994! Check if the field_type is the same as previously
2995! If not then reset max_index to 0
2996 if (temp_field_p%field_type /= string_type ) then
2997 temp_field_p%max_index = 0
2998 endif
2999! Assign the type
3000 temp_field_p%field_type = string_type
3001! Set the index if appending
3002 if (present(append)) then
3003 if (append) then
3004 index_t = temp_field_p%max_index + 1
3005 endif
3006 endif
3007
3008 if (index_t .gt. temp_field_p%max_index + 1) then
3009! Index too large
3010 field_index = no_field
3011 return
3012
3013 elseif (index_t .eq. 0 .and. &
3014 temp_field_p%max_index .gt. 0) then
3015! Can't set non-null field to null
3016 field_index = no_field
3017 return
3018
3019 elseif (.not.allocated(temp_field_p%s_value) .and. &
3020 index_t .gt. 0) then
3021! Array undefined, so allocate the array
3022 allocate(temp_field_p%s_value(1))
3023 temp_field_p%max_index = 1
3024 temp_field_p%array_dim = 1
3025
3026 elseif (index_t .gt. temp_field_p%array_dim) then
3027! Array is too small, so allocate new array and copy over
3028! old values
3029 temp_field_p%array_dim = temp_field_p%array_dim + array_increment
3030 allocate (temp_s_value(temp_field_p%array_dim))
3031 do i = 1, temp_field_p%max_index
3032 temp_s_value(i) = temp_field_p%s_value(i)
3033 enddo
3034 if (allocated(temp_field_p%s_value)) deallocate(temp_field_p%s_value)
3035 temp_field_p%s_value = temp_s_value
3036 temp_field_p%max_index = index_t
3037
3038 endif
3039! Assign the value and set the field_index for return
3040! for non-null fields (index_t > 0)
3041 if (index_t .gt. 0) then
3042 temp_field_p%s_value(index_t) = new_sval
3043 if (index_t .gt. temp_field_p%max_index) then
3044 temp_field_p%max_index = index_t
3045 endif
3046 endif
3047 field_index = temp_field_p%index
3048 else
3049! Error in making the field
3050 field_index = no_field
3051 endif
3052else
3053! Error following the path
3054 field_index = no_field
3055endif
3056
3057end function fm_new_value_string
3058
3059
3060!> Resets the loop variable. For use in conjunction with fm_loop_over_list.
3062! Initialize the field manager if needed
3063if (.not. module_is_initialized) then
3065endif
3066! Reset the variables
3067loop_list = ' '
3068nullify(loop_list_p)
3069
3070end subroutine fm_reset_loop
3071
3072!> Return the root list to the value at initialization.
3073!!
3074!> For use in conjunction with fm_change_root.
3075!!
3076!! Users should use this routine before leaving their routine if they
3077!! previously used fm_change_root.
3079! Initialize the field manager if needed
3080if (.not. module_is_initialized) then
3082endif
3083! restore the saved values to the current root
3084root_p%name = save_root_name
3085root_p%parent => save_root_parent_p
3086! set the pointer to the original root field
3087root_p => root
3088! reset the save root name and parent variables
3089save_root_name = ' '
3090nullify(save_root_parent_p)
3091
3092end subroutine fm_return_root
3093
3094!> Return a pointer to the field if it exists relative to this_list_p,
3095!! null otherwise
3096!! @returns A pointer to the field name
3097function get_field(name, this_list_p) &
3098 result(list_p)
3099type (field_def), pointer :: list_p
3100character(len=*), intent(in) :: name !< The name of a list that the user wishes to get information for
3101type (field_def), pointer :: this_list_p !< A pointer to a list that serves as the base point
3102 !! for searching for name
3103
3104character(len=FMS_PATH_LEN) :: path
3105character(len=fm_field_name_len) :: base
3106type (field_def), pointer, save :: temp_p
3107
3108nullify(list_p)
3109! Get the path and base for name
3110call find_base(name, path, base)
3111! Find the list if path is not empty
3112if (path .ne. ' ') then
3113 temp_p => find_list(path, this_list_p, .false.)
3114 if (associated(temp_p)) then
3115 list_p => find_field(base, temp_p)
3116 else
3117 nullify(list_p)
3118 endif
3119else
3120 list_p => find_field(base, this_list_p)
3121endif
3122
3123end function get_field
3124
3125!> This function allows a user to rename a field without modifying the
3126!! contents of the field.
3127!!
3128!> Function to modify the name of a field.
3129!! Should be used with caution.
3130!! @returns A flag to indicate whether the function operated with (FALSE) or
3131!! without (TRUE) errors.
3132function fm_modify_name(oldname, newname) &
3133 result(success)
3134logical :: success
3135character(len=*), intent(in) :: oldname !< The name of a field that the user wishes to change
3136 !! the name of
3137character(len=*), intent(in) :: newname !< The name that the user wishes to change the name of
3138 !! the field to.
3139
3140character(len=FMS_PATH_LEN) :: path
3141character(len=fm_field_name_len) :: base
3142type (field_def), pointer, save :: list_p
3143type (field_def), pointer, save :: temp_p
3144! Get the path and base for name
3145call find_base(oldname, path, base)
3146! Find the list if path is not empty
3147success = .false.
3148if (path .ne. ' ') then
3149 temp_p => find_list(path, current_list_p, .false.)
3150 if (associated(temp_p)) then
3151 list_p => find_field(base, temp_p)
3152 if (associated(list_p)) then
3153 list_p%name = newname
3154 success = .true.
3155 endif
3156 else
3157 nullify(list_p)
3158 endif
3159else
3160 list_p => find_field(base, current_list_p)
3161 if (associated(list_p)) then
3162 list_p%name = newname
3163 success = .true.
3164 endif
3165endif
3166
3167end function fm_modify_name
3168
3169!> A function to initialize the values of the pointers. This will remove
3170!! all fields and reset the field tree to only the root field.
3172 ! Initialize the root field
3173 integer :: io, ierr !< Error codes when reading the namelist
3174 integer :: logunit !< Unit number for the log file
3175
3176 if (.not. module_is_initialized) then
3177
3178 read (input_nml_file, nml=field_manager_nml, iostat=io)
3179 ierr = check_nml_error(io,"field_manager_nml")
3180
3181 logunit = stdlog()
3182 if (mpp_pe() == mpp_root_pe()) write (logunit, nml=field_manager_nml)
3183
3184 root_p => root
3185
3186 field_type_name(integer_type) = 'integer'
3187 field_type_name(list_type) = 'list'
3188 field_type_name(logical_type) = 'logical'
3189 field_type_name(real_type) = 'real'
3190 field_type_name(string_type) = 'string'
3191
3192 root%name = ' '
3193 root%index = 1
3194 root%parent => root_p
3195
3196 root%field_type = list_type
3197
3198 root%length = 0
3199 nullify(root%first_field)
3200 nullify(root%last_field)
3201 root%max_index = 0
3202 root%array_dim = 0
3203 if (allocated(root%i_value)) deallocate(root%i_value)
3204 if (allocated(root%l_value)) deallocate(root%l_value)
3205 if (allocated(root%r_value)) deallocate(root%r_value)
3206 if (allocated(root%s_value)) deallocate(root%s_value)
3207
3208 nullify(root%next)
3209 nullify(root%prev)
3210
3211 current_list_p => root
3212
3213 nullify(loop_list_p)
3214 loop_list = ' '
3215
3216 nullify(save_root_parent_p)
3217 save_root_name = ' '
3218
3219 module_is_initialized = .true.
3220
3221endif
3222
3223end subroutine initialize_module_variables
3224
3225!> This function creates a new field and returns a pointer to that field.
3226!!
3227!> Allocate and initialize a new list in this_list_p list.
3228!! @return a pointer to the list on success, or a null pointer
3229!! on failure.
3230function make_list(this_list_p, name) &
3231 result(list_p)
3232type (field_def), pointer :: list_p
3233type (field_def), pointer :: this_list_p !< Base of a list that the user wishes to add a list to
3234character(len=*), intent(in) :: name !< name of a list that the user wishes to create
3235
3236type (field_def), pointer, save :: dummy_p
3237integer :: out_unit
3238
3239out_unit = stdout()
3240! Check to see whether there is already a list with
3241! this name, and if so, return an error as list names
3242! must be unique
3243dummy_p => find_field(name, this_list_p )
3244if (associated(dummy_p)) then
3245! This list is already specified, return an error
3246 list_p => dummy_p
3247 return
3248endif
3249! Create a field for the new list
3250nullify(list_p)
3251list_p => create_field(this_list_p, name)
3252if (.not. associated(list_p)) then
3253 nullify(list_p)
3254 return
3255endif
3256! Initialize the new list
3257list_p%length = 0
3258list_p%field_type = list_type
3259if (allocated(list_p%i_value)) deallocate(list_p%i_value)
3260if (allocated(list_p%l_value)) deallocate(list_p%l_value)
3261if (allocated(list_p%r_value)) deallocate(list_p%r_value)
3262if (allocated(list_p%s_value)) deallocate(list_p%s_value)
3263
3264end function make_list
3265
3266!> This is a function that provides the capability to return parameters
3267!! associated with a field in a pair of strings.
3268!!
3269!> Given a name return a list of method names and control strings.
3270!! This function should return strings similar to those in the field
3271!! table if a comma delimited format is being used.
3272!> @return A flag to indicate whether the function operated with (FALSE) or
3273!! without (TRUE) errors
3274function fm_query_method(name, method_name, method_control) &
3275 result(success)
3276logical :: success
3277character(len=*), intent(in) :: name !< name of a list that the user wishes to change to
3278character(len=*), intent(out) :: method_name !< name of a parameter associated with the named field
3279character(len=*), intent(out) :: method_control !< value of parameters associated with the named field
3280
3281character(len=FMS_PATH_LEN) :: path
3282character(len=FMS_PATH_LEN) :: base
3283character(len=FMS_PATH_LEN) :: name_loc
3284logical :: recursive_t
3285type (field_def), pointer, save :: temp_list_p
3286type (field_def), pointer, save :: temp_value_p
3287type (field_def), pointer, save :: this_field_p
3288integer :: out_unit
3289
3290 out_unit = stdout()
3291 success = .false.
3292 recursive_t = .true.
3293 method_name = " "
3294 method_control = " "
3295! Initialize the field manager if needed
3296if (.not. module_is_initialized) call initialize_module_variables
3297name_loc = lowercase(name)
3298call find_base(name_loc, path, base)
3299
3300 temp_list_p => find_list(name_loc, current_list_p, .false.)
3301
3302if (associated(temp_list_p)) then
3303! Find the entry values for the list.
3304 success = query_method(temp_list_p, recursive_t, base, method_name, method_control)
3305else
3306! This is not a list but it may be a parameter with a value
3307! If so put the parameter value in method_name.
3308
3309 temp_value_p => find_list(path, current_list_p, .false.)
3310 if (associated(temp_value_p)) then
3311! Find the entry values for this item.
3312 this_field_p => temp_value_p%first_field
3313
3314 do while (associated(this_field_p))
3315 if ( this_field_p%name == base ) then
3316 method_name = this_field_p%s_value(1)
3317 method_control = ""
3318 success = .true.
3319 exit
3320 else
3321 success = .false.
3322 endif
3323 this_field_p => this_field_p%next
3324 enddo
3325
3326 else
3327! Error following the path
3328 success = .false.
3329 endif
3330endif
3331
3332end function fm_query_method
3333
3334!> A private function that can recursively recover values for parameters
3335!! associated with a field.
3336!> @return A flag to indicate whether the function operated with (FALSE) or
3337!! without (TRUE) errors
3338recursive function query_method(list_p, recursive, name, method_name, method_control) &
3339 result(success)
3340logical :: success
3341type (field_def), pointer :: list_p !< A pointer to the field that is of interest
3342logical, intent(in) :: recursive !< A flag to enable recursive searching if true
3343character(len=*), intent(in) :: name !< name of a list that the user wishes to change to
3344character(len=*), intent(out) :: method_name !< name of a parameter associated with the named field
3345character(len=*), intent(out) :: method_control !< value of parameters associated with the named field
3346
3347integer :: i
3348character(len=64) :: scratch
3349type (field_def), pointer :: this_field_p
3350integer :: out_unit
3351
3352out_unit = stdout()
3353
3354! Check for a valid list
3355if (.not. associated(list_p) .or. list_p%field_type .ne. list_type) then
3356 success = .false.
3357else
3358
3359 ! set the default return value
3360 success = .true.
3361
3362 this_field_p => list_p%first_field
3363
3364 do while (associated(this_field_p))
3365 select case(this_field_p%field_type)
3366 case(list_type)
3367 ! If this is a list, then this is the method name
3368 if (recursive) then
3369 if (.not. query_method(this_field_p, .true., this_field_p%name, method_name, method_control)) then
3370 success = .false.
3371 exit
3372 else
3373 method_name = trim(method_name)//trim(this_field_p%name)
3374 endif
3375 endif
3376
3377 case(integer_type)
3378 write (scratch,*) this_field_p%i_value
3379 call concat_strings(method_control, comma//trim(this_field_p%name)//' = '//trim(adjustl(scratch)))
3380
3381 case(logical_type)
3382 write (scratch,'(l1)')this_field_p%l_value
3383 call concat_strings(method_control, comma//trim(this_field_p%name)//' = '//trim(adjustl(scratch)))
3384
3385 case(real_type)
3386 write (scratch,*) this_field_p%r_value
3387 call concat_strings(method_control, comma//trim(this_field_p%name)//' = '//trim(adjustl(scratch)))
3388
3389 case(string_type)
3390 call concat_strings(method_control, comma//trim(this_field_p%name)//' = '//trim(this_field_p%s_value(1)))
3391 do i = 2, this_field_p%max_index
3392 call concat_strings(method_control, comma//trim(this_field_p%s_value(i)))
3393 enddo
3394
3395 case default
3396 success = .false.
3397 exit
3398
3399 end select
3400 this_field_p => this_field_p%next
3401 enddo
3402endif
3403
3404end function query_method
3405
3406!> private function: appends str2 to the end of str1, with length check
3407subroutine concat_strings(str1,str2)
3408 character(*), intent(inout) :: str1
3409 character(*), intent(in) :: str2
3410
3411 character(64) :: n1,n2 ! for error reporting
3412
3413 if (len_trim(str1)+len_trim(str2)>len(str1)) then
3414 write(n1,*)len(str1)
3415 write(n2,*)len_trim(str1)+len_trim(str2)
3416 call mpp_error(fatal,'length of output string ('//trim(adjustl(n1))&
3417 //') is not enough for the result of concatenation (len='&
3418 //trim(adjustl(n2))//')')
3419 endif
3420 str1 = trim(str1)//trim(str2)
3421end subroutine concat_strings
3422
3423!> A function that allows the user to copy a field and add a suffix to
3424!! the name of the new field.
3425!!
3426!> Given the name of a pre-existing field and a suffix, this function
3427!! will create a new field. The name of the new field will be that of
3428!! the old field with a suffix supplied by the user.
3429!! @return index of the field that has been created by the copy
3430function fm_copy_list(list_name, suffix, create ) &
3431 result(index)
3432integer :: index
3433character(len=*), intent(in) :: list_name !< name of a field that the user wishes to copy
3434character(len=*), intent(in) :: suffix !< suffix that will be added to list_name when
3435 !! field is copied
3436logical, intent(in), optional :: create !< flag to create new list if applicable
3437
3438character(len=fm_string_len), dimension(:), allocatable :: control
3439character(len=fm_string_len), dimension(:), allocatable :: method
3440character(len=fm_string_len) :: head
3441character(len=fm_string_len) :: list_name_new
3442character(len=fm_string_len) :: tail
3443character(len=fm_string_len) :: val_str
3444integer :: n
3445integer :: num_meth
3446integer :: val_int
3447logical :: found_methods
3448logical :: got_value
3449logical :: recursive_t
3450logical :: success
3451logical :: val_logical
3452real(r8_kind) :: val_real
3453type (field_def), pointer, save :: temp_field_p
3454type (field_def), pointer, save :: temp_list_p
3455integer :: out_unit
3456
3457out_unit = stdout()
3458
3459
3460num_meth= 1
3461list_name_new = trim(list_name)//trim(suffix)
3462 recursive_t = .true.
3463! Initialize the field manager if needed
3464if (.not. module_is_initialized) then
3466endif
3467
3468if (list_name .eq. ' ') then
3469! If list is empty, then dump the current list
3470 temp_list_p => current_list_p
3471 success = .true.
3472else
3473! Get a pointer to the list
3474 temp_list_p => find_list(list_name, current_list_p, .false.)
3475 if (associated(temp_list_p)) then
3476 success = .true.
3477 else
3478! Error following the path
3479 success = .false.
3480 endif
3481endif
3482! Find the list
3483if (success) then
3484 found_methods = fm_find_methods(trim(list_name), method, control)
3485 do n = 1, size(method)
3486 if (len_trim(method(n)) > 0 ) then
3487 index = fm_new_list(trim(list_name_new)//list_sep//method(n), create = create)
3488 call find_base(method(n), head, tail)
3489 temp_field_p => find_list(trim(list_name)//list_sep//head,temp_list_p, .false.)
3490 temp_field_p => find_field(tail,temp_field_p)
3491 select case (temp_field_p%field_type)
3492 case (integer_type)
3493 got_value = fm_get_value( trim(list_name)//list_sep//method(n), val_int)
3494 if ( fm_new_value( trim(list_name_new)//list_sep//method(n), val_int, &
3495 create = create, append = .true.) < 0 ) &
3496 call mpp_error(fatal, trim(error_header)//'Could not set the '//trim(method(n))//&
3497 ' for '//trim(list_name)//trim(suffix))
3498
3499 case (logical_type)
3500 got_value = fm_get_value( trim(list_name)//list_sep//method(n), val_logical)
3501 if ( fm_new_value( trim(list_name_new)//list_sep//method(n), val_logical, &
3502 create = create, append = .true.) < 0 ) &
3503 call mpp_error(fatal, trim(error_header)//'Could not set the '//trim(method(n))//&
3504 ' for '//trim(list_name)//trim(suffix))
3505
3506 case (real_type)
3507 got_value = fm_get_value( trim(list_name)//list_sep//method(n), val_real)
3508 if ( fm_new_value( trim(list_name_new)//list_sep//method(n), val_real, &
3509 create = create, append = .true.) < 0 ) &
3510 call mpp_error(fatal, trim(error_header)//'Could not set the '//trim(method(n))//&
3511 ' for '//trim(list_name)//trim(suffix))
3512
3513 case (string_type)
3514 got_value = fm_get_value( trim(list_name)//list_sep//method(n), val_str)
3515 if ( fm_new_value( trim(list_name_new)//list_sep//method(n), val_str, &
3516 create = create, append = .true.) < 0 ) &
3517 call mpp_error(fatal, trim(error_header)//'Could not set the '//trim(method(n))//&
3518 ' for '//trim(list_name)//trim(suffix))
3519 case default
3520 end select
3521
3522 endif
3523 enddo
3524endif
3525
3526end function fm_copy_list
3527
3528!> This function retrieves all the methods associated with a field.
3529!!
3530!> This is different from fm_query_method in that this function gets all
3531!! the methods associated as opposed to 1 method.
3532!! @return A flag to indicate whether the function operated with (FALSE) or
3533!! without (TRUE) errors.
3534function fm_find_methods(list_name, methods, control ) &
3535 result(success)
3536logical :: success
3537character(len=*), intent(in) :: list_name !< The name of a list that the user wishes to find methods for
3538character(len=*), intent(out), dimension(:) :: methods !< An array of the methods associated with list_name
3539character(len=*), intent(out), dimension(:) :: control !< An array of the parameters associated with methods
3540
3541integer :: num_meth
3542logical :: recursive_t
3543type (field_def), pointer, save :: temp_list_p
3544integer :: out_unit
3545
3546out_unit = stdout()
3547num_meth= 1
3548! Check whether to do things recursively
3549 recursive_t = .true.
3550
3551if (.not. module_is_initialized) then
3553endif
3554
3555if (list_name .eq. ' ') then
3556! If list is empty, then dump the current list
3557 temp_list_p => current_list_p
3558 success = .true.
3559else
3560! Get a pointer to the list
3561 temp_list_p => find_list(list_name, current_list_p, .false.)
3562 if (associated(temp_list_p)) then
3563 success = .true.
3564 else
3565! Error following the path
3566 success = .false.
3567 endif
3568endif
3569! Find the list
3570if (success) then
3571 success = find_method(temp_list_p, recursive_t, num_meth, methods, control)
3572endif
3573
3574end function fm_find_methods
3575
3576!> Given a field list pointer this function retrieves methods and
3577!! associated parameters for the field list.
3578!! @returns A flag to indicate whether the function operated with (FALSE) or
3579!! without (TRUE) errors.
3580recursive function find_method(list_p, recursive, num_meth, method, control) &
3581 result(success)
3582logical :: success
3583type (field_def), pointer :: list_p !< A pointer to the field of interest
3584logical, intent(in) :: recursive !< If true, search recursively for fields
3585integer, intent(inout) :: num_meth !< The number of methods found
3586character(len=*), intent(out), dimension(:) :: method !< The methods associated with the field pointed to by list_p
3587character(len=*), intent(out), dimension(:) :: control !< The control parameters for the methods found
3588
3589character(len=FMS_PATH_LEN) :: scratch
3590integer :: i
3591integer :: n
3592type (field_def), pointer, save :: this_field_p
3593integer :: out_unit
3594
3595out_unit = stdout()
3596! Check for a valid list
3597if (.not. associated(list_p) .or. list_p%field_type .ne. list_type) then
3598 success = .false.
3599else
3600! set the default return value
3601 success = .true.
3602
3603 this_field_p => list_p%first_field
3604
3605 do while (associated(this_field_p))
3606 select case(this_field_p%field_type)
3607 case(list_type)
3608! If this is a list, then this is the method name
3609 if ( this_field_p%length > 1) then
3610 do n = num_meth+1, num_meth + this_field_p%length - 1
3611 write (method(n),'(a,a,a,$)') trim(method(num_meth)), &
3612 trim(this_field_p%name), list_sep
3613 enddo
3614 write (method(num_meth),'(a,a,a,$)') trim(method(num_meth)), &
3615 trim(this_field_p%name), list_sep
3616 else
3617 write (method(num_meth),'(a,a,a,$)') trim(method(num_meth)), &
3618 trim(this_field_p%name), list_sep
3619 endif
3620 success = find_method(this_field_p, .true., num_meth, method, control)
3621
3622 case(integer_type)
3623 write (scratch,*) this_field_p%i_value
3624 call strip_front_blanks(scratch)
3625 write (method(num_meth),'(a,a)') trim(method(num_meth)), &
3626 trim(this_field_p%name)
3627 write (control(num_meth),'(a)') &
3628 trim(scratch)
3629 num_meth = num_meth + 1
3630
3631
3632 case(logical_type)
3633
3634 write (method(num_meth),'(a,a)') trim(method(num_meth)), &
3635 trim(this_field_p%name)
3636 write (control(num_meth),'(l1)') &
3637 this_field_p%l_value
3638 num_meth = num_meth + 1
3639
3640 case(real_type)
3641
3642 if(allocated(this_field_p%r_value)) write (scratch,*) this_field_p%r_value
3643 call strip_front_blanks(scratch)
3644 write (method(num_meth),'(a,a)') trim(method(num_meth)), &
3645 trim(this_field_p%name)
3646 write (control(num_meth),'(a)') &
3647 trim(scratch)
3648 num_meth = num_meth + 1
3649
3650
3651 case(string_type)
3652 write (method(num_meth),'(a,a)') trim(method(num_meth)), &
3653 trim(this_field_p%name)
3654 write (control(num_meth),'(a)') &
3655 trim(this_field_p%s_value(1))
3656 do i = 2, this_field_p%max_index
3657 write (control(num_meth),'(a,a,$)') comma//trim(this_field_p%s_value(i))
3658 enddo
3659 num_meth = num_meth + 1
3660
3661
3662 case default
3663 success = .false.
3664 exit
3665
3666 end select
3667
3668 this_field_p => this_field_p%next
3669 enddo
3670endif
3671
3672end function find_method
3673
3674#include "field_manager_r4.fh"
3675#include "field_manager_r8.fh"
3676
3677end module field_manager_mod
3678!> @}
3679! close documentation grouping
character(len=fms_path_len) function, public fm_get_current_list()
A function to return the full path of the current list.
integer function, public fm_get_index(name)
A function to return the index of a named field.
logical function set_list_stuff()
If the the pointer matches to the right list, extract the field information. Used in fm_loop_over_lis...
subroutine, public get_field_info(n, fld_type, fld_name, model, num_methods)
This routine allows access to field information given an index.
subroutine concat_strings(str1, str2)
private function: appends str2 to the end of str1, with length check
integer function, public find_field_index_new(field_name)
character(len=8) function, public fm_get_type(name)
A function to return the type of the named field.
subroutine, private find_head(name, head, rest)
Find the first list for a name by splitting the name into a head and the rest.
type(field_def) function, pointer, private get_field(name, this_list_p)
Return a pointer to the field if it exists relative to this_list_p, null otherwise.
subroutine, public get_field_method(n, m, method)
A routine to get a specified method.
logical function fm_loop_over_list_old(list, name, field_type, index)
Iterates through the given list.
integer, parameter, public fm_string_len
The length of a character string representing character values for the field.
logical function fm_loop_over_list_new(iter, name, field_type, index)
given a list iterator, returns information about curren list element and advances the iterator to the...
subroutine read_field_table_legacy(nfields, table_name)
Routine to read and parse the field table yaml.
logical function, public fm_dump_list(name, recursive, unit)
A function to list properties associated with a field.
subroutine new_name_yaml(list_name, method_name_in, val_name_in)
Subroutine to add new values to list parameters.
subroutine, public fm_return_root
Return the root list to the value at initialization.
logical function, public fm_modify_name(oldname, newname)
This function allows a user to rename a field without modifying the contents of the field.
integer, parameter, public model_land
Land model.
type(field_def) function, pointer, private make_list(this_list_p, name)
This function creates a new field and returns a pointer to that field.
function parse_strings(text, label, values)
function parse_integers(text, label, values)
integer function parse_string(text, label, parse_sval)
type(field_def) function, pointer, private find_list(path, relative_p, create)
Find and return a pointer to the specified list, relative to relative_p. Return a null pointer on err...
logical function, public fm_change_root(name)
Change the root list.
subroutine, public fm_init_loop(loop_list, iter)
given a name of the list, prepares an iterator over the list content. If the name of the given list i...
character(len=11), dimension(num_models), parameter, public model_names
Model names, e.g. MODEL_NAMES(MODEL_OCEAN) is 'oceanic'.
logical function, public fm_change_list(name)
Change the current list. Return true on success, false otherwise.
type(field_mgr_type), dimension(:), allocatable, private fields
fields of field_mgr_type
subroutine, private initialize_module_variables
A function to initialize the values of the pointers. This will remove all fields and reset the field ...
subroutine, public fm_reset_loop
Resets the loop variable. For use in conjunction with fm_loop_over_list.
subroutine, public field_manager_end
Destructor for field manager.
subroutine new_name(list_name, method_name_in, val_name_in)
Subroutine to add new values to list parameters.
integer, parameter, public model_coupler
Ice model.
subroutine, private find_base(name, path, base)
A subroutine that splits a listname into a path and a base.
type(field_def) function, pointer, private create_field(parent_p, name)
A function to create a field as a child of parent_p. This will return a pointer to a field_def type.
integer, parameter, public fm_field_name_len
The length of a character string representing the field name.
logical recursive function, private dump_list(list_p, recursive, depth, out_unit)
This is a function that lists the parameters of a field.
subroutine read_field_table_yaml(nfields, table_name)
Routine to read and parse the field table yaml.
integer, parameter, public fm_type_name_len
The length of a character string representing the various types that the values of the field can take...
logical use_field_table_yaml
.True. if using the field_table.yaml, .false. if using the legacy field_table
subroutine, public get_field_methods(n, methods)
A routine to obtain all the methods associated with a field.
logical function, public fm_get_value_string(name, get_sval, index)
integer, parameter, public model_ocean
Ocean model.
logical function, public fm_get_value_integer(name, get_ival, index)
integer, parameter, public fm_path_name_len
The length of a character string representing the field path.
integer function, public fm_new_list(name, create, keep)
A function to create a new list.
integer, parameter, public no_field
The value returned if a field is not defined.
integer function, public fm_new_value_integer(name, new_ival, create, index, append)
Assigns a given value to a given field.
integer, parameter, public model_ice
Ice model.
logical function, public fm_find_methods(list_name, methods, control)
This function retrieves all the methods associated with a field.
logical function, public fm_query_method(name, method_name, method_control)
This is a function that provides the capability to return parameters associated with a field in a pai...
integer function, public fm_new_value_logical(name, new_lval, create, index, append)
Assigns a given value to a given field.
recursive logical function query_method(list_p, recursive, name, method_name, method_control)
A private function that can recursively recover values for parameters associated with a field.
type(field_def) function, pointer, private find_field(name, this_list_p)
Find and return a pointer to the field in the specified list. Return a null pointer on error.
integer function, public fm_copy_list(list_name, suffix, create)
A function that allows the user to copy a field and add a suffix to the name of the new field.
logical function, public fm_exists(name)
A function to test whether a named field exists.
integer function parse_integer(text, label, parse_ival)
integer, parameter, public model_atmos
Atmospheric model.
logical function, public fm_get_value_logical(name, get_lval, index)
integer, parameter, public num_models
Number of models (ATMOS, OCEAN, LAND, ICE, COUPLER).
subroutine, public field_manager_init(nfields, table_name)
Routine to initialize the field manager.
integer function, public fm_new_value_string(name, new_sval, create, index, append)
Assigns a given value to a given field.
subroutine strip_front_blanks(name)
A routine to strip whitespace from the start of character strings.
integer function, public fm_get_length(name)
A function to return how many elements are contained within the named list or entry.
recursive logical function find_method(list_p, recursive, num_meth, method, control)
Given a field list pointer this function retrieves methods and associated parameters for the field li...
integer function, public find_field_index_old(model, field_name)
Function to return the index of the field.
Returns an index corresponding to the given field name.
An overloaded function to find and extract a value for a named field.
A function for looping over a list.
An overloaded function to assign a value to a field.
A function to parse an integer or an array of integers, a real or an array of reals,...
Private type for internal use.
Private type for internal use.
Private type for internal use.
Iterator over the field manager list.
This method_type is a way to allow a component module to alter the parameters it needs for various tr...
This method_type is the same as method_type except that the method_control string is not present....
This is the same as method_type except that the method_control and method_name strings are not presen...
subroutine, public build_fmtable(fmtable, filename)
Subroutine to populate an fmTable by reading a yaml file, given an optional filename.
Definition fm_yaml.F90:98
Error handler.
Definition mpp.F90:382