! Copyright (c) 2012 Joseph A. Levin ! ! Permission is hereby granted, free of charge, to any person obtaining a copy of this ! software and associated documentation files (the "Software"), to deal in the Software ! without restriction, including without limitation the rights to use, copy, modify, merge, ! publish, distribute, sublicense, and/or sell copies of the Software, and to permit ! persons to whom the Software is furnished to do so, subject to the following conditions: ! ! The above copyright notice and this permission notice shall be included in all copies or ! substantial portions of the Software. ! ! THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, ! INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR ! PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE ! LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT ! OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER ! DEALINGS IN THE SOFTWARE. ! ! File: ncdf_path_m.f95 ! Author: Joseph A. Levin ! ! Created on March 10, 2012, 11:01 PM ! module ncdf_path_m use ncdf_value_m use ncdf_string_m private public :: ncdf_path_get interface ncdf_path_get module procedure ncdf_get_by_path module procedure ncdf_get_integer module procedure ncdf_get_real module procedure ncdf_get_double module procedure ncdf_get_logical module procedure ncdf_get_chars module procedure ncdf_get_array_1d_integer module procedure ncdf_get_array_2d_integer module procedure ncdf_get_array_1d_real module procedure ncdf_get_array_2d_real module procedure ncdf_get_array_1d_double module procedure ncdf_get_array_2d_double module procedure ncdf_get_array_1d_logical module procedure ncdf_get_array_2d_logical end interface ncdf_path_get abstract interface subroutine ncdf_array_callback_1d(element, i, count) use ncdf_value_m implicit none type(ncdf_value), pointer,intent(in) :: element integer, intent(in) :: i ! index integer, intent(in) :: count ! size of array end subroutine ncdf_array_callback_1d subroutine ncdf_array_callback_2d(element, i1, i2, count1, count2) use ncdf_value_m implicit none type(ncdf_value), pointer,intent(in) :: element integer, intent(in) :: i1, i2 integer, intent(in) :: count1, count2 end subroutine ncdf_array_callback_2d end interface contains ! ! GET BY PATH ! ! $ = root ! @ = this ! . = child object member ! [] = child array element ! recursive subroutine ncdf_get_by_path(this, path, p) type(ncdf_value), pointer :: this, p character(len=*) :: path integer :: i, length, child_i character :: c logical :: array ! default to assuming relative to this p => this child_i = 1 array = .false. length = len_trim(path) do i=1, length c = path(i:i) select case (c) case ("$") ! root do while (associated (p % parent)) p => p % parent end do child_i = i + 1 case ("@") ! this p => this child_i = i + 1 case (".", "[") ! get child member from p if (child_i < i) then p => ncdf_value_get(p, path(child_i:i-1)) else child_i = i + 1 cycle end if if(.not.associated(p)) then return end if child_i = i+1 ! check if this is an array ! if so set the array flag if (c == "[") then ! start looking for the array element index array = .true. end if case ("]") if (.not.array) then print *, "ERROR: Unexpected ], not missing preceding [" call exit(1) end if array = .false. child_i = parse_integer(path(child_i:i-1)) p => ncdf_value_get(p, child_i) child_i= i + 1 end select end do ! grab the last child if present in the path if (child_i <= length) then p => ncdf_value_get(p, path(child_i:i-1)) if(.not.associated(p)) then return else end if end if end subroutine ncdf_get_by_path ! ! PARSE INTEGER ! integer function parse_integer(chars) result(integral) character(len=*) :: chars character :: c integer :: tmp, i integral = 0 do i=1, len_trim(chars) c = chars(i:i) select case(c) case ("0":"9") ! digit read (c, '(i1)') tmp ! shift if(i > 1) then integral = integral * 10 end if ! add integral = integral + tmp case default return end select end do end function parse_integer ! ! GET INTEGER ! subroutine ncdf_get_integer(this, path, value) type(ncdf_value), pointer :: this, p character(len=*), optional :: path integer :: value nullify(p) if(present(path)) then call ncdf_get_by_path(this=this, path=path, p=p) else p => this end if if(.not.associated(p)) then print *, "Unable to resolve path: ", path return end if if(p % value_type == TYPE_INTEGER) then value = p % value_integer else if (p % value_type == TYPE_REAL) then value = p % value_real else if (p % value_type == TYPE_LOGICAL) then if (p % value_logical) then value = 1 else value = 0 end if else print *, "Unable to resolve value to integer: ", path call exit(1) end if end subroutine ncdf_get_integer ! ! GET REAL ! subroutine ncdf_get_real(this, path, value) type(ncdf_value), pointer :: this, p character(len=*), optional :: path real :: value nullify(p) if(present(path)) then call ncdf_get_by_path(this=this, path=path, p=p) else p => this end if if(.not.associated(p)) then print *, "Unable to resolve path: ", path return end if if(p % value_type == TYPE_INTEGER) then value = p % value_integer else if (p % value_type == TYPE_REAL) then value = p % value_real else if (p % value_type == TYPE_LOGICAL) then if (p % value_logical) then value = 1 else value = 0 end if else print *, "Unable to resolve value to real: ", path call exit(1) end if end subroutine ncdf_get_real ! ! GET DOUBLE ! subroutine ncdf_get_double(this, path, value) type(ncdf_value), pointer :: this, p character(len=*), optional :: path double precision :: value nullify(p) if(present(path)) then call ncdf_get_by_path(this=this, path=path, p=p) else p => this end if if(.not.associated(p)) then print *, "Unable to resolve path: ", path return end if if(p % value_type == TYPE_INTEGER) then value = p % value_integer else if (p % value_type == TYPE_REAL) then value = p % value_double else if (p % value_type == TYPE_LOGICAL) then if (p % value_logical) then value = 1 else value = 0 end if else print *, "Unable to resolve value to double: ", path call exit(1) end if end subroutine ncdf_get_double ! ! GET LOGICAL ! subroutine ncdf_get_logical(this, path, value) type(ncdf_value), pointer :: this, p character(len=*), optional :: path logical :: value nullify(p) if(present(path)) then call ncdf_get_by_path(this=this, path=path, p=p) else p => this end if if(.not.associated(p)) then print *, "Unable to resolve path: ", path return end if if(p % value_type == TYPE_INTEGER) then value = (p % value_integer > 0) else if (p % value_type == TYPE_LOGICAL) then value = p % value_logical else print *, "Unable to resolve value to real: ", path call exit(1) end if end subroutine ncdf_get_logical ! ! GET CHARS ! subroutine ncdf_get_chars(this, path, value) type(ncdf_value), pointer :: this, p character(len=*), optional :: path character(len=*) :: value nullify(p) if(present(path)) then call ncdf_get_by_path(this=this, path=path, p=p) else p => this end if if(.not.associated(p)) then print *, "Unable to resolve path: ", path return end if if(p % value_type == TYPE_STRING) then call ncdf_string_copy(p % value_string, value) else print *, "Unable to resolve value to characters: ", path call exit(1) end if end subroutine ncdf_get_chars ! ! GET ARRAY 1D ! subroutine ncdf_get_array_1d(this, path, array_callback) type(ncdf_value), pointer :: this character(len = *), optional :: path procedure(ncdf_array_callback_1d) :: array_callback type(ncdf_value), pointer :: p, element integer :: index, count nullify(p) ! resolve the path to the value if(present(path)) then call ncdf_get_by_path(this=this, path=path, p=p) else p => this end if if(.not.associated(p)) then print *, "Unable to resolve path: ", path return end if if(p % value_type == TYPE_ARRAY) then count = ncdf_value_count(p) element => p % children do index = 1, count call array_callback(element, index, count) element => element % next end do else print *, "Resolved value is not an array. ", path call exit(1) end if if (associated(p)) nullify(p) end subroutine ncdf_get_array_1d ! ! GET ARRAY INTEGER 1D ! subroutine ncdf_get_array_1d_integer(this, path, arr) implicit none type(ncdf_value), pointer, intent(in) :: this character(len=*), intent(in), optional :: path integer, allocatable, intent(out) :: arr(:) if (allocated(arr)) deallocate(arr) #ifdef OLDPGI #else call ncdf_get_array_1d(this, path, ncdf_array_callback_1d_integer) #endif contains subroutine ncdf_array_callback_1d_integer(element, i, count) implicit none type(ncdf_value), pointer, intent(in) :: element integer, intent(in) :: i, count if (.not. allocated(arr)) allocate(arr(count)) call ncdf_path_get(element, "", arr(i)) end subroutine ncdf_array_callback_1d_integer end subroutine ncdf_get_array_1d_integer ! ! GET ARRAY REAL 1D ! subroutine ncdf_get_array_1d_real(this, path, arr) implicit none type(ncdf_value), pointer, intent(in) :: this character(len=*), intent(in), optional :: path real, allocatable, intent(out) :: arr(:) if (allocated(arr)) deallocate(arr) #ifdef OLDPGI #else call ncdf_get_array_1d(this, path, ncdf_array_callback_1d_real) #endif contains subroutine ncdf_array_callback_1d_real(element, i, count) implicit none type(ncdf_value), pointer, intent(in) :: element integer, intent(in) :: i, count if (.not. allocated(arr)) allocate(arr(count)) call ncdf_path_get(element, "", arr(i)) end subroutine ncdf_array_callback_1d_real end subroutine ncdf_get_array_1d_real ! ! GET ARRAY DOUBLE 1D ! subroutine ncdf_get_array_1d_double(this, path, arr) implicit none type(ncdf_value), pointer, intent(in) :: this character(len=*), intent(in), optional :: path double precision, allocatable, intent(out) :: arr(:) if (allocated(arr)) deallocate(arr) #ifdef OLDPGI #else call ncdf_get_array_1d(this, path, ncdf_array_callback_1d_double) #endif contains subroutine ncdf_array_callback_1d_double(element, i, count) implicit none type(ncdf_value), pointer, intent(in) :: element integer, intent(in) :: i, count if (.not. allocated(arr)) allocate(arr(count)) call ncdf_path_get(element, "", arr(i)) end subroutine ncdf_array_callback_1d_double end subroutine ncdf_get_array_1d_double ! ! GET ARRAY LOGICAL 1D ! subroutine ncdf_get_array_1d_logical(this, path, arr) implicit none type(ncdf_value), pointer, intent(in) :: this character(len=*), intent(in), optional :: path logical, allocatable, intent(out) :: arr(:) if (allocated(arr)) deallocate(arr) #ifdef OLDPGI #else call ncdf_get_array_1d(this, path, ncdf_array_callback_1d_logical) #endif contains subroutine ncdf_array_callback_1d_logical(element, i, count) implicit none type(ncdf_value), pointer, intent(in) :: element integer, intent(in) :: i, count if (.not. allocated(arr)) allocate(arr(count)) call ncdf_path_get(element, "", arr(i)) end subroutine ncdf_array_callback_1d_logical end subroutine ncdf_get_array_1d_logical ! ! GET ARRAY 2D ! subroutine ncdf_get_array_2d(this, path, array_callback) type(ncdf_value), pointer :: this character(len = *), optional :: path procedure(ncdf_array_callback_2d) :: array_callback type(ncdf_value), pointer :: p, element, item integer :: i1, i2, count1, count2, c nullify(p) ! resolve the path to the value if(present(path)) then call ncdf_get_by_path(this=this, path=path, p=p) else p => this end if if(.not.associated(p)) then print *, "Unable to resolve path: ", path return end if if(p % value_type == TYPE_ARRAY) then count1 = ncdf_value_count(p) element => p % children do i1 = 1, count1 if (element % value_type == TYPE_ARRAY) then c = ncdf_value_count(element) if (i1 == 1) then count2 = c else if (c /= count2) then print *, "Resolved value has the wrong number of elements. ", & path, "[", i1, "]" call exit(1) end if item => element % children do i2 = 1, count2 call array_callback(item, i1, i2, count1, count2) item => item % next end do element => element % next else print *, "Resolved value is not an array. ", path, "[", i1, "]" call exit(1) end if end do else print *, "Resolved value is not an array. ", path call exit(1) end if if (associated(p)) nullify(p) end subroutine ncdf_get_array_2d ! ! GET ARRAY INTEGER 2D ! subroutine ncdf_get_array_2d_integer(this, path, arr) implicit none type(ncdf_value), pointer, intent(in) :: this character(len=*), intent(in), optional :: path integer, allocatable, intent(out) :: arr(:, :) if (allocated(arr)) deallocate(arr) #ifdef OLDPGI #else call ncdf_get_array_2d(this, path, ncdf_array_callback_2d_integer) #endif contains subroutine ncdf_array_callback_2d_integer(element, i1, i2, count1, count2) implicit none type(ncdf_value), pointer, intent(in) :: element integer, intent(in) :: i1, i2, count1, count2 if (.not. allocated(arr)) allocate(arr(count1, count2)) call ncdf_path_get(element, "", arr(i1, i2)) end subroutine ncdf_array_callback_2d_integer end subroutine ncdf_get_array_2d_integer ! ! GET ARRAY REAL 2D ! subroutine ncdf_get_array_2d_real(this, path, arr) implicit none type(ncdf_value), pointer, intent(in) :: this character(len=*), intent(in), optional :: path real, allocatable, intent(out) :: arr(:, :) if (allocated(arr)) deallocate(arr) #ifdef OLDPGI #else call ncdf_get_array_2d(this, path, ncdf_array_callback_2d_real) #endif contains subroutine ncdf_array_callback_2d_real(element, i1, i2, count1, count2) implicit none type(ncdf_value), pointer, intent(in) :: element integer, intent(in) :: i1, i2, count1, count2 if (.not. allocated(arr)) allocate(arr(count1, count2)) call ncdf_path_get(element, "", arr(i1, i2)) end subroutine ncdf_array_callback_2d_real end subroutine ncdf_get_array_2d_real ! ! GET ARRAY DOUBLE 2D ! subroutine ncdf_get_array_2d_double(this, path, arr) implicit none type(ncdf_value), pointer, intent(in) :: this character(len=*), intent(in), optional :: path double precision, allocatable, intent(out) :: arr(:, :) if (allocated(arr)) deallocate(arr) #ifdef OLDPGI #else call ncdf_get_array_2d(this, path, ncdf_array_callback_2d_double) #endif contains subroutine ncdf_array_callback_2d_double(element, i1, i2, count1, count2) implicit none type(ncdf_value), pointer, intent(in) :: element integer, intent(in) :: i1, i2, count1, count2 if (.not. allocated(arr)) allocate(arr(count1, count2)) call ncdf_path_get(element, "", arr(i1, i2)) end subroutine ncdf_array_callback_2d_double end subroutine ncdf_get_array_2d_double ! ! GET ARRAY LOGICAL 2D ! subroutine ncdf_get_array_2d_logical(this, path, arr) implicit none type(ncdf_value), pointer, intent(in) :: this character(len=*), intent(in), optional :: path logical, allocatable, intent(out) :: arr(:, :) if (allocated(arr)) deallocate(arr) #ifdef OLDPGI #else call ncdf_get_array_2d(this, path, ncdf_array_callback_2d_logical) #endif contains subroutine ncdf_array_callback_2d_logical(element, i1, i2, count1, count2) implicit none type(ncdf_value), pointer, intent(in) :: element integer, intent(in) :: i1, i2, count1, count2 if (.not. allocated(arr)) allocate(arr(count1, count2)) call ncdf_path_get(element, "", arr(i1, i2)) end subroutine ncdf_array_callback_2d_logical end subroutine ncdf_get_array_2d_logical end module ncdf_path_m