Ignore:
Timestamp:
Mar 31, 2023, 8:42:57 PM (15 months ago)
Author:
lguez
Message:

Merge LMDZ_ECRad branch back into trunk!

Location:
LMDZ6/trunk
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • LMDZ6/trunk

  • LMDZ6/trunk/libf/phylmd/ecrad/easy_netcdf.F90

    r3908 r4489  
    3535  ! a NetCDF file
    3636  type netcdf_file
    37     integer :: ncid             ! NetCDF file ID
     37    integer :: ncid = -1! NetCDF file ID
    3838    integer :: iverbose ! Verbosity: 0 = report only fatal errors,
    3939                        !            1 = ...and warnings,
     
    5555    procedure :: create => create_netcdf_file
    5656    procedure :: close => close_netcdf_file
     57    procedure :: is_open
    5758    procedure :: get_real_scalar
     59    procedure :: get_int_scalar
    5860    procedure :: get_real_vector
    59     procedure :: get_integer_vector
     61    procedure :: get_int_vector
    6062    procedure :: get_real_matrix
    6163    procedure :: get_real_array3
     
    6567    procedure :: get_real_array3_indexed
    6668    procedure :: get_real_array4
    67     generic   :: get => get_real_scalar, get_real_vector, &
     69    procedure :: get_char_vector
     70    procedure :: get_char_matrix
     71    generic   :: get => get_real_scalar, get_int_scalar, &
     72         &              get_real_vector, get_int_vector, &
    6873         &              get_real_matrix, get_real_array3, &
    69          &              get_real_array4, get_integer_vector, &
     74         &              get_real_array4, &
    7075         &              get_real_scalar_indexed, get_real_vector_indexed, &
    71          &              get_real_matrix_indexed, get_real_array3_indexed
     76         &              get_real_matrix_indexed, get_real_array3_indexed, &
     77         &              get_char_vector, get_char_matrix
    7278    procedure :: get_real_scalar_attribute
    7379    procedure :: get_string_attribute
     
    8389    procedure :: put_real_scalar
    8490    procedure :: put_real_vector
     91    procedure :: put_int_vector
    8592    procedure :: put_real_matrix
    8693    procedure :: put_real_array3
     
    9198         &              put_real_matrix, put_real_array3, &
    9299         &              put_real_scalar_indexed, put_real_vector_indexed, &
    93          &              put_real_matrix_indexed
     100         &              put_real_matrix_indexed, put_int_vector
    94101    procedure :: set_verbose
    95102    procedure :: transpose_matrices
     
    101108    procedure :: attribute_exists
    102109    procedure :: global_attribute_exists
     110#ifdef NC_NETCDF4
     111    procedure :: copy_dimensions
     112#endif
     113    procedure :: copy_variable_definition
     114    procedure :: copy_variable
    103115    procedure, private :: get_array_dimensions
    104116    procedure, private :: get_variable_id
     
    257269    end if
    258270
     271    this%ncid = -1
     272
    259273  end subroutine close_netcdf_file
    260274
     
    367381
    368382    integer                        :: j, istatus
    369     integer                        :: dimids(NF90_MAX_VAR_DIMS)
     383    integer                        :: idimids(NF90_MAX_VAR_DIMS)
    370384
    371385    istatus = nf90_inquire_variable(this%ncid, ivarid, &
    372          &                          ndims=ndims, dimids=dimids)
     386         &                          ndims=ndims, dimids=idimids)
    373387    if (istatus /= NF90_NOERR) then
    374388      write(nulerr,'(a,i0,a,a)') '*** Error inquiring about NetCDF variable with id ', &
     
    379393    ndimlens(:) = 0
    380394    do j = 1,ndims
    381       istatus = nf90_inquire_dimension(this%ncid, dimids(j), len=ndimlens(j))
     395      istatus = nf90_inquire_dimension(this%ncid, idimids(j), len=ndimlens(j))
    382396      if (istatus /= NF90_NOERR) then
    383397        write(nulerr,'(a,i0,a,i0,a,a)') '*** Error reading length of dimension ', &
     
    420434
    421435  !---------------------------------------------------------------------
     436  ! Return true if file is open, false otherwise
     437  function is_open(this)
     438    class(netcdf_file) :: this
     439    logical            :: is_open
     440    is_open = (this%ncid >= 0)
     441  end function is_open
     442
     443  !---------------------------------------------------------------------
    422444  ! Return the number of dimensions of variable with name var_name, or
    423445  ! -1 if the variable is not found
     
    619641
    620642  !---------------------------------------------------------------------
     643  ! Read an integer scalar
     644  subroutine get_int_scalar(this, var_name, scalar)
     645    class(netcdf_file)           :: this
     646    character(len=*), intent(in) :: var_name
     647    integer,          intent(out):: scalar
     648
     649    integer                      :: istatus
     650    integer                      :: ivarid, ndims
     651    integer                      :: ndimlens(NF90_MAX_VAR_DIMS)
     652    integer                      :: j, ntotal
     653
     654    ! Inquire the ID, shape & size of the variable
     655    call this%get_variable_id(var_name, ivarid)
     656    call this%get_array_dimensions(ivarid, ndims, ndimlens)
     657
     658    ! Compute number of elements of the variable in the file
     659    ntotal = 1
     660    do j = 1, ndims
     661      ntotal = ntotal * ndimlens(j)
     662    end do
     663
     664    if (this%iverbose >= 3) then
     665      write(nulout,'(a,a)',advance='no') '  Reading ', var_name
     666      call this%print_variable_attributes(ivarid,nulout)
     667    end if
     668
     669    ! Abort if the number of elements is anything other than 1
     670    if (ntotal /= 1) then
     671      write(nulerr,'(a,a,a,i0,a)') '*** Error reading NetCDF variable ', &
     672           &    var_name, ' with total length ', ntotal, ' as a scalar'
     673      call my_abort('Error reading NetCDF file')
     674    end if
     675
     676    ! Read variable
     677    istatus = nf90_get_var(this%ncid, ivarid, scalar)
     678    if (istatus /= NF90_NOERR) then
     679      write(nulerr,'(a,a,a,a)') '*** Error reading NetCDF variable ', &
     680           &  var_name, ' as a scalar: ', trim(nf90_strerror(istatus))
     681      call my_abort('Error reading NetCDF file')
     682    end if
     683
     684  end subroutine get_int_scalar
     685
     686
     687  !---------------------------------------------------------------------
    621688  ! Read a scalar from a larger array, where "index" indexes the most
    622689  ! slowly varying dimension
    623   subroutine get_real_scalar_indexed(this, var_name, index, scalar)
     690  subroutine get_real_scalar_indexed(this, var_name, scalar, index)
    624691    class(netcdf_file)           :: this
    625692    character(len=*), intent(in) :: var_name
     
    676743
    677744  !---------------------------------------------------------------------
    678   ! Read a 1D array into "vector", which must be allocatable and will
    679   ! be reallocated if necessary
     745  ! Read a 1D real array into "vector", which must be allocatable and
     746  ! will be reallocated if necessary
    680747  subroutine get_real_vector(this, var_name, vector)
    681748    class(netcdf_file)           :: this
     
    734801
    735802  !---------------------------------------------------------------------
    736   ! Read a 1D integer array into "vector", which must be allocatable
     803  ! Read a 1D character array into "vector", which must be allocatable
    737804  ! and will be reallocated if necessary
    738   subroutine get_integer_vector(this, var_name, vector)
     805  subroutine get_char_vector(this, var_name, vector)
    739806    class(netcdf_file)           :: this
    740807    character(len=*), intent(in) :: var_name
    741     integer, allocatable, intent(out) :: vector(:)
     808    character(len=1), allocatable, intent(out) :: vector(:)
    742809
    743810    integer                      :: n  ! Length of vector
     
    784851    if (istatus /= NF90_NOERR) then
    785852      write(nulerr,'(a,a,a,a)') '*** Error reading NetCDF variable ', &
     853           &  var_name, ' as a vector of chars: ', trim(nf90_strerror(istatus))
     854      call my_abort('Error reading NetCDF file')
     855    end if
     856
     857  end subroutine get_char_vector
     858
     859
     860  !---------------------------------------------------------------------
     861  ! Read a 1D integer array into "vector", which must be allocatable
     862  ! and will be reallocated if necessary
     863  subroutine get_int_vector(this, var_name, vector)
     864
     865    class(netcdf_file)           :: this
     866    character(len=*), intent(in) :: var_name
     867    integer, allocatable, intent(out) :: vector(:)
     868
     869    integer                      :: n  ! Length of vector
     870    integer                      :: istatus
     871    integer                      :: ivarid, ndims
     872    integer                      :: ndimlens(NF90_MAX_VAR_DIMS)
     873    integer                      :: j
     874
     875    call this%get_variable_id(var_name, ivarid)
     876    call this%get_array_dimensions(ivarid, ndims, ndimlens)
     877
     878    ! Ensure variable has only one dimension in the file
     879    n = 1
     880    do j = 1, ndims
     881      n = n * ndimlens(j)
     882      if (j > 1 .and. ndimlens(j) > 1) then
     883        write(nulerr,'(a,a,a)') '*** Error reading NetCDF variable ', &
     884             & var_name, &
     885             & ' as a vector: all dimensions above the first must be singletons'
     886        call my_abort('Error reading NetCDF file')
     887      end if
     888    end do
     889
     890    ! Reallocate if necessary
     891    if (allocated(vector)) then
     892      if (size(vector) /= n) then
     893        if (this%iverbose >= 1) then
     894          write(nulout,'(a,a)') '  Warning: resizing vector to read ', var_name
     895        end if
     896        deallocate(vector)
     897        allocate(vector(n))
     898      end if
     899    else
     900      allocate(vector(n))
     901    end if
     902
     903    if (this%iverbose >= 3) then
     904      write(nulout,'(a,a,a,i0,a)',advance='no') '  Reading ', var_name, '(', n, ')'
     905      call this%print_variable_attributes(ivarid,nulout)
     906    end if
     907
     908    ! Read variable
     909    istatus = nf90_get_var(this%ncid, ivarid, vector)
     910    if (istatus /= NF90_NOERR) then
     911      write(nulerr,'(a,a,a,a)') '*** Error reading NetCDF variable ', &
    786912           &  var_name, ' as an integer vector: ', trim(nf90_strerror(istatus))
    787913      call my_abort('Error reading NetCDF file')
    788914    end if
    789915
    790   end subroutine get_integer_vector
    791 
     916  end subroutine get_int_vector
    792917
    793918  !---------------------------------------------------------------------
    794919  ! Read a vector of data from a larger array; the vector must be
    795920  ! allocatable and will be reallocated if necessary
    796   subroutine get_real_vector_indexed(this, var_name, index, vector)
     921  subroutine get_real_vector_indexed(this, var_name, vector, index)
    797922    class(netcdf_file)           :: this
    798923    character(len=*), intent(in) :: var_name
     
    9751100
    9761101  !---------------------------------------------------------------------
     1102  ! Read 2D array of characters into "matrix", which must be
     1103  ! allocatable and will be reallocated if necessary.  Whether to
     1104  ! transpose is specifed by the final optional argument, but can also
     1105  ! be specified by the do_transpose_2d class data member.
     1106  subroutine get_char_matrix(this, var_name, matrix, do_transp)
     1107    class(netcdf_file)           :: this
     1108    character(len=*), intent(in) :: var_name
     1109    character(len=1), allocatable, intent(inout) :: matrix(:,:)
     1110    logical, optional, intent(in):: do_transp ! Transpose data?
     1111
     1112    character(len=1), allocatable:: tmp_matrix(:,:)
     1113    integer                      :: ndimlen1, ndimlen2
     1114    integer                      :: istatus
     1115    integer                      :: ivarid, ndims
     1116    integer                      :: ndimlens(NF90_MAX_VAR_DIMS)
     1117    integer                      :: vstart(NF90_MAX_VAR_DIMS)
     1118    integer                      :: vcount(NF90_MAX_VAR_DIMS)
     1119    integer                      :: j, ntotal
     1120    logical                      :: do_transpose
     1121
     1122    ! Decide whether to transpose the array
     1123    if (present(do_transp)) then
     1124      do_transpose = do_transp
     1125    else
     1126      do_transpose = this%do_transpose_2d
     1127    end if
     1128
     1129    call this%get_variable_id(var_name, ivarid)
     1130    call this%get_array_dimensions(ivarid, ndims, ndimlens)
     1131
     1132    ! Ensure the variable has no more than two non-singleton
     1133    ! dimensions
     1134    ntotal = 1
     1135    do j = 1, ndims
     1136      ntotal = ntotal * ndimlens(j)
     1137      if (j > 2 .and. ndimlens(j) > 1) then
     1138        write(nulerr,'(a,a,a)') '*** Error reading NetCDF variable ', &
     1139           & var_name, &
     1140           & ' as a matrix: all dimensions above the second must be singletons'
     1141        call my_abort('Error reading NetCDF file')
     1142      end if
     1143    end do
     1144
     1145    ! Work out dimension lengths
     1146    if (ndims >= 2) then
     1147      ndimlen1 = ndimlens(1)
     1148      ndimlen2 = ntotal/ndimlen1
     1149    else
     1150      ndimlen1 = ntotal
     1151      ndimlen2 = 1
     1152    end if
     1153
     1154    if (do_transpose) then
     1155      ! Read and transpose
     1156      allocate(tmp_matrix(ndimlen1, ndimlen2))
     1157
     1158      ! Reallocate if necessary
     1159      if (allocated(matrix)) then
     1160        if (size(matrix,1) /= ndimlen2 .or. size(matrix,2) /= ndimlen1) then
     1161          if (this%iverbose >= 1) then
     1162            write(nulout,'(a,a)') '  Warning: resizing matrix to read ', var_name
     1163          end if
     1164          deallocate(matrix)
     1165          allocate(matrix(ndimlen2, ndimlen1))
     1166        end if
     1167      else
     1168        allocate(matrix(ndimlen2, ndimlen1))
     1169      end if
     1170
     1171      if (this%iverbose >= 3) then
     1172        write(nulout,'(a,a,a,i0,a,i0,a)',advance='no') '  Reading ', var_name, '(', &
     1173             &                            ndimlen2, ',', ndimlen1, ')'
     1174        call this%print_variable_attributes(ivarid,nulout)
     1175      end if
     1176
     1177      istatus = nf90_get_var(this%ncid, ivarid, tmp_matrix)
     1178      matrix = transpose(tmp_matrix)
     1179      deallocate(tmp_matrix)
     1180    else
     1181      ! Read data without transposition
     1182
     1183      ! Reallocate if necessary
     1184      if (allocated(matrix)) then
     1185        if (size(matrix,1) /= ndimlen1 .or. size(matrix,2) /= ndimlen2) then
     1186          if (this%iverbose >= 1) then
     1187            write(nulout,'(a,a)') '  Warning: resizing matrix to read ', var_name
     1188          end if
     1189          allocate(matrix(ndimlen1, ndimlen2))
     1190        end if
     1191      else
     1192        allocate(matrix(ndimlen1, ndimlen2))
     1193      end if
     1194
     1195      if (this%iverbose >= 3) then
     1196        write(nulout,'(a,a,a,i0,a,i0,a)',advance='no') '  Reading ', var_name, '(', &
     1197             &                            ndimlen1, ',', ndimlen2, ')'
     1198        call this%print_variable_attributes(ivarid,nulout)
     1199      end if
     1200
     1201      vstart = 1
     1202      vcount(1:2) = [ndimlen1,1]
     1203     
     1204      do j = 1,ndimlen2
     1205        vstart(2) = j
     1206        istatus = nf90_get_var(this%ncid, ivarid, matrix(:,j), start=vstart, count=vcount)
     1207      end do
     1208    end if
     1209
     1210    if (istatus /= NF90_NOERR) then
     1211      write(nulerr,'(a,a,a,a)') '*** Error reading NetCDF variable ', &
     1212           &    var_name, ' as a matrix of characters: ', trim(nf90_strerror(istatus))
     1213      call my_abort('Error reading NetCDF file')
     1214    end if
     1215
     1216  end subroutine get_char_matrix
     1217
     1218
     1219  !---------------------------------------------------------------------
    9771220  ! Read matrix of data from a larger array, which must be allocatable
    9781221  ! and will be reallocated if necessary.  Whether to transpose is
    9791222  ! specifed by the final optional argument, but can also be specified
    9801223  ! by the do_transpose_2d class data member.
    981   subroutine get_real_matrix_indexed(this, var_name, index, matrix, do_transp)
     1224  subroutine get_real_matrix_indexed(this, var_name, matrix, index, do_transp)
    9821225    class(netcdf_file)           :: this
    9831226    character(len=*), intent(in) :: var_name
     
    11871430      if (this%iverbose >= 3) then
    11881431        write(nulout,'(a,a,a,i0,i0,i0,a)',advance='no') '  Reading ', var_name, &
    1189              & ' (permuted dimensions ', i_permute_3d, ')'
     1432             & ' (permuting dimensions ', i_permute_3d, ')'
    11901433        call this%print_variable_attributes(ivarid,nulout)
    11911434      end if
     
    12351478  ! be allocatable and will be reallocated if necessary.  Whether to
    12361479  ! pemute is specifed by the final optional argument
    1237   subroutine get_real_array3_indexed(this, var_name, index, var, ipermute)
     1480  subroutine get_real_array3_indexed(this, var_name, var, index, ipermute)
    12381481    class(netcdf_file)                   :: this
    12391482    character(len=*), intent(in)         :: var_name
     
    13311574        write(nulout,'(a,i0,a,a,a,i0,i0,i0,a)') '  Reading slice ', index, &
    13321575             &  ' of ', var_name, &
    1333              & ' (permuted dimensions ', i_permute_3d, ')'
     1576             & ' (permuting dimensions ', i_permute_3d, ')'
    13341577      end if
    13351578
     
    14711714      if (this%iverbose >= 3) then
    14721715        write(nulout,'(a,a,a,i0,i0,i0,a)',advance='no') '  Reading ', var_name, &
    1473              & ' (permuted dimensions ', i_permute_4d, ')'
     1716             & ' (permuting dimensions ', i_permute_4d, ')'
    14741717        call this%print_variable_attributes(ivarid,nulout)
    14751718      end if
     
    15491792    !    allocate(character(len=i_attr_len) :: attr_str)
    15501793    if (len(attr_str) < i_attr_len) then
    1551       write(nulerr,'(a,a)') '*** Error: not enough space to read attribute ', attr_name
     1794      write(nulerr,'(a,a)') '*** Not enough space to read attribute ', attr_name
    15521795      call my_abort('Error reading NetCDF file')
    15531796    end if
     
    15771820    real(jprb),       intent(out) :: attr
    15781821
    1579     integer :: i_attr_len, ivarid
     1822    integer :: ivarid
    15801823    integer :: istatus
    1581     integer :: j
    15821824
    15831825    istatus = nf90_inq_varid(this%ncid, var_name, ivarid)
     
    16241866    !    allocate(character(len=i_attr_len) :: attr_str)
    16251867    if (len(attr_str) < i_attr_len) then
    1626       write(nulerr,'(a,a)') '*** Error: not enough space to read global attribute ', attr_name
     1868      write(nulerr,'(a,a)') '*** Not enough space to read global attribute ', attr_name
    16271869      call my_abort('Error reading NetCDF file')
    16281870    end if
     
    16521894
    16531895    character(len=4000) :: attr_str
    1654     integer :: i_attr_len
    16551896    integer :: istatus
    1656     integer :: j
    16571897
    16581898    if (this%iverbose >= 4) then
    16591899      istatus = nf90_get_att(this%ncid, ivarid, 'long_name', attr_str)
    16601900      if (istatus == NF90_NOERR) then
    1661         write(iunit, '(a)') ':'
    1662         write(iunit, '(a,a)', advance='no') '    ', trim(attr_str)
     1901        write(iunit, '(a,a,a)', advance='no') ': "', trim(attr_str), '"'
    16631902        istatus = nf90_get_att(this%ncid, ivarid, 'units', attr_str)
    16641903        if (istatus == NF90_NOERR) then
     
    17211960  ! names.
    17221961  subroutine define_variable(this, var_name, dim1_name, dim2_name, dim3_name, &
    1723        &                     long_name, units_str, comment_str, standard_name, is_double, &
    1724        &                     data_type_name, fill_value, deflate_level, shuffle, chunksizes)
     1962       &                     dim4_name, long_name, units_str, comment_str, &
     1963       &                     standard_name, is_double, data_type_name, fill_value, &
     1964       &                     deflate_level, shuffle, chunksizes, ndims)
    17251965    class(netcdf_file)                     :: this
    17261966    character(len=*), intent(in)           :: var_name
    17271967    character(len=*), intent(in), optional :: long_name, units_str, comment_str, standard_name
    1728     character(len=*), intent(in), optional :: dim1_name, dim2_name, dim3_name
     1968    character(len=*), intent(in), optional :: dim1_name, dim2_name, dim3_name, dim4_name
    17291969    logical,          intent(in), optional :: is_double
    17301970    character(len=*), intent(in), optional :: data_type_name
     
    17331973    logical,          intent(in), optional :: shuffle ! Shuffle bytes before compression
    17341974    integer, dimension(:), intent(in), optional :: chunksizes
    1735 
    1736     integer :: istatus, ndims, ivarid
     1975    integer,          intent(in), optional :: ndims
     1976
     1977    integer :: istatus, ndims_local, ndims_input, ivarid
    17371978    integer, dimension(NF90_MAX_VAR_DIMS) :: idimids
    17381979    integer :: data_type
    17391980
    1740     if (present(dim1_name)) then
     1981    ! Sometimes a program may not know at compile time the exact
     1982    ! dimensions of a variable - if ndims is present then only up to
     1983    ! that many dimensions will be defined
     1984    ndims_input = 4
     1985    if (present(ndims)) then
     1986      ndims_input = ndims
     1987    end if
     1988
     1989    if (present(dim1_name) .and. ndims_input >= 1) then
    17411990      ! Variable is at least one dimensional
    1742       ndims = 1
     1991      ndims_local = 1
    17431992      istatus = nf90_inq_dimid(this%ncid, dim1_name, idimids(1))
    17441993      if (istatus /= NF90_NOERR) then
     
    17471996        call my_abort('Error writing NetCDF file')
    17481997      end if
    1749       if (present(dim2_name)) then
     1998      if (present(dim2_name) .and. ndims_input >= 2) then
    17501999        ! Variable is at least two dimensional
    1751         ndims = 2
     2000        ndims_local = 2
    17522001        istatus = nf90_inq_dimid(this%ncid, dim2_name, idimids(2))
    17532002        if (istatus /= NF90_NOERR) then
     
    17562005          call my_abort('Error writing NetCDF file')
    17572006        end if
    1758         if (present(dim3_name)) then
     2007        if (present(dim3_name) .and. ndims_input >= 3) then
    17592008          ! Variable is at least three dimensional
    1760           ndims = 3
     2009          ndims_local = 3
    17612010          istatus = nf90_inq_dimid(this%ncid, dim3_name, idimids(3))
    17622011          if (istatus /= NF90_NOERR) then
     
    17652014            call my_abort('Error writing NetCDF file')
    17662015          end if
     2016          if (present(dim4_name) .and. ndims_input >= 4) then
     2017            ! Variable is at least three dimensional
     2018            ndims_local = 4
     2019            istatus = nf90_inq_dimid(this%ncid, dim4_name, idimids(4))
     2020            if (istatus /= NF90_NOERR) then
     2021              write(nulerr,'(a,a,a,a)') '*** Error inquiring ID of dimension ', &
     2022                   &             dim4_name, ': ', trim(nf90_strerror(istatus))
     2023              call my_abort('Error writing NetCDF file')
     2024            end if
     2025          end if
    17672026        end if
    17682027      end if
    17692028    else
    17702029      ! Variable is a scalar
    1771       ndims = 0
     2030      ndims_local = 0
    17722031    end if
    17732032
    17742033    ! Read output precision from optional argument "is_double" if
    17752034    ! present, otherwise from default output precision for this file
    1776     data_type = NF90_FLOAT ! Default
    17772035    if (present(data_type_name)) then
    17782036      if (data_type_name == 'double') then
     
    17872045        data_type = NF90_FLOAT
    17882046      else
    1789         write(nulerr,'(a,a,a)') '*** Error: netCDF data type "', data_type_name, '" not supported'
     2047        write(nulerr,'(a,a,a)') '*** NetCDF data type "', data_type_name, '" not supported'
    17902048        call my_abort('Error writing NetCDF file')
    17912049      end if
    17922050    else if (present(is_double)) then
     2051      if (is_double) then
     2052        data_type = NF90_DOUBLE
     2053      else
     2054        data_type = NF90_FLOAT
     2055      end if
     2056    else if (this%is_double_precision) then
    17932057      data_type = NF90_DOUBLE
     2058    else
     2059      data_type = NF90_FLOAT
    17942060    end if
    17952061
    17962062    ! Define variable
    17972063#ifdef NC_NETCDF4
    1798     istatus = nf90_def_var(this%ncid, var_name, data_type, idimids(1:ndims), &
     2064    istatus = nf90_def_var(this%ncid, var_name, data_type, idimids(1:ndims_local), &
    17992065         & ivarid, deflate_level=deflate_level, shuffle=shuffle, chunksizes=chunksizes)
    18002066#else
    1801     istatus = nf90_def_var(this%ncid, var_name, data_type, idimids(1:ndims), ivarid)
     2067    istatus = nf90_def_var(this%ncid, var_name, data_type, idimids(1:ndims_local), ivarid)
    18022068#endif
    18032069    if (istatus /= NF90_NOERR) then
     
    18112077      istatus = nf90_put_att(this%ncid, ivarid, "long_name", long_name)
    18122078      if (this%iverbose >= 4) then
    1813         write(nulout,'(a,a,a,a)') '  Defining ',trim(var_name),': ',long_name
     2079        write(nulout,'(a,a,a,a,a)', advance='no') '  Defining ',trim(var_name), &
     2080             &  ': "', long_name, '"'
    18142081      end if
    18152082    else
    18162083      if (this%iverbose >= 4) then
    1817         write(nulout,'(a,a)') '  Defining ',trim(var_name)
    1818       end if
    1819     end if
     2084        write(nulout,'(a,a)', advance='no') '  Defining ',trim(var_name)
     2085      end if
     2086    end if
     2087
    18202088    if (present(units_str)) then
    18212089      istatus = nf90_put_att(this%ncid, ivarid, "units", units_str)
    1822     end if
     2090      if (this%iverbose >= 4) then
     2091        if (trim(units_str) == '1') then
     2092          write(nulout, '(a)') ' (dimensionless)'
     2093        else
     2094          write(nulout, '(a,a,a)') ' (', trim(units_str), ')'
     2095        end if
     2096      end if
     2097    else
     2098      if (this%iverbose >= 4) then
     2099        write(nulout, '(1x)')
     2100      end if
     2101    end if
     2102
    18232103    if (present(standard_name)) then
    18242104      istatus = nf90_put_att(this%ncid, ivarid, "standard_name", standard_name)
     
    18632143  subroutine put_global_attributes(this, title_str, inst_str, source_str, &
    18642144       &  comment_str, references_str, creator_name, creator_email_str, &
    1865        &  contributor_name, project_str, conventions_str)
     2145       &  contributor_name, project_str, conventions_str, prior_history_str)
    18662146    class(netcdf_file)                     :: this
    18672147
     
    18722152    character(len=*), intent(in), optional :: contributor_name, project_str
    18732153    character(len=*), intent(in), optional :: comment_str, conventions_str
    1874     character(len=*), intent(in), optional :: references_str
     2154    character(len=*), intent(in), optional :: references_str, prior_history_str
    18752155
    18762156    character(len=32)   :: date_time_str
     
    18872167         &   time_vals(1), time_vals(2), time_vals(3), time_vals(5), time_vals(6), time_vals(7)
    18882168
    1889     history_str = trim(date_time_str) // ': ' // trim(command_line_str)
     2169    if (present(prior_history_str)) then
     2170      history_str = trim(prior_history_str) // new_line('a') &
     2171           &  // trim(date_time_str) // ': ' // trim(command_line_str)
     2172    else
     2173      history_str = trim(date_time_str) // ': ' // trim(command_line_str)
     2174    end if
    18902175
    18912176    if (present(title_str))   i=nf90_put_att(this%ncid, NF90_GLOBAL, "title", title_str)
     
    20572342
    20582343  !---------------------------------------------------------------------
     2344  ! Save an integer vector with name var_name in the file
     2345  subroutine put_int_vector(this, var_name, var)
     2346    class(netcdf_file)             :: this
     2347    character(len=*), intent(in)   :: var_name
     2348    integer,          intent(in)   :: var(:)
     2349
     2350    integer :: ivarid, ndims, istatus
     2351    integer(kind=jpib) :: ntotal
     2352    integer :: ndimlens(NF90_MAX_VAR_DIMS)
     2353
     2354    call this%end_define_mode()
     2355
     2356    ! Check the vector is of the right length
     2357    call this%get_variable_id(var_name, ivarid)
     2358    call this%get_array_dimensions(ivarid, ndims, ndimlens, ntotal)
     2359    if (ntotal /= size(var,kind=jpib)) then
     2360      write(nulerr,'(a,i0,a,a,a,i0)') '*** Error: attempt to write vector of length ', &
     2361           & size(var), ' to ', var_name, ' which has total length ', ntotal
     2362      call my_abort('Error writing NetCDF file')
     2363    end if
     2364
     2365    ! Save the vector
     2366    istatus = nf90_put_var(this%ncid, ivarid, var)
     2367    if (istatus /= NF90_NOERR) then
     2368      write(nulerr,'(a,a,a,a)') '*** Error writing vector ', var_name, ': ', &
     2369           &                    trim(nf90_strerror(istatus))
     2370      call my_abort('Error writing NetCDF file')
     2371    end if
     2372
     2373  end subroutine put_int_vector
     2374
     2375
     2376  !---------------------------------------------------------------------
    20592377  ! Save a vector slice with name var_name in the file
    2060   subroutine put_real_vector_indexed(this, var_name, index, var)
     2378  subroutine put_real_vector_indexed(this, var_name, var, index2, index3)
    20612379    class(netcdf_file)             :: this
    20622380    character(len=*), intent(in)   :: var_name
    20632381    real(jprb), intent(in)         :: var(:)
    2064     integer, intent(in)            :: index
     2382    integer, intent(in)            :: index2
     2383    integer, intent(in), optional  :: index3
    20652384
    20662385    integer :: ivarid, ndims, istatus
     
    20702389    integer :: vcount(NF90_MAX_VAR_DIMS)
    20712390
     2391    character(len=512) :: var_slice_name
     2392    integer :: index_last
     2393
    20722394    call this%end_define_mode()
    20732395
     
    20762398    call this%get_array_dimensions(ivarid, ndims, ndimlens, ntotal)
    20772399    ntotal = ntotal / ndimlens(ndims)
     2400    if (present(index3)) then
     2401      ntotal = ntotal / ndimlens(ndims-1)
     2402      index_last = index3
     2403      write(var_slice_name,'(a,a,i0,a,i0,a)') var_name, '(:,', index2, ',', index3, ')'
     2404    else
     2405      index_last = index2
     2406      write(var_slice_name,'(a,a,i0,a)') var_name, '(:,', index2, ')'
     2407    end if
     2408
    20782409    if (ntotal /= size(var,kind=jpib)) then
    2079       write(nulerr,'(a,i0,a,a,a,i0)') '*** Error: attempt to write vector of length ', &
    2080            & size(var), ' to slice of ', var_name, ' which has length ', ntotal
     2410      write(nulerr,'(a,i0,a,a,i0)') '*** Error: attempt to write vector of length ', &
     2411           & size(var), ' to ', trim(var_slice_name), ' which has length ', ntotal
    20812412      call my_abort('Error writing NetCDF file')
    20822413    end if
    2083     if (index < 1 .or. index > ndimlens(ndims)) then
    2084       write(nulerr,'(a,i0,a,a,a,i0)') '*** Error: attempt to write vector to slice ', &
    2085            &  index, ' of ', var_name, ' which has outer dimension  ', ndimlens(ndims)
     2414    if (index_last < 1 .or. index_last > ndimlens(ndims)) then
     2415      write(nulerr,'(a,a,a,i0)') '*** Error: attempt to write vector to ', &
     2416           &  trim(var_slice_name), ' which has outer dimension  ', ndimlens(ndims)
    20862417      call my_abort('Error writing NetCDF file')
    20872418    end if
     
    20892420    ! Save the vector
    20902421    vstart(1:ndims-1) = 1
    2091     vstart(ndims)     = index
    20922422    vcount(1:ndims-1) = ndimlens(1:ndims-1)
    20932423    vcount(ndims)     = 1
     2424    if (present(index3)) then
     2425      vstart(ndims)   = index3
     2426      vstart(ndims-1) = index2
     2427      vcount(ndims-1) = 1
     2428    else
     2429      vstart(ndims)   = index2
     2430    end if
     2431
    20942432    istatus = nf90_put_var(this%ncid, ivarid, var, start=vstart, count=vcount)
    20952433    if (istatus /= NF90_NOERR) then
    2096       write(nulerr,'(a,a,a,a)') '*** Error writing vector to ', var_name, ': ', &
    2097            &                    trim(nf90_strerror(istatus))
     2434      write(nulerr,'(a,a,a,a)') '*** Error writing vector to ', trim(var_slice_name), &
     2435           &  ': ', trim(nf90_strerror(istatus))
    20982436      call my_abort('Error writing NetCDF file')
    20992437    end if
     
    21712509  ! dimensions if either optional argument transp is .true., or the
    21722510  ! transpose_matrices method has already been called.
    2173   subroutine put_real_matrix_indexed(this, var_name, index, var, do_transp)
     2511  subroutine put_real_matrix_indexed(this, var_name, var, index3, index4, do_transp)
    21742512    class(netcdf_file)             :: this
    21752513    character(len=*), intent(in)   :: var_name
    21762514    real(jprb), intent(in)         :: var(:,:)
    2177     integer, intent(in)            :: index
     2515    integer, intent(in)            :: index3
     2516    integer, intent(in), optional  :: index4
    21782517
    21792518    real(jprb), allocatable        :: var_transpose(:,:)
    2180     logical, optional, intent(in):: do_transp
     2519    logical, optional, intent(in)  :: do_transp
    21812520
    21822521    integer :: ivarid, ndims, nvarlen, istatus
     
    21862525    integer :: vcount(NF90_MAX_VAR_DIMS)
    21872526
     2527    character(len=512) :: var_slice_name
     2528
    21882529    logical :: do_transpose
    21892530
     
    22042545    ! ntotal is zero then there must be an unlimited dimension)
    22052546    ntotal = ntotal / ndimlens(ndims)
     2547    if (present(index4)) then
     2548      ntotal = ntotal / ndimlens(ndims-1)
     2549      write(var_slice_name,'(a,a,i0,a,i0,a)') var_name, '(:,:,', index3, ',', index4, ')'
     2550    else
     2551      write(var_slice_name,'(a,a,i0,a)') var_name, '(:,:,', index3, ')'
     2552    end if
    22062553    if (ntotal /= size(var,kind=jpib) .and. ntotal /= 0) then
    22072554      write(nulerr,'(a,i0,a,a,a,i0)') '*** Error: attempt to write matrix of total size ', &
    2208            & nvarlen, ' to ', var_name, ' which has total size ', ntotal
     2555           & nvarlen, ' to ', trim(var_slice_name), ' which has total size ', ntotal
    22092556      call my_abort('Error writing NetCDF file')
    22102557    end if
    22112558
    22122559    vstart(1:ndims-1) = 1
    2213     vstart(ndims)     = index
    22142560    vcount(1:ndims-1) = ndimlens(1:ndims-1)
    22152561    vcount(ndims)     = 1
     2562    if (present(index4)) then
     2563      vstart(ndims)   = index4
     2564      vstart(ndims-1) = index3
     2565      vcount(ndims-1) = 1
     2566    else
     2567      vstart(ndims)   = index3
     2568    end if
    22162569
    22172570    if (do_transpose) then
    22182571      ! Save the matrix with transposition
    22192572      if (this%iverbose >= 3) then
    2220         write(nulout,'(a,i0,a,a,a)') '  Writing slice ', index, ' of ', var_name, &
     2573        write(nulout,'(a,a,a)') '  Writing ', trim(var_slice_name), &
    22212574             & ' (transposing dimensions)'
    22222575      end if
     
    22282581      ! Save the matrix without transposition
    22292582      if (this%iverbose >= 3) then
    2230         write(nulout,'(a,i0,a,a)') '  Writing slice ', index, ' of ', var_name
     2583        write(nulout,'(a,a)') '  Writing ', trim(var_slice_name)
    22312584      end if
    22322585      istatus = nf90_put_var(this%ncid, ivarid, var, start=vstart, count=vcount)
     
    22342587
    22352588    if (istatus /= NF90_NOERR) then
    2236       write(nulerr,'(a,a,a,a)') '*** Error writing matrix ', var_name, &
     2589      write(nulerr,'(a,a,a)') '*** Error writing ', trim(var_slice_name), &
    22372590           &                    ': ', trim(nf90_strerror(istatus))
    22382591      call my_abort('Error writing NetCDF file')
     
    22912644      if (this%iverbose >= 3) then
    22922645        write(nulout,'(a,a,a,i0,i0,i0,a)') '  Writing ', var_name, &
    2293              & ' (permuted dimensions: ', i_permute_3d, ')'
     2646             & ' (permuting dimensions: ', i_permute_3d, ')'
    22942647      end if
    22952648      n_dimlens_permuted = (/ size(var,i_permute_3d(1)), &
    22962649           &                  size(var,i_permute_3d(2)), &
    22972650           &                  size(var,i_permute_3d(3))  /)
    2298       if (this%iverbose >= 4) then
    2299         write(nulout,'(a,i0,a,i0,a,i0,a,i0,a,i0,a,i0,a)') '    (', &
    2300              &  n_dimlens_permuted(1), ',', n_dimlens_permuted(2), &
    2301              &  ',', n_dimlens_permuted(3), ') -> (', ndimlens(1), &
    2302              &  ',', ndimlens(2), ',', ndimlens(3), ')'
    2303       end if
     2651      !! FIX: This makes it look like the dimensions have stayed the same
     2652      ! if (this%iverbose >= 4) then
     2653      !   write(nulout,'(a,i0,a,i0,a,i0,a,i0,a,i0,a,i0,a)') '    (', &
     2654      !        &  n_dimlens_permuted(1), ',', n_dimlens_permuted(2), &
     2655      !        &  ',', n_dimlens_permuted(3), ') -> (', ndimlens(1), &
     2656      !        &  ',', ndimlens(2), ',', ndimlens(3), ')'
     2657      ! end if
    23042658      allocate(var_permute(n_dimlens_permuted(1), &
    23052659           &   n_dimlens_permuted(2), n_dimlens_permuted(3)))
     
    23262680  end subroutine put_real_array3
    23272681
     2682
     2683#ifdef NC_NETCDF4
     2684  !---------------------------------------------------------------------
     2685  ! Copy dimensions from "infile" to "this"
     2686  subroutine copy_dimensions(this, infile)
     2687    class(netcdf_file)            :: this
     2688    type(netcdf_file), intent(in) :: infile
     2689
     2690    integer :: jdim
     2691    integer :: ndims
     2692    integer :: idimids(1024)
     2693    integer :: dimlen
     2694    character(len=512) :: dimname
     2695    integer :: istatus
     2696    integer :: include_parents
     2697   
     2698    include_parents = 0
     2699
     2700    istatus = nf90_inq_dimids(infile%ncid, ndims, idimids, include_parents)
     2701    if (istatus /= NF90_NOERR) then
     2702      write(nulerr,'(a,a)') '*** Error reading dimensions of NetCDF file: ', &
     2703           trim(nf90_strerror(istatus))
     2704      call my_abort('Error reading NetCDF file')
     2705    end if
     2706
     2707    do jdim = 1,ndims
     2708      istatus = nf90_inquire_dimension(infile%ncid, idimids(jdim), &
     2709           &  name=dimname, len=dimlen)
     2710      if (istatus /= NF90_NOERR) then
     2711        write(nulerr,'(a,a)') '*** Error reading NetCDF dimension properties: ', &
     2712             trim(nf90_strerror(istatus))
     2713        call my_abort('Error reading NetCDF file')
     2714      end if
     2715      call this%define_dimension(trim(dimname), dimlen)
     2716    end do
     2717
     2718  end subroutine copy_dimensions
     2719#endif
     2720
     2721  !---------------------------------------------------------------------
     2722  ! Copy variable definition and attributes from "infile" to "this"
     2723  subroutine copy_variable_definition(this, infile, var_name)
     2724    class(netcdf_file)            :: this
     2725    type(netcdf_file), intent(in) :: infile
     2726    character(len=*),  intent(in) :: var_name
     2727
     2728#ifdef NC_NETCDF4
     2729    integer :: deflate_level  ! Compression: 0 (none) to 9 (most)
     2730    logical :: shuffle        ! Shuffle bytes before compression
     2731    integer :: chunksizes(NF90_MAX_VAR_DIMS)
     2732#endif
     2733    integer :: data_type
     2734    integer :: ndims
     2735    integer :: idimids_in(NF90_MAX_VAR_DIMS)
     2736    integer :: idimids_out(NF90_MAX_VAR_DIMS)
     2737    integer :: nattr
     2738    character(len=512) :: attr_name
     2739    character(len=512) :: dim_name
     2740
     2741    integer :: istatus
     2742    integer :: ivarid_in, ivarid_out
     2743    integer :: jattr, jdim
     2744
     2745    if (this%iverbose >= 4) then
     2746      write(nulout,'(a,a)') '  Copying definition of ', trim(var_name)
     2747    end if
     2748
     2749    ! Get variable ID from name
     2750    istatus = nf90_inq_varid(infile%ncid, var_name, ivarid_in)
     2751    if (istatus /= NF90_NOERR) then
     2752      write(nulerr,'(a,i0,a)') '*** Error inquiring about NetCDF variable "', &
     2753           & var_name, '": ', trim(nf90_strerror(istatus))
     2754      call my_abort('Error reading NetCDF file')
     2755    end if
     2756
     2757    ! Get variable properties
     2758#ifdef NC_NETCDF4
     2759    istatus = nf90_inquire_variable(infile%ncid, ivarid_in, xtype=data_type, ndims=ndims, &
     2760         &  dimids=idimids_in, chunksizes=chunksizes, deflate_level=deflate_level, &
     2761         &  shuffle=shuffle, natts=nattr)
     2762#else
     2763    istatus = nf90_inquire_variable(infile%ncid, ivarid_in, xtype=data_type, ndims=ndims, &
     2764         &  dimids=idimids_in, natts=nattr)
     2765#endif
     2766    if (istatus /= NF90_NOERR) then
     2767      write(nulerr,'(a,a)') '*** Error reading NetCDF variable properties: ', &
     2768           trim(nf90_strerror(istatus))
     2769      call my_abort('Error reading NetCDF file')
     2770    end if
     2771
     2772    ! Map dimension IDs
     2773    do jdim = 1,ndims
     2774      istatus = nf90_inquire_dimension(infile%ncid, idimids_in(jdim), name=dim_name)
     2775      if (istatus /= NF90_NOERR) then
     2776        write(nulerr,'(a,a)') '*** Error reading NetCDF dimension name: ', &
     2777             trim(nf90_strerror(istatus))
     2778        call my_abort('Error reading NetCDF file')
     2779      end if
     2780
     2781      istatus = nf90_inq_dimid(this%ncid, trim(dim_name), idimids_out(jdim))
     2782      if (istatus /= NF90_NOERR) then
     2783        write(nulerr,'(a,a)') '*** Error reading NetCDF dimension ID: ', &
     2784             trim(nf90_strerror(istatus))
     2785        call my_abort('Error reading NetCDF file')
     2786      end if
     2787    end do
     2788
     2789    ! Create variable
     2790#ifdef NC_NETCDF4
     2791    istatus = nf90_def_var(this%ncid, var_name, data_type, idimids_out(1:ndims), &
     2792         & ivarid_out, deflate_level=deflate_level, shuffle=shuffle, chunksizes=chunksizes(1:ndims))
     2793#else
     2794    istatus = nf90_def_var(this%ncid, var_name, data_type, idimids_out(1:ndims), ivarid_out)
     2795#endif
     2796    if (istatus /= NF90_NOERR) then
     2797      write(nulerr,'(a,a,a,a)') '*** Error defining variable "', var_name, &
     2798           &                    '": ', trim(nf90_strerror(istatus))
     2799      call my_abort('Error writing NetCDF file')
     2800    end if
     2801
     2802    ! Copy attributes
     2803    do jattr = 1,nattr
     2804      istatus = nf90_inq_attname(infile%ncid, ivarid_in, jattr, attr_name)
     2805      if (istatus /= NF90_NOERR) then
     2806        write(nulerr,'(a,a)') '*** Error reading attribute: ', &
     2807             &  trim(nf90_strerror(istatus))
     2808        call my_abort('Error reading NetCDF file')
     2809      end if
     2810      istatus = nf90_copy_att(infile%ncid, ivarid_in, trim(attr_name), &
     2811           &                    this%ncid, ivarid_out)
     2812
     2813    end do
     2814
     2815  end subroutine copy_variable_definition
     2816
     2817
     2818  !---------------------------------------------------------------------
     2819  ! Copy variable from "infile" to "this"
     2820  subroutine copy_variable(this, infile, var_name)
     2821    class(netcdf_file)             :: this
     2822    class(netcdf_file), intent(in) :: infile
     2823    character(len=*),   intent(in) :: var_name
     2824
     2825    integer :: ivarid_in, ivarid_out
     2826    integer :: ndims
     2827    integer :: ndimlens(NF90_MAX_VAR_DIMS)
     2828    integer(kind=jpib) :: ntotal
     2829    integer :: data_type
     2830    integer :: istatus
     2831
     2832    ! We use the Fortran-77 functions because they don't check that
     2833    ! the rank of the arguments is correct
     2834    integer, external :: nf_get_var_double, nf_put_var_double
     2835    integer, external :: nf_get_var_int, nf_put_var_int
     2836
     2837    real(kind=jprd), allocatable :: data_real(:)
     2838    integer,         allocatable :: data_int(:)
     2839
     2840    ! If we are in define mode, exit define mode
     2841    call this%end_define_mode()
     2842
     2843    if (this%iverbose >= 4) then
     2844      write(nulout,'(a,a)') '  Copying ', trim(var_name)
     2845    end if
     2846
     2847    call infile%get_variable_id(var_name, ivarid_in)
     2848    call infile%get_array_dimensions(ivarid_in, ndims, ndimlens, ntotal)
     2849    istatus = nf90_inquire_variable(infile%ncid, ivarid_in, xtype=data_type)
     2850    if (istatus /= NF90_NOERR) then
     2851      write(nulerr,'(a,a,a,a)') '*** Error reading variable "', var_name, '": ', &
     2852           &  trim(nf90_strerror(istatus))
     2853      call my_abort('Error reading NetCDF file')
     2854    end if
     2855
     2856    call infile%get_variable_id(var_name, ivarid_out)
     2857    if (data_type == NF90_DOUBLE .or. data_type == NF90_FLOAT) then
     2858      allocate(data_real(ntotal))
     2859      !istatus = nf90_get_var(infile%ncid, ivarid_in, data_real(1))
     2860      istatus = nf_get_var_double(infile%ncid, ivarid_in, data_real)
     2861      if (istatus /= NF90_NOERR) then
     2862        deallocate(data_real)
     2863        write(nulerr,'(a,a,a,a)') '*** Error reading variable "', var_name, '": ', &
     2864             &  trim(nf90_strerror(istatus))
     2865        call my_abort('Error reading NetCDF file')
     2866      end if
     2867
     2868      !istatus = nf90_put_var(this%ncid, ivarid_out, data_real)
     2869      istatus = nf_put_var_double(this%ncid, ivarid_out, data_real)
     2870      deallocate(data_real)
     2871      if (istatus /= NF90_NOERR) then
     2872        write(nulerr,'(a,a,a,a)') '*** Error writing variable "', var_name, '": ', &
     2873             &  trim(nf90_strerror(istatus))
     2874        call my_abort('Error writing NetCDF file')
     2875      end if
     2876
     2877    else
     2878      allocate(data_int(ntotal))
     2879      !istatus = nf90_get_var(infile%ncid, ivarid_in, data_int)
     2880      istatus = nf_get_var_int(infile%ncid, ivarid_in, data_int)
     2881      if (istatus /= NF90_NOERR) then
     2882        deallocate(data_int)
     2883 
     2884        write(nulerr,'(a,a,a,a)') '*** Error reading variable "', var_name, '": ', &
     2885             &  trim(nf90_strerror(istatus))
     2886        call my_abort('Error reading NetCDF file')
     2887      end if
     2888
     2889      !istatus = nf90_put_var(this%ncid, ivarid_out, data_int)
     2890      istatus = nf_put_var_int(this%ncid, ivarid_out, data_int)
     2891      deallocate(data_int)
     2892      if (istatus /= NF90_NOERR) then
     2893        write(nulerr,'(a,a,a,a)') '*** Error writing variable "', var_name, '": ', &
     2894             &  trim(nf90_strerror(istatus))
     2895        call my_abort('Error writing NetCDF file')
     2896      end if
     2897    end if
     2898
     2899  end subroutine copy_variable
     2900
    23282901end module easy_netcdf
Note: See TracChangeset for help on using the changeset viewer.