FMS 2025.01.02-dev
Flexible Modeling System
Loading...
Searching...
No Matches
fm_yaml.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
20!> @defgroup fm_yaml_mod fm_yaml_mod
21!> @ingroup fm_yaml
22!> @brief Reads entries from a field table yaml into a
23!! nested object for use in the field manager.
24!!
25!> @author Eric Stofferahn
26!!
27
28!> @file
29!> @brief File for @ref fm_yaml_mod
30
31!> @addtogroup fm_yaml_mod
32!> @{
33module fm_yaml_mod
34#ifdef use_yaml
35
36use yaml_parser_mod
37use mpp_mod, only: mpp_error, fatal
38implicit none
39private
40
41!> @}
42
43public :: build_fmtable
44
45!> @brief This type represents a subparameter block for a given variable parameter.
46!> This type contains the name of the associated parameter and the subparameter key/value pairs
47!> @ingroup fm_yaml_mod
48type, public :: fmattr_t
49 integer :: id !< block id of this var
50 character(len=:), allocatable :: paramname !< name of associated parameter
51 character(len=:), dimension(:), allocatable :: keys !< name of the attribute
52 character(len=:), dimension(:), allocatable :: values !< value of the attribute
53end type fmattr_t
54
55!> @brief This type represents the entries for a given variable, e.g. dust.
56!> This type contains the name of the variable, the block id, the key/value pairs for the
57!> variable's parameters, and any applicable subparameters
58!> @ingroup fm_yaml_mod
59type, public :: fmvar_t
60 integer :: id !< block id of this var
61 character(len=:), allocatable :: name !< name of the variable
62 character(len=:), dimension(:), allocatable :: keys !< names of params
63 character(len=:), dimension(:), allocatable :: values !< values of params
64 type (fmattr_t), allocatable :: attributes(:) !< attributes in this var
65end type fmvar_t
66
67!> @brief This type represents the entries for a given model, e.g. land, ocean, atmosphere.
68!> This type contains the name of the model, the block id, and the variables within this model
69!> @ingroup fm_yaml_mod
70type, public :: fmmodel_t
71 integer :: id !< block id of this model
72 character(len=:), allocatable :: name !< name of the model
73 type (fmvar_t), allocatable :: variables(:) !< variables in this model
74end type fmmodel_t
75
76!> @brief This type represents the entries for a specific field type, e.g. a tracer.
77!> This type contains the name of the field type, the block id, and the models within this field type
78!> @ingroup fm_yaml_mod
79type, public :: fmtype_t
80 integer :: id !< block id of this type
81 character(len=:), allocatable :: name !< name of the type
82 type (fmmodel_t), allocatable :: models(:) !< models in this type
83end type fmtype_t
84
85!> @brief This type contains the field types within a field table.
86!> @ingroup fm_yaml_mod
87type, public :: fmtable_t
88 type (fmtype_t), allocatable :: types(:) !< field types in this table
89end type fmtable_t
90
91contains
92
93!> @addtogroup fm_yaml_mod
94!> @{
95
96!> @brief Subroutine to populate an fmTable by reading a yaml file, given an optional filename.
97subroutine build_fmtable(fmTable, filename)
98 type(fmtable_t), intent(out) :: fmtable !< the field table
99 character(len=*), intent(in), optional :: filename !< the name of the yaml file
100 integer :: yfid !< file id of the yaml file
101 integer :: ntypes !< number of field types attached to this table
102 integer :: i !< Loop counter
103
104 if (.not. present(filename)) then
105 yfid = open_and_parse_file("field_table.yaml")
106 else
107 yfid = open_and_parse_file(trim(filename))
108 endif
109
110 ntypes = get_num_blocks(yfid, "field_table", 0)
111 allocate(fmtable%types(ntypes))
112
113 ! Gets the block ids for the associated types of fmTable.
114 call get_block_ids(yfid, "field_table", fmtable%types(:)%id)
115
116 do i=1,ntypes
117 call build_fmtype(fmtable%types(i), yfid)
118 enddo
119end subroutine build_fmtable
120
121!> @brief Populates an fmType, which is assumed to already have its `id` parameter set.
122subroutine build_fmtype(fmType, yfid)
123 type(fmtype_t), intent(inout) :: fmType !< type object
124 integer, intent(in) :: yfid !< file id of the yaml file
125 integer, dimension(1) :: key_ids !< array of key ids
126 character(len=256) :: key_name !< the name of a key
127 character(len=256) :: key_value !< the value of a key
128 integer :: nmodels !< number of models attached to this type
129 integer :: i !< Loop counter
130
131 nmodels = get_num_blocks(yfid, "modlist", fmtype%id)
132 allocate(fmtype%models(nmodels))
133
134 ! Gets the block ids for the associated models of fmType.
135 call get_block_ids(yfid, "modlist", fmtype%models(:)%id, fmtype%id)
136
137 if (get_nkeys(yfid, fmtype%id).ne.1) then
138 call mpp_error(fatal, "fm_yaml_mod: A single `field_type` key is expected")
139 endif
140
141 call get_key_ids(yfid, fmtype%id, key_ids)
142 call get_key_name(yfid, key_ids(1), key_name)
143 call get_key_value(yfid, key_ids(1), key_value)
144
145 if (trim(key_name).ne."field_type") then
146 call mpp_error(fatal, "fm_yaml_mod: A single `field_type` key is expected")
147 endif
148
149 fmtype%name = trim(key_value)
150
151 do i=1,nmodels
152 call build_fmmodel(fmtype%models(i), yfid)
153 enddo
154end subroutine build_fmtype
155
156!> @brief Populates an fmModel, which is assumed to already have its `id` parameter set.
157subroutine build_fmmodel(fmModel, yfid)
158 type(fmmodel_t), intent(inout) :: fmModel !< model object
159 integer, intent(in) :: yfid !< file id of the yaml file
160 integer, dimension(1) :: key_ids !< array of key ids
161 character(len=256) :: key_name !< the name of a key
162 character(len=256) :: key_value !< the value of a key
163 integer :: nvars !< number of variables attached to this model
164 integer :: i !< Loop counter
165
166 nvars = get_num_blocks(yfid, "varlist", fmmodel%id)
167 allocate(fmmodel%variables(nvars))
168
169 ! gets the block ids for the associated variables of fmModel.
170 call get_block_ids(yfid, "varlist", fmmodel%variables(:)%id, fmmodel%id)
171
172 if (get_nkeys(yfid, fmmodel%id).ne.1) then
173 call mpp_error(fatal, "fm_yaml_mod: A single `model_type` key is expected")
174 endif
175
176 call get_key_ids(yfid, fmmodel%id, key_ids)
177 call get_key_name(yfid, key_ids(1), key_name)
178 call get_key_value(yfid, key_ids(1), key_value)
179
180 if (trim(key_name).ne."model_type") then
181 call mpp_error(fatal, "fm_yaml_mod: A single `model_type` key is expected")
182 endif
183
184 fmmodel%name = trim(key_value)
185
186 do i=1,nvars
187 call build_fmvar(fmmodel%variables(i), yfid)
188 enddo
189end subroutine build_fmmodel
190
191!> @brief Populates an fmVar and creates any associated fmAttrs
192subroutine build_fmvar(fmVar, yfid)
193 type(fmvar_t), intent(inout) :: fmVar !< variable object
194 integer, intent(in) :: yfid !< file id of the yaml file
195 integer :: nkeys !< number of keys defined for this var
196 integer, allocatable :: key_ids(:) !< array of key ids
197 character(len=256) :: key_name !< the name of a key
198 character(len=256) :: key_value !< the value of a key
199 integer :: nattrs !< number of attribute blocks attached to this var
200 integer :: nmethods !< total number of methods attached to this var
201 integer :: maxln !< max string length of method names
202 integer :: maxlv !< max string length of method values
203 character(:), allocatable :: attr_method_keys(:) !< Keys of methods defined in attribute blocks
204 character(:), allocatable :: attr_method_values(:) !< Values of methods defined in attribute blocks
205 integer :: i_name !< Index of the key containing the variable's name
206 integer :: i, j !< Loop indices
207
208 ! Read attribute blocks attached to this variable
209 call fmvar_read_attrs(fmvar, yfid, attr_method_keys, attr_method_values)
210 nattrs = size(attr_method_keys)
211
212 nkeys = get_nkeys(yfid, fmvar%id)
213 allocate(key_ids(nkeys))
214 call get_key_ids(yfid, fmvar%id, key_ids)
215
216 maxln = len(attr_method_keys)
217 maxlv = len(attr_method_values)
218 i_name = -1
219
220 do i=1,nkeys
221 call get_key_name(yfid, key_ids(i), key_name)
222 call get_key_value(yfid, key_ids(i), key_value)
223
224 if (trim(key_name) .eq. "variable") then
225 if (i_name .ne. -1) then
226 call mpp_error(fatal, "fm_yaml_mod: A variable can have only one `variable` key")
227 endif
228
229 fmvar%name = trim(key_value)
230 i_name = i
231 else
232 maxln = max(maxln, len_trim(key_name))
233 maxlv = max(maxlv, len_trim(key_value))
234 endif
235 enddo
236
237 if (i_name .eq. -1) then
238 call mpp_error(fatal, "fm_yaml_mod: Every variable must have a `variable` key")
239 endif
240
241 ! Number of methods is the number of keys (excluding `variable`), plus one for each attribute block.
242 nmethods = nkeys - 1 + nattrs
243
244 allocate(character(len=maxln)::fmVar%keys(nmethods))
245 allocate(character(len=maxlv)::fmVar%values(nmethods))
246
247 j = 1
248 do i=1,nkeys
249 if (i.eq.i_name) cycle ! Exclude `variable` key
250
251 call get_key_name(yfid, key_ids(i), key_name)
252 call get_key_value(yfid, key_ids(i), key_value)
253 fmvar%keys(j) = trim(key_name)
254 fmvar%values(j) = trim(key_value)
255
256 j = j + 1
257 enddo
258
259 ! Add methods defined within attribute blocks.
260 fmvar%keys(j:) = attr_method_keys
261 fmvar%values(j:) = attr_method_values
262end subroutine build_fmvar
263
264!> @brief Reads the attribute blocks attached to a variable and populates the associated fmAttr structures.
265!! Returns two arrays containing key/value pairs of all methods defined via attribute blocks.
266subroutine fmvar_read_attrs(fmVar, yfid, method_keys, method_values)
267 type(fmvar_t), intent(inout) :: fmVar !< variable object
268 integer, intent(in) :: yfid !< file id of the yaml file
269 character(:), allocatable, intent(out) :: method_keys(:) !< Method keys (names of attribute blocks)
270 character(:), allocatable, intent(out) :: method_values(:) !< Method values from attribute blocks
271 integer :: nattrs !< number of attribute blocks
272 integer :: nkeys !< number of keys in an attribute block
273 integer, allocatable :: key_ids(:) !< array of key ids
274 character(len=256) :: key_name !< the name of a key
275 character(len=256) :: key_value !< the value of a key
276 integer :: maxln_m !< max string length of method names
277 integer :: maxlv_m !< max string length of method values
278 integer :: maxln_a !< max string length of subparameter names
279 integer :: maxlv_a !< max string length of subparameter values
280 integer,allocatable :: name_key_id(:) !< Indices of attribute `value` keys
281 integer :: i, j, k !< Loop counters
282
283 nattrs = get_num_unique_blocks(yfid, fmvar%id)
284 allocate(fmvar%attributes(nattrs))
285 allocate(name_key_id(nattrs))
286
287 ! gets the block ids for the associated attributes of fmVar.
288 call get_unique_block_ids(yfid, fmvar%attributes(:)%id, fmvar%id)
289
290 maxln_m = 0
291 maxlv_m = 0
292 name_key_id = -1
293
294 do i=1,nattrs
295 associate(fmattr => fmvar%attributes(i))
296 call get_block_name(yfid, fmattr%id, key_value)
297 fmattr%paramname = trim(key_value)
298
299 nkeys = get_nkeys(yfid, fmattr%id)
300 allocate(key_ids(nkeys))
301 call get_key_ids(yfid, fmattr%id, key_ids)
302
303 maxln_a = 0
304 maxlv_a = 0
305
306 do j=1,nkeys
307 call get_key_name(yfid, key_ids(j), key_name)
308 call get_key_value(yfid, key_ids(j), key_value)
309
310 if (trim(key_name) .eq. "value") then
311 if (name_key_id(i) .ne. -1) then
312 call mpp_error(fatal, "fm_yaml_mod: A variable attribute block can only have one `value` key")
313 endif
314
315 maxln_m = max(maxln_m, len(fmattr%paramname))
316 maxlv_m = max(maxlv_m, len_trim(key_value))
317
318 name_key_id(i) = key_ids(j)
319 else
320 maxln_a = max(maxln_a, len_trim(key_name))
321 maxlv_a = max(maxlv_a, len_trim(key_value))
322 endif
323 enddo
324
325 if (name_key_id(i) .eq. -1) then
326 call mpp_error(fatal, "fm_yaml_mod: Every variable attribute must have a `value` key")
327 endif
328
329 allocate(character(len=maxln_a)::fmAttr%keys(nkeys - 1))
330 allocate(character(len=maxlv_a)::fmAttr%values(nkeys - 1))
331
332 k = 1
333 do j=1,nkeys
334 if (key_ids(j).eq.name_key_id(i)) cycle
335
336 call get_key_name(yfid, key_ids(j), key_name)
337 call get_key_value(yfid, key_ids(j), key_value)
338 fmattr%keys(k) = trim(key_name)
339 fmattr%values(k) = trim(key_value)
340
341 k = k + 1
342 enddo
343
344 deallocate(key_ids)
345 end associate
346 enddo
347
348 allocate(character(len=maxln_m)::method_keys(nattrs))
349 allocate(character(len=maxlv_m)::method_values(nattrs))
350
351 do i=1,nattrs
352 method_keys(i) = fmvar%attributes(i)%paramname
353 call get_key_value(yfid, name_key_id(i), method_values(i))
354 enddo
355end subroutine fmvar_read_attrs
356
357#endif
358end module fm_yaml_mod
359
360!> @}
361! close documentation grouping
subroutine build_fmtype(fmtype, yfid)
Populates an fmType, which is assumed to already have its id parameter set.
Definition fm_yaml.F90:123
subroutine build_fmvar(fmvar, yfid)
Populates an fmVar and creates any associated fmAttrs.
Definition fm_yaml.F90:193
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
subroutine fmvar_read_attrs(fmvar, yfid, method_keys, method_values)
Reads the attribute blocks attached to a variable and populates the associated fmAttr structures....
Definition fm_yaml.F90:267
subroutine build_fmmodel(fmmodel, yfid)
Populates an fmModel, which is assumed to already have its id parameter set.
Definition fm_yaml.F90:158
This type represents a subparameter block for a given variable parameter. This type contains the name...
Definition fm_yaml.F90:48
This type represents the entries for a given model, e.g. land, ocean, atmosphere. This type contains ...
Definition fm_yaml.F90:70
This type contains the field types within a field table.
Definition fm_yaml.F90:87
This type represents the entries for a specific field type, e.g. a tracer. This type contains the nam...
Definition fm_yaml.F90:79
This type represents the entries for a given variable, e.g. dust. This type contains the name of the ...
Definition fm_yaml.F90:59
Error handler.
Definition mpp.F90:382
integer function, public get_num_blocks(file_id, block_name, parent_block_id)
Determines the number of blocks with block_name in the yaml file If parent_block_id is present,...
subroutine, public get_unique_block_ids(file_id, block_ids, parent_block_id)
Gets the ids of the unique block ids.
subroutine, public get_block_name(file_id, block_id, block_name)
Gets the block name form the block id.
subroutine, public get_key_value(file_id, key_id, key_value)
Gets the value from a file id.
integer function, public get_nkeys(file_id, block_id)
Gets the number of key-value pairs in a block.
subroutine, public get_key_ids(file_id, block_id, key_ids)
Gets the ids of the key-value pairs in a block.
integer function, public get_num_unique_blocks(file_id, parent_block_id)
Gets the number of unique blocks.
integer function, public open_and_parse_file(filename)
Opens and parses a yaml file.
subroutine, public get_key_name(file_id, key_id, key_name)
Gets the key from a file id.
subroutine, public get_block_ids(file_id, block_name, block_ids, parent_block_id)
Gets the the ids of the blocks with block_name in the yaml file If parent_block_id is present,...