FMS  2025.04
Flexible Modeling System
register_variable_attribute.inc
1 !***********************************************************************
2 !* Apache License 2.0
3 !*
4 !* This file is part of the GFDL Flexible Modeling System (FMS).
5 !*
6 !* Licensed under the Apache License, Version 2.0 (the "License");
7 !* you may not use this file except in compliance with the License.
8 !* You may obtain a copy of the License at
9 !*
10 !* http://www.apache.org/licenses/LICENSE-2.0
11 !*
12 !* FMS is distributed in the hope that it will be useful, but WITHOUT
13 !* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied;
14 !* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
15 !* PARTICULAR PURPOSE. See the License for the specific language
16 !* governing permissions and limitations under the License.
17 !***********************************************************************
18 !> @file
19 !> @brief Routines to add variable attributes for different dimensions to be used in
20 !! the @ref register_variable_attribute interface
21 
22 !> @addtogroup fms2_io_mod
23 !> @{
24 
25 !> @brief Add an attribute to a variable.
26 subroutine register_variable_attribute_0d(fileobj, variable_name, attribute_name, &
27  attribute_value, str_len)
28 
29  class(fmsnetcdffile_t), intent(in) :: fileobj !< File object.
30  character(len=*), intent(in) :: variable_name !< Variable name.
31  character(len=*), intent(in) :: attribute_name !< Attribute name.
32  class(*), intent(in) :: attribute_value !< Attribute value
33  integer, intent(in), optional :: str_len !< Length of the string
34 
35  integer :: varid
36  integer :: err
37  integer :: axtype
38  integer :: xtype
39  integer(kind=i4_kind), dimension(2) :: i32_range
40  integer(kind=i8_kind), dimension(2) :: i64_range
41  real(kind=r4_kind), dimension(2) :: r32_range
42  real(kind=r8_kind), dimension(2) :: r64_range
43  character(len=200) :: append_error_msg !< Msg to be appended to FATAL error message
44 
45  append_error_msg = "register_variable_attribute_0d: file:"//trim(fileobj%path)//"- variable:"//&
46  &trim(variable_name)//" attribute: "//trim(attribute_name)
47 
48  if (fileobj%is_root) then
49  call set_netcdf_mode(fileobj%ncid, define_mode)
50  varid = get_variable_id(fileobj%ncid, trim(variable_name), msg=append_error_msg)
51  select type(attribute_value)
52  type is (character(len=*))
53  if (.not. present(str_len)) call error("Need to include str length:"//trim(append_error_msg))
54  err = nf90_put_att(fileobj%ncid, varid, trim(attribute_name), &
55  trim(attribute_value(1:str_len)))
56  type is (integer(kind=i4_kind))
57  err = nf90_put_att(fileobj%ncid, varid, trim(attribute_name), &
58  attribute_value)
59  type is (integer(kind=i8_kind))
60  if ( .not. fileobj%is_netcdf4) call error(trim(fileobj%path)//&
61  & ": 64 bit integers are only supported with 'netcdf4' file format"// &
62  & ". Set netcdf_default_format='netcdf4' in the fms2_io namelist OR "//&
63  & "add nc_format='netcdf4' to your open_file call")
64  err = nf90_put_att(fileobj%ncid, varid, trim(attribute_name), &
65  attribute_value)
66  type is (real(kind=r4_kind))
67  err = nf90_put_att(fileobj%ncid, varid, trim(attribute_name), &
68  attribute_value)
69  type is (real(kind=r8_kind))
70  err = nf90_put_att(fileobj%ncid, varid, trim(attribute_name), &
71  attribute_value)
72  class default
73  call error("Unsupported attribute type:"//trim(append_error_msg))
74  end select
75  call check_netcdf_code(err, append_error_msg)
76 
77  !The missing_value attribute is not NUG compliant, but is included here to support
78  !legacy model component code.
79  axtype = get_attribute_type(fileobj%ncid, varid, attribute_name, append_error_msg)
80  xtype = get_variable_type(fileobj%ncid, varid, append_error_msg)
81  if (string_compare(attribute_name, "_FillValue") .or. &
82  string_compare(attribute_name, "valid_min") .or. &
83  string_compare(attribute_name, "valid_max") .or. &
84  string_compare(attribute_name, "scale_factor") .or. &
85  string_compare(attribute_name, "add_offset") .or. &
86  string_compare(attribute_name, "missing_value")) then
87  if (axtype .ne. xtype) then
88  call error("The variable type does not match the attribute type: "//trim(append_error_msg))
89  endif
90  endif
91 
92  if (string_compare(attribute_name, "_FillValue")) then
93  if (attribute_exists(fileobj%ncid, varid, "valid_range", msg=append_error_msg)) then
94  select type(attribute_value)
95  type is (integer(kind=i4_kind))
96  call get_variable_attribute(fileobj, variable_name, "valid_range", &
97  i32_range, .false.)
98  if (attribute_value .lt. i32_range(1) .or. &
99  attribute_value .gt. i32_range(2)) then
100  call error("_FillValue inside valid_range: "//trim(append_error_msg))
101  endif
102  type is (integer(kind=i8_kind))
103  call get_variable_attribute(fileobj, variable_name, "valid_range", &
104  i64_range, .false.)
105  if (attribute_value .lt. i64_range(1) .or. &
106  attribute_value .gt. i64_range(2)) then
107  call error("_FillValue inside valid_range: "//trim(append_error_msg))
108  endif
109  type is (real(kind=r4_kind))
110  call get_variable_attribute(fileobj, variable_name, "valid_range", &
111  r32_range, .false.)
112  if (attribute_value .lt. r32_range(1) .or. &
113  attribute_value .gt. r32_range(2)) then
114  call error("_FillValue inside valid_range: "//trim(append_error_msg))
115  endif
116  type is (real(kind=r8_kind))
117  call get_variable_attribute(fileobj, variable_name, "valid_range", &
118  r64_range, .false.)
119  if (attribute_value .lt. r64_range(1) .or. &
120  attribute_value .gt. r64_range(2)) then
121  call error("_FillValue inside valid_range: "//trim(append_error_msg))
122  endif
123  class default
124  call error("unsupported attribute type: "//trim(append_error_msg))
125  end select
126  endif
127  elseif (string_compare(attribute_name, "valid_min") .or. &
128  string_compare(attribute_name, "valid_max")) then
129  if (attribute_exists(fileobj%ncid, varid, "valid_range", msg=append_error_msg)) then
130  call error("cannot have attributes valid_range and valid_min/max: "//trim(append_error_msg))
131  endif
132  endif
133  endif
134 end subroutine register_variable_attribute_0d
135 
136 
137 !> @brief Add an attribute to a variable.
138 subroutine register_variable_attribute_1d(fileobj, variable_name, attribute_name, &
139  attribute_value, str_len)
140 
141  class(fmsnetcdffile_t), intent(in) :: fileobj !< File object.
142  character(len=*), intent(in) :: variable_name !< Variable name.
143  character(len=*), intent(in) :: attribute_name !< Attribute name.
144  class(*), dimension(:), intent(in) :: attribute_value !< Attribute value
145  integer, intent(in), optional :: str_len !< Length of the string
146 
147  integer :: varid
148  integer :: err
149  integer :: axtype
150  integer :: xtype
151  character(len=200) :: append_error_msg !< Msg to be appended to FATAL error message
152 
153  append_error_msg = "register_variable_attribute_0d: file:"//trim(fileobj%path)//"- variable:"//&
154  &trim(variable_name)//" attribute: "//trim(attribute_name)
155 
156  if (fileobj%is_root) then
157  call set_netcdf_mode(fileobj%ncid, define_mode)
158  varid = get_variable_id(fileobj%ncid, trim(variable_name), msg=append_error_msg)
159  select type(attribute_value)
160  type is (integer(kind=i4_kind))
161  err = nf90_put_att(fileobj%ncid, varid, trim(attribute_name), &
162  attribute_value)
163  type is (integer(kind=i8_kind))
164  if ( .not. fileobj%is_netcdf4) call error(trim(fileobj%path)// &
165  & ": 64 bit integers are only supported with 'netcdf4' file format"// &
166  & ". Set netcdf_default_format='netcdf4' in the fms2_io namelist OR "//&
167  & "add nc_format='netcdf4' to your open_file call")
168  err = nf90_put_att(fileobj%ncid, varid, trim(attribute_name), &
169  attribute_value)
170  type is (real(kind=r4_kind))
171  err = nf90_put_att(fileobj%ncid, varid, trim(attribute_name), &
172  attribute_value)
173  type is (real(kind=r8_kind))
174  err = nf90_put_att(fileobj%ncid, varid, trim(attribute_name), &
175  attribute_value)
176  class default
177  call error("unsupported attribute type: "//trim(append_error_msg))
178  end select
179  call check_netcdf_code(err, append_error_msg)
180 
181  if (string_compare(attribute_name, "_FillValue") .or. &
182  string_compare(attribute_name, "valid_min") .or. &
183  string_compare(attribute_name, "valid_max") .or. &
184  string_compare(attribute_name, "scale_factor") .or. &
185  string_compare(attribute_name, "add_offset") .or. &
186  string_compare(attribute_name, "missing_value")) then
187  call error(trim(attribute_name)//" must be a scalar.")
188  elseif (string_compare(attribute_name, "valid_range")) then
189  if (attribute_exists(fileobj%ncid, varid, "valid_min", msg=append_error_msg) .or. &
190  attribute_exists(fileobj%ncid, varid, "valid_max", msg=append_error_msg)) then
191  call error("cannot have valid_range and valid_min/max: "//trim(append_error_msg))
192  endif
193  axtype = get_attribute_type(fileobj%ncid, varid, attribute_name, append_error_msg)
194  xtype = get_variable_type(fileobj%ncid, varid, append_error_msg)
195  if (axtype .ne. xtype) then
196  call error("The type of the variable is not the same as the type of the variable: "//trim(append_error_msg))
197  endif
198  if (size(attribute_value) .ne. 2) then
199  call error("valid_range must be a vector with two values: "//trim(append_error_msg))
200  endif
201  endif
202  endif
203 end subroutine register_variable_attribute_1d
204 !> @}
subroutine register_variable_attribute_1d(fileobj, variable_name, attribute_name, attribute_value, str_len)
Add an attribute to a variable.
subroutine register_variable_attribute_0d(fileobj, variable_name, attribute_name, attribute_value, str_len)
Add an attribute to a variable.