bem_matching_plane_query_io.f90 Source File


This file depends on

sourcefile~~bem_matching_plane_query_io.f90~~EfferentGraph sourcefile~bem_matching_plane_query_io.f90 bem_matching_plane_query_io.f90 sourcefile~bem_kinds.f90 bem_kinds.f90 sourcefile~bem_matching_plane_query_io.f90->sourcefile~bem_kinds.f90 sourcefile~bem_string_utils.f90 bem_string_utils.f90 sourcefile~bem_matching_plane_query_io.f90->sourcefile~bem_string_utils.f90

Files dependent on this one

sourcefile~~bem_matching_plane_query_io.f90~~AfferentGraph sourcefile~bem_matching_plane_query_io.f90 bem_matching_plane_query_io.f90 sourcefile~bem_matching_plane_response_generator.f90 bem_matching_plane_response_generator.f90 sourcefile~bem_matching_plane_response_generator.f90->sourcefile~bem_matching_plane_query_io.f90 sourcefile~bem_matching_plane_zhao_atlas.f90 bem_matching_plane_zhao_atlas.f90 sourcefile~bem_matching_plane_zhao_atlas.f90->sourcefile~bem_matching_plane_query_io.f90 sourcefile~zhao_atlas_main.f90 zhao_atlas_main.f90 sourcefile~zhao_atlas_main.f90->sourcefile~bem_matching_plane_zhao_atlas.f90 sourcefile~zhao_response_main.f90 zhao_response_main.f90 sourcefile~zhao_response_main.f90->sourcefile~bem_matching_plane_response_generator.f90

Source Code

!> Offline Zhao ツール共通の数値 query CSV 入力。物理条件と格子条件は呼出側が扱う。
module bem_matching_plane_query_io
  use, intrinsic :: ieee_arithmetic, only: ieee_is_finite
  use bem_kinds, only: dp, i32
  use bem_string_utils, only: is_decimal_real_token
  implicit none
  private

  integer, parameter :: query_line_length = 4096
  integer, parameter :: initial_query_capacity = 64
  integer(i32), parameter, public :: matching_plane_query_ok = 0_i32
  integer(i32), parameter, public :: matching_plane_query_io_error = 2_i32
  integer(i32), parameter, public :: matching_plane_query_invalid_grid = 3_i32

  public :: read_matching_plane_query_csv

contains

  !> 指定 header の列数だけ有限の十進実数を読み、入力順の列ベクトルとして返す。
  subroutine read_matching_plane_query_csv(path, expected_header, queries, status, message)
    character(len=*), intent(in) :: path, expected_header
    real(dp), allocatable, intent(out) :: queries(:, :)
    integer(i32), intent(out) :: status
    character(len=*), intent(out) :: message

    real(dp), allocatable :: buffer(:, :), grown(:, :), query(:)
    character(len=query_line_length) :: line
    character(len=:), allocatable :: record
    integer :: unit_id, ios, line_number, row_count, capacity, column_count
    logical :: header_found

    status = matching_plane_query_ok
    message = ''
    column_count = count_character(expected_header, ',') + 1
    capacity = initial_query_capacity
    allocate (queries(column_count, 0), buffer(column_count, capacity), query(column_count))
    row_count = 0
    line_number = 0
    header_found = .false.
    open (newunit=unit_id, file=trim(path), status='old', action='read', iostat=ios)
    if (ios /= 0) then
      status = matching_plane_query_io_error
      message = 'could not open matching-plane query CSV: '//trim(path)
      return
    end if

    do
      read (unit_id, '(a)', iostat=ios) line
      if (ios < 0) exit
      line_number = line_number + 1
      if (ios > 0) then
        status = matching_plane_query_io_error
        message = 'failed to read matching-plane query CSV line '//trim(integer_text(line_number))//'.'
        exit
      end if
      record = trim(adjustl(line))
      if (len(record) == 0) cycle
      if (record(1:1) == '#') cycle
      if (.not. header_found) then
        if (record /= expected_header) then
          status = matching_plane_query_invalid_grid
          message = 'matching-plane query CSV header does not match the required columns.'
          exit
        end if
        header_found = .true.
        cycle
      end if
      if (count_character(record, ',') /= column_count - 1) then
        status = matching_plane_query_invalid_grid
        message = 'matching-plane query row '//trim(integer_text(line_number))// &
                  ' must contain exactly '//trim(integer_text(column_count))//' values.'
        exit
      end if
      call parse_query_record(record, query, ios)
      if (ios /= 0 .or. any(.not. ieee_is_finite(query))) then
        status = matching_plane_query_invalid_grid
        message = 'matching-plane query row '//trim(integer_text(line_number))//' contains invalid values.'
        exit
      end if
      if (row_count == capacity) then
        if (capacity > huge(capacity)/2) then
          status = matching_plane_query_invalid_grid
          message = 'matching-plane query row capacity overflowed.'
          exit
        end if
        allocate (grown(column_count, 2*capacity))
        grown(:, :capacity) = buffer
        call move_alloc(grown, buffer)
        capacity = 2*capacity
      end if
      row_count = row_count + 1
      buffer(:, row_count) = query
    end do
    close (unit_id)
    if (status /= matching_plane_query_ok) return

    if (.not. header_found .or. row_count == 0) then
      status = matching_plane_query_invalid_grid
      message = 'matching-plane query CSV must contain the required header and at least one row.'
      return
    end if
    queries = buffer(:, :row_count)
  end subroutine read_matching_plane_query_csv

  !> List-directed repeat/null/slash syntax を拒否し、十進実数 token だけを読む。
  subroutine parse_query_record(record, query, ios)
    character(len=*), intent(in) :: record
    real(dp), intent(out) :: query(:)
    integer, intent(out) :: ios

    character(len=:), allocatable :: token
    integer :: column, comma, first, last

    query = 0.0_dp
    ios = 0
    first = 1
    do column = 1, size(query)
      if (column < size(query)) then
        comma = index(record(first:), ',')
        if (comma <= 0) then
          ios = 1
          return
        end if
        last = first + comma - 2
      else
        if (index(record(first:), ',') /= 0) then
          ios = 1
          return
        end if
        last = len(record)
      end if
      if (last < first) then
        ios = 1
        return
      end if
      token = trim(adjustl(record(first:last)))
      if (len(token) == 0 .or. scan(token, ' '//achar(9)) > 0 .or. &
          .not. is_decimal_real_token(token)) then
        ios = 1
        return
      end if
      read (token, *, iostat=ios) query(column)
      if (ios /= 0) return
      first = last + 2
    end do
  end subroutine parse_query_record

  pure integer function count_character(text, target) result(count_value)
    character(len=*), intent(in) :: text
    character(len=1), intent(in) :: target
    integer :: index_value

    count_value = 0
    do index_value = 1, len(text)
      if (text(index_value:index_value) == target) count_value = count_value + 1
    end do
  end function count_character

  function integer_text(value) result(text)
    integer, intent(in) :: value
    character(len=32) :: text

    write (text, '(i0)') value
  end function integer_text

end module bem_matching_plane_query_io