← back to index

src/framework/MOM_netcdf.F90

portedportable, not yet portedexecuted, not portableexecutable, not hit by this run

1! This file is part of MOM6, the Modular Ocean Model version 6.
2! See the LICENSE file for licensing information.
3! SPDX-License-Identifier: Apache-2.0
4
5!> MOM6 interface to netCDF operations
6module MOM_netcdf
7
8use, intrinsic :: iso_fortran_env, only : real32, real64
9
10use netcdf, only : nf90_create, nf90_open, nf90_close
11use netcdf, only : nf90_sync
12use netcdf, only : NF90_CLOBBER, NF90_NOCLOBBER, NF90_WRITE, NF90_NOWRITE
13use netcdf, only : nf90_enddef
14use netcdf, only : nf90_def_dim, nf90_def_var
15use netcdf, only : NF90_UNLIMITED
16use netcdf, only : nf90_get_var
17use netcdf, only : nf90_put_var, nf90_put_att
18use netcdf, only : NF90_FLOAT, NF90_DOUBLE
19use netcdf, only : nf90_strerror, NF90_NOERR
20use netcdf, only : NF90_GLOBAL
21use netcdf, only : nf90_inquire, nf90_inquire_dimension, nf90_inquire_variable
22use netcdf, only : nf90_inq_dimids, nf90_inq_varids
23use netcdf, only : NF90_MAX_NAME
24
25use MOM_error_handler, only : MOM_error, FATAL
26use MOM_io_infra, only : READONLY_FILE, WRITEONLY_FILE
27use MOM_io_infra, only : APPEND_FILE, OVERWRITE_FILE
28
29implicit none ; private
30
31public :: netcdf_file_type
32public :: netcdf_axis
33public :: netcdf_field
34public :: open_netcdf_file
35public :: close_netcdf_file
36public :: flush_netcdf_file
37public :: register_netcdf_axis
38public :: register_netcdf_field
39public :: write_netcdf_field
40public :: write_netcdf_axis
41public :: write_netcdf_attribute
42public :: get_netcdf_size
43public :: get_netcdf_fields
44public :: get_netcdf_filename
45public :: read_netcdf_field
46
47
48!> Internal time value used to indicate an uninitialized time
49real, parameter :: NULLTIME = -1
50! NOTE: For now, we use the FMS-compatible value, but may change in the future.
51
52
53!> netCDF file abstraction
54type :: netcdf_file_type
55 private
56 integer :: ncid
57 !< netCDF file ID
58 character(len=:), allocatable :: filename
59 !< netCDF filename
60 logical :: define_mode
61 !< True if file is in define mode.
62 integer :: time_id
63 !< Time axis variable ID
64 real :: time
65 !< Current model time
66 integer :: time_level
67 !< Current time level for output
68end type netcdf_file_type
69
70
71!> Dimension axis for a netCDF file
72type :: netcdf_axis
73 private
74 character(len=:), allocatable, public :: label
75 !< Axis label name
76 real, allocatable :: points(:)
77 !< Grid points along the axis
78 integer :: dimid
79 !< netCDF dimension ID associated with axis
80 integer :: varid
81 !< netCDF variable ID associated with axis
82end type netcdf_axis
83
84
85!> Field variable for a netCDF file
86type netcdf_field
87 private
88 character(len=:), allocatable, public :: label
89 !< Variable name
90 integer :: varid
91 !< netCDF variable ID for field
92end type netcdf_field
93
94
95!> Write values to a field of a netCDF file
96interface write_netcdf_field
97 module procedure write_netcdf_field_4d
98 module procedure write_netcdf_field_3d
99 module procedure write_netcdf_field_2d
100 module procedure write_netcdf_field_1d
101 module procedure write_netcdf_field_0d
102end interface write_netcdf_field
103
104contains
105
1062subroutine open_netcdf_file(handle, filename, mode)
107 type(netcdf_file_type), intent(inout) :: handle
108 !< netCDF file handle
109 character(len=*), intent(in) :: filename
110 !< netCDF filename
111 integer, intent(in), optional :: mode
112 !< Input MOM I/O mode
113
114 integer :: io_mode
115 ! MOM I/O mode
116 integer :: cmode
117 ! netCDF creation mode
118 integer :: rc
119 ! nf90_create return code
120 character(len=:), allocatable :: msg
121 ! netCDF error message buffer
122
123 ! I/O configuration
1242 io_mode = WRITEONLY_FILE
1252 if (present(mode)) io_mode = mode
126
127 ! Translate the MOM I/O config to the netCDF mode
1280 select case(io_mode)
129 case (WRITEONLY_FILE)
1300 rc = nf90_create(filename, nf90_noclobber, handle%ncid)
1310 handle%define_mode = .true.
132 case (OVERWRITE_FILE)
1332 rc = nf90_create(filename, nf90_clobber, handle%ncid)
1342 handle%define_mode = .true.
135 case (APPEND_FILE)
1360 rc = nf90_open(filename, nf90_write, handle%ncid)
1370 handle%define_mode = .false.
138 case (READONLY_FILE)
1390 rc = nf90_open(filename, nf90_nowrite, handle%ncid)
1400 handle%define_mode = .false.
141 case default
142 call MOM_error(FATAL, &
1432 'open_netcdf_file: File ' // filename // ': Unknown mode.')
144 end select
1452 call check_netcdf_call(rc, 'open_netcdf_file', 'File ' // filename)
146
1472 handle%filename = filename
148
149 ! FMS writes the filename as an attribute
1504 if (any(io_mode == [WRITEONLY_FILE, OVERWRITE_FILE])) &
1512 call write_netcdf_attribute(handle, 'filename', filename)
1522end subroutine open_netcdf_file
153
154
155!> Close an opened netCDF file.
1561subroutine close_netcdf_file(handle)
157 type(netcdf_file_type), intent(in) :: handle
158
159 integer :: rc
160
1611 rc = nf90_close(handle%ncid)
162 call check_netcdf_call(rc, 'close_netcdf_file', &
1631 'File "' // handle%filename // '"')
1641end subroutine close_netcdf_file
165
166
167!> Flush buffered output to the netCDF file
1683subroutine flush_netcdf_file(handle)
169 type(netcdf_file_type), intent(in) :: handle
170
171 integer :: rc
172
1733 rc = nf90_sync(handle%ncid)
174 call check_netcdf_call(rc, 'flush_netcdf_file', &
1753 'File "' // handle%filename // '"')
1763end subroutine flush_netcdf_file
177
178
179!> Change netCDF mode of handle from 'define' to 'write'.
1802subroutine enable_netcdf_write(handle)
181 type(netcdf_file_type), intent(inout) :: handle
182
183 integer :: rc
184
1852 if (handle%define_mode) then
1862 rc = nf90_enddef(handle%ncid)
187 call check_netcdf_call(rc, 'enable_netcdf_write', &
1882 'File "' // handle%filename // '"')
1892 handle%define_mode = .false.
190 endif
1912end subroutine enable_netcdf_write
192
193
194!> Register a netCDF variable
19520function register_netcdf_field(handle, label, axes, longname, units) &
196 result(field)
197 type(netcdf_file_type), intent(in) :: handle
198 !< netCDF file handle
199 character(len=*), intent(in) :: label
200 !< netCDF field name in the file
201 type(netcdf_axis), intent(in) :: axes(:)
202 !< Axes along which field is defined
203 character(len=*), intent(in) :: longname
204 !< Long name of the netCDF field
205 character(len=*), intent(in) :: units
206 !< Field units of measurement
207 type(netcdf_field) :: field
208 !< netCDF field
209
210 integer :: rc
211 ! netCDF function return code
212 integer :: i
213 ! Loop index
21420 integer, allocatable :: dimids(:)
215 ! netCDF dimension IDs of axes
216 integer :: xtype
217 ! netCDF data type
218
219 ! Gather the axis netCDF dimension IDs
22020 allocate(dimids(size(axes)))
22168 dimids(:) = [(axes(i)%dimid, i = 1, size(axes))]
222
22320 field%label = label
224
225 ! Determine the corresponding netCDF data type
226 ! TODO: Support a `pack`-like argument
227 select case (kind(1.0))
228 case (real32)
22920 xtype = NF90_FLOAT
230 case (real64)
23120 xtype = NF90_DOUBLE
232 case default
233 call MOM_error(FATAL, "register_netcdf_field: Unknown kind(real).")
234 end select
235
236 ! Register the field variable
23720 rc = nf90_def_var(handle%ncid, label, xtype, dimids, field%varid)
238 call check_netcdf_call(rc, 'register_netcdf_field', &
23920 'File "' // handle%filename // '", Field "' // label // '"')
240
241 ! Assign attributes
242
24320 rc = nf90_put_att(handle%ncid, field%varid, 'long_name', longname)
244 call check_netcdf_call(rc, 'register_netcdf_field', &
245 'Attribute "long_name" of variable "' // label // '" in file "' &
24620 // handle%filename // '"')
247
24820 rc = nf90_put_att(handle%ncid, field%varid, 'units', units)
249 call check_netcdf_call(rc, 'register_netcdf_field', &
250 'Attribute "units" of variable "' // label // '" in file "' &
25120 // handle%filename // '"')
25240end function register_netcdf_field
253
254
255!> Create an axis and associated dimension in a netCDF file
2565function register_netcdf_axis(handle, label, units, longname, points, &
257 cartesian, sense) result(axis)
258 type(netcdf_file_type), intent(inout) :: handle
259 !< netCDF file handle
260 character(len=*), intent(in) :: label
261 !< netCDF axis name in the file
262 character(len=*), intent(in), optional :: units
263 !< Axis units of measurement
264 character(len=*), intent(in), optional :: longname
265 !< Long name of the axis
266 real, intent(in), optional :: points(:)
267 !< Values of axis points (for fixed axes)
268 character(len=*), intent(in), optional :: cartesian
269 !< Character denoting axis direction: X, Y, Z, T, or N for none
270 integer, intent(in), optional :: sense
271 !< Axis direction; +1 if axis increases upward or -1 if downward
272
273 type(netcdf_axis) :: axis
274 !< netCDF coordinate axis
275
276 integer :: xtype
277 ! netCDF external data type
278 integer :: rc
279 ! netCDF function return code
280 logical :: unlimited
281 ! True if the axis is unlimited in size (e.g. time)
282 integer :: axis_size
283 ! Either the number of points in the axis, or unlimited flag
284 integer :: axis_sense
285 ! Axis direction; +1 if axis increases upward or -1 if downward
2865 character(len=:), allocatable :: sense_attr
287 ! CF-compiant value of sense attribute (as 'positive')
288
289 ! Create the axis dimension
2905 unlimited = .false.
2915 if (present(cartesian)) then
2925 if (cartesian == 'T') unlimited = .true.
293 endif
294
295 ! Either the axis is explicitly set with data or is declared as unlimited
2965 if (present(points) .eqv. unlimited) then
297 call MOM_error(FATAL, &
2980 "Axis must either have explicit points or be a time axis ('T').")
299 endif
300
3015 axis%label = label
302
3035 if (present(points)) then
3044 axis_size = size(points)
3054 allocate(axis%points(axis_size))
306306 axis%points(:) = points(:)
307 else
3081 axis_size = NF90_UNLIMITED
309 endif
310
3115 rc = nf90_def_dim(handle%ncid, label, axis_size, axis%dimid)
312 call check_netcdf_call(rc, 'register_netcdf_axis', &
3135 'Dimension "' // label // '" in file "' // handle%filename // '"')
314
315 ! Determine the corresponding netCDF data type
316 ! TODO: Support a `pack`-like argument
317 select case (kind(1.0))
318 case (real32)
319 xtype = NF90_FLOAT
320 case (real64)
3215 xtype = NF90_DOUBLE
322 case default
323 call MOM_error(FATAL, "register_netcdf_axis: Unknown kind(real).")
324 end select
325
326 ! Create a variable corresponding to the axis
3275 rc = nf90_def_var(handle%ncid, label, xtype, axis%dimid, axis%varid)
328 call check_netcdf_call(rc, 'register_netcdf_axis', &
3295 'Variable ' // label // ' in file ' // handle%filename)
330
331 ! Define the time axis, if available
3325 if (unlimited) then
3331 handle%time_id = axis%varid
3341 handle%time_level = 0
3351 handle%time = NULLTIME
336 endif
337
338 ! Assign attributes if present
3395 if (present(longname)) then
3405 rc = nf90_put_att(handle%ncid, axis%varid, 'long_name', longname)
341 call check_netcdf_call(rc, 'register_netcdf_axis', &
342 'Attribute ''long_name'' of variable ' // label // ' in file ' &
3435 // handle%filename)
344 endif
345
3465 if (present(units)) then
3475 rc = nf90_put_att(handle%ncid, axis%varid, 'units', units)
348 call check_netcdf_call(rc, 'register_netcdf_axis', &
349 'Attribute ''units'' of variable ' // label // ' in file ' &
3505 // handle%filename)
351 endif
352
3535 if (present(cartesian)) then
3545 rc = nf90_put_att(handle%ncid, axis%varid, 'cartesian_axis', cartesian)
355 call check_netcdf_call(rc, 'register_netcdf_axis', &
356 'Attribute ''cartesian_axis'' of variable ' // label // ' in file ' &
3575 // handle%filename)
358 endif
359
3605 axis_sense = 0
3615 if (present(sense)) axis_sense = sense
362
3635 if (axis_sense /= 0) then
3644 select case (axis_sense)
365 case (1)
3664 sense_attr = 'up'
367 case (-1)
3680 sense_attr = 'down'
369 case default
370 call MOM_error(FATAL, 'register_netcdf_axis: sense must be either ' &
3714 // '0, 1, or -1.')
372 end select
3734 rc = nf90_put_att(handle%ncid, axis%varid, 'positive', sense_attr)
374 call check_netcdf_call(rc, 'register_netcdf_axis', &
375 'Attribute "positive" of variable "' // label // '" in file "' &
3764 // handle%filename // '"')
377 endif
37810end function register_netcdf_axis
379
380
381!> Write a 4D array to a compatible netCDF field
3820subroutine write_netcdf_field_4d(handle, field, values, time)
383 type(netcdf_file_type), intent(inout) :: handle
384 !< netCDF file handle
385 type(netcdf_field), intent(in) :: field
386 !< Field metadata
387 real, intent(in) :: values(:,:,:,:)
388 !< Field values
389 real, intent(in), optional :: time
390 !< Timestep index to write data
391
392 integer :: rc
393 ! netCDF return code
394 integer :: start(5)
395 ! Start indices, if timestep is included
396
397 ! Verify write mode
3980 if (handle%define_mode) &
3990 call enable_netcdf_write(handle)
400
4010 if (present(time)) then
4020 call update_netcdf_timestep(handle, time)
4030 start(:4) = 1
4040 start(5) = handle%time_level
4050 rc = nf90_put_var(handle%ncid, field%varid, values, start)
406 else
4070 rc = nf90_put_var(handle%ncid, field%varid, values)
408 endif
409 call check_netcdf_call(rc, 'write_netcdf_file', &
4100 'File "' // handle%filename // '", Field "' // field%label // '"')
4110end subroutine write_netcdf_field_4d
412
413
414!> Write a 3D array to a compatible netCDF field
4150subroutine write_netcdf_field_3d(handle, field, values, time)
416 type(netcdf_file_type), intent(inout) :: handle
417 !< netCDF file handle
418 type(netcdf_field), intent(in) :: field
419 !< Field metadata
420 real, intent(in) :: values(:,:,:)
421 !< Field values
422 real, intent(in), optional :: time
423 !< Timestep index to write data
424
425 integer :: rc
426 ! netCDF return code
427 integer :: start(4)
428 ! Start indices, if timestep is included
429
430 ! Verify write mode
4310 if (handle%define_mode) &
4320 call enable_netcdf_write(handle)
433
4340 if (present(time)) then
4350 call update_netcdf_timestep(handle, time)
4360 start(:3) = 1
4370 start(4) = handle%time_level
4380 rc = nf90_put_var(handle%ncid, field%varid, values, start)
439 else
4400 rc = nf90_put_var(handle%ncid, field%varid, values)
441 endif
442 call check_netcdf_call(rc, 'write_netcdf_file', &
4430 'File "' // handle%filename // '", Field "' // field%label // '"')
4440end subroutine write_netcdf_field_3d
445
446
447!> Write a 2D array to a compatible netCDF field
4480subroutine write_netcdf_field_2d(handle, field, values, time)
449 type(netcdf_file_type), intent(inout) :: handle
450 !< netCDF file handle
451 type(netcdf_field), intent(in) :: field
452 !< Field metadata
453 real, intent(in) :: values(:,:)
454 !< Field values
455 real, intent(in), optional :: time
456 !< Timestep index to write data
457
458 integer :: rc
459 ! netCDF return code
460 integer :: start(3)
461 ! Start indices, if timestep is included
462
463 ! Verify write mode
4640 if (handle%define_mode) &
4650 call enable_netcdf_write(handle)
466
4670 if (present(time)) then
4680 call update_netcdf_timestep(handle, time)
4690 start(:2) = 1
4700 start(3) = handle%time_level
4710 rc = nf90_put_var(handle%ncid, field%varid, values, start)
472 else
4730 rc = nf90_put_var(handle%ncid, field%varid, values)
474 endif
475 call check_netcdf_call(rc, 'write_netcdf_file', &
4760 'File "' // handle%filename // '", Field "' // field%label // '"')
4770end subroutine write_netcdf_field_2d
478
479
480!> Write a 1D array to a compatible netCDF field
48114subroutine write_netcdf_field_1d(handle, field, values, time)
482 type(netcdf_file_type), intent(inout) :: handle
483 !< netCDF file handle
484 type(netcdf_field), intent(in) :: field
485 !< Field metadata
486 real, intent(in) :: values(:)
487 !< Field values
488 real, intent(in), optional :: time
489 !< Timestep index to write data
490
491 integer :: rc
492 ! netCDF return code
493 integer :: start(2)
494 ! Start indices, if timestep is included
495
496 ! Verify write mode
49714 if (handle%define_mode) &
4980 call enable_netcdf_write(handle)
499
50014 if (present(time)) then
50112 call update_netcdf_timestep(handle, time)
50212 start(1) = 1
50312 start(2) = handle%time_level
50412 rc = nf90_put_var(handle%ncid, field%varid, values, start)
505 else
5062 rc = nf90_put_var(handle%ncid, field%varid, values)
507 endif
508 call check_netcdf_call(rc, 'write_netcdf_file', &
50914 'File "' // handle%filename // '", Field "' // field%label // '"')
51014end subroutine write_netcdf_field_1d
511
512
513!> Write a scalar to a compatible netCDF field
51442subroutine write_netcdf_field_0d(handle, field, scalar, time)
515 type(netcdf_file_type), intent(inout) :: handle
516 !< netCDF file handle
517 type(netcdf_field), intent(in) :: field
518 !< Field metadata
519 real, intent(in) :: scalar
520 !< Field values
521 real, intent(in), optional :: time
522 !< Timestep index to write data
523
524 integer :: rc
525 ! netCDF return code
526 integer :: start(1)
527 ! Start indices, if timestep is included
528
529 ! Verify write mode
53042 if (handle%define_mode) &
5310 call enable_netcdf_write(handle)
532
53342 if (present(time)) then
53442 call update_netcdf_timestep(handle, time)
53542 start(1) = handle%time_level
53642 rc = nf90_put_var(handle%ncid, field%varid, scalar, start)
537 else
5380 rc = nf90_put_var(handle%ncid, field%varid, scalar)
539 endif
540 call check_netcdf_call(rc, 'write_netcdf_file', &
54142 'File "' // handle%filename // '", Field "' // field%label // '"')
54242end subroutine write_netcdf_field_0d
543
544
545!> Write axis points to associated netCDF variable
5464subroutine write_netcdf_axis(handle, axis)
547 type(netcdf_file_type), intent(inout) :: handle
548 !< netCDF file handle
549 type(netcdf_axis), intent(in) :: axis
550 !< field variable
551
552 integer :: rc
553 ! netCDF return code
554
555 ! Verify write mode
5564 if (handle%define_mode) &
5572 call enable_netcdf_write(handle)
558
5594 rc = nf90_put_var(handle%ncid, axis%varid, axis%points)
560 call check_netcdf_call(rc, 'write_netcdf_axis', &
5614 'File "' // handle%filename // '", Axis "' // axis%label // '"')
5624end subroutine write_netcdf_axis
563
564
565!> Write a global attribute to a netCDF file
5662subroutine write_netcdf_attribute(handle, label, attribute)
567 type(netcdf_file_type), intent(in) :: handle
568 !< netCDF file handle
569 character(len=*), intent(in) :: label
570 !< File attribute
571 character(len=*), intent(in) :: attribute
572 !< File attribute value
573
574 integer :: rc
575 ! netCDF return code
576
5774 rc = nf90_put_att(handle%ncid, NF90_GLOBAL, label, attribute)
578 call check_netcdf_call(rc, 'write_netcdf_attribute', &
5792 'File "' // handle%filename // '", Attribute "' // label // '"')
5802end subroutine write_netcdf_attribute
581
582
583! This is a thin interface to nf90_inquire, designed to mirror the existing
584! I/O API. A more axis-aware system might not need this, but for now it's here
585!> Get the number of dimensions, variables, and timesteps in a netCDF file
5860subroutine get_netcdf_size(handle, ndims, nvars, nsteps)
587 type(netcdf_file_type), intent(in) :: handle
588 !< netCDF input file
589 integer, intent(out), optional :: ndims
590 !< number of dimensions in the file
591 integer, intent(out), optional :: nvars
592 !< number of variables in the file
593 integer, intent(out), optional :: nsteps
594 !< number of values in the file's unlimited axis
595
596 integer :: rc
597 ! netCDF return code
598 integer :: unlimited_dimid
599 ! netCDF dimension ID for unlimited time axis
600
601 rc = nf90_inquire(handle%ncid, &
602 nDimensions=ndims, &
603 nVariables=nvars, &
604 unlimitedDimId=unlimited_dimid &
6050 )
606 call check_netcdf_call(rc, 'get_netcdf_size', &
6070 'File "' // handle%filename // '"')
608
6090 rc = nf90_inquire_dimension(handle%ncid, unlimited_dimid, len=nsteps)
610 call check_netcdf_call(rc, 'get_netcdf_size', &
6110 'File "' // handle%filename // '"')
6120end subroutine get_netcdf_size
613
614
615!> Get the metadata of the registered fields in a netCDF file
6160subroutine get_netcdf_fields(handle, axes, fields)
617 type(netcdf_file_type), intent(inout) :: handle
618 !< netCDF file handle
619 type(netcdf_axis), intent(inout), allocatable :: axes(:)
620 !< netCDF file axes
621 type(netcdf_field), intent(inout), allocatable :: fields(:)
622 !< netCDF file fields
623
624 integer :: ndims
625 ! Number of netCDF dimensions
626 integer :: nvars
627 ! Number of netCDF dimensions
6280 type(netcdf_field), allocatable :: vars(:)
629 ! netCDF variables in handle
630 integer :: nfields
631 ! Number of fields in the file (i.e. non-axis variables)
6320 integer, allocatable :: dimids(:)
633 ! netCDF dimension IDs of file
6340 integer, allocatable :: varids(:)
635 ! netCDF variable IDs of file
636 integer :: unlim_dimid
637 ! netCDF dimension ID for the unlimited axis variable, if present
638 integer :: unlim_index
639 ! Index of the unlimited axis in axes(:), if present
640 character(len=NF90_MAX_NAME) :: label
641 ! Current dimension or variable label
642 integer :: len
643 ! Current dimension length
644 integer :: rc
645 ! netCDF return code
646 integer :: grp_ndims, grp_nvars
647 ! Group-based counts for nf90_inq_* (unused)
648 logical :: is_axis
649 ! True if the current variable is an axis
650 integer :: i, j, n
651
652 integer, save :: no_parent_groups = 0
653 ! Flag indicating exclusion of parent groups in netCDF file
654 ! NOTE: This must be passed as a variable, and cannot be declared as a
655 ! parameter.
656
657 rc = nf90_inquire(handle%ncid, &
658 nDimensions=ndims, &
659 nVariables=nvars, &
660 unlimitedDimId=unlim_dimid &
6610 )
662 call check_netcdf_call(rc, 'get_netcdf_fields', &
6630 'File "' // handle%filename // '"')
664
6650 allocate(dimids(ndims))
6660 rc = nf90_inq_dimids(handle%ncid, grp_ndims, dimids, no_parent_groups)
667 call check_netcdf_call(rc, 'get_netcdf_fields', &
6680 'File "' // handle%filename // '"')
669
6700 allocate(varids(nvars))
6710 rc = nf90_inq_varids(handle%ncid, grp_nvars, varids)
672 call check_netcdf_call(rc, 'get_netcdf_fields', &
6730 'File "' // trim(handle%filename) // '"')
674
675 ! Initialize unlim_index with an unreachable value (outside [1,ndims])
6760 unlim_index = -1
677
6780 allocate(axes(ndims))
6790 do i = 1, ndims
6800 rc = nf90_inquire_dimension(handle%ncid, dimids(i), name=label, len=len)
681 call check_netcdf_call(rc, 'get_netcdf_fields', &
6820 'File "' // trim(handle%filename) // '"')
683
684 ! Check for the unlimited axis
6850 if (dimids(i) == unlim_dimid) unlim_index = i
686
6870 axes(i)%dimid = dimids(i)
6880 axes(i)%label = trim(label)
6890 allocate(axes(i)%points(len))
690 enddo
691
692 ! We cannot know if every axis also has a variable representation, so we
693 ! over-allocate vars(:) and fill as fields are identified.
6940 allocate(vars(nvars))
695
6960 nfields = 0
6970 do i = 1, nvars
6980 rc = nf90_inquire_variable(handle%ncid, varids(i), name=label)
699 call check_netcdf_call(rc, 'get_netcdf_fields', &
7000 'File "' // trim(handle%filename) // '"')
701
702 ! Check if variable is an axis
7030 is_axis = .false.
7040 do j = 1, ndims
7050 if (label == axes(j)%label) then
7060 rc = nf90_get_var(handle%ncid, varids(i), axes(j)%points)
707 call check_netcdf_call(rc, 'get_netcdf_fields', &
7080 'File "' // trim(handle%filename) // '"')
7090 axes(j)%varid = varids(i)
710
7110 if (j == unlim_index) then
7120 handle%time_id = varids(i)
7130 handle%time_level = size(axes(j)%points)
7140 handle%time = NULLTIME
715 endif
716
7170 is_axis = .true.
7180 exit
719 endif
720 enddo
7210 if (is_axis) cycle
722
7230 nfields = nfields + 1
7240 vars(nfields)%label = trim(label)
7250 vars(nfields)%varid = varids(i)
726 enddo
727
7280 allocate(fields(nfields))
7290 fields(:) = vars(:nfields)
7300end subroutine get_netcdf_fields
731
732!> Return the name of a file from a netCDF handle
7330function get_netcdf_filename(handle)
734 type(netcdf_file_type), intent(in) :: handle !< A netCDF file handle
735 character(len=:), allocatable :: get_netcdf_filename !< The name of the file that this handle refers to.
736
7370 get_netcdf_filename = handle%filename
738
7390end function
740
741!> Read the values of a field from a netCDF file
7420subroutine read_netcdf_field(handle, field, values, bounds)
743 type(netcdf_file_type), intent(in) :: handle
744 type(netcdf_field), intent(in) :: field
745 real, intent(out) :: values(:,:)
746 integer, optional, intent(in) :: bounds(2,2)
747
748 integer :: rc
749 ! netCDF return code
750 integer :: istart(2)
751 ! Axis start index
752 integer :: icount(2)
753 ! Axis index count
754
7550 if (present(bounds)) then
7560 istart(:) = bounds(1,:)
7570 icount(:) = bounds(2,:) - bounds(1,:) + 1
7580 rc = nf90_get_var(handle%ncid, field%varid, values, start=istart, count=icount)
759 else
7600 rc = nf90_get_var(handle%ncid, field%varid, values)
761 endif
762 call check_netcdf_call(rc, 'read_netcdf_field', &
7630 'File "' // trim(handle%filename) // '", Field "' // trim(field%label) // '"')
7640end subroutine read_netcdf_field
765
766
767!> Set the current timestep of an open netCDF file
76854subroutine update_netcdf_timestep(handle, time)
769 type(netcdf_file_type), intent(inout) :: handle
770 !< netCDF file handle
771 real, intent(in) :: time
772 !< New model time
773
774 integer :: start(1)
775 !< Time axis start index array
776 integer :: rc
777 !< netCDF return code
778
77954 if (time > handle%time + epsilon(time)) then
7803 handle%time = time
7813 handle%time_level = handle%time_level + 1
782
783 ! Write new value to time axis
7846 start = [handle%time_level]
7853 rc = nf90_put_var(handle%ncid, handle%time_id, time, start=start)
786 call check_netcdf_call(rc, 'update_netcdf_timestep', &
7873 'File "' // handle%filename // '"')
788 endif
78954end subroutine update_netcdf_timestep
790
791
792!> Check netCDF function return codes, report the error log, and abort the run.
793162subroutine check_netcdf_call(ncerr, header, message)
794 integer, intent(in) :: ncerr
795 !< netCDF error code
796 character(len=*), intent(in) :: header
797 !< Message header (usually calling subroutine)
798 character(len=*), intent(in) :: message
799 !< Error message (usually action which instigated the error)
800
801162 character(len=:), allocatable :: errmsg
802 ! Full error message, including netCDF message
803
804162 if (ncerr /= NF90_NOERR) then
805 errmsg = trim(header) // ": " // trim(message) // new_line('/') &
8060 // trim(nf90_strerror(ncerr))
8070 call MOM_error(FATAL, errmsg)
808 endif
809162end subroutine check_netcdf_call
810
8110end module MOM_netcdf