module_field.f90 Source File


Contents

Source Code


Source Code

module field
  implicit none

  type, abstract :: field_type
     !! Implement a 3D scalar field, for instance a temperature field.
     !! ```f90
     !! type(field_type) = afield
     !! real :: u0(nx, ny, nz), dx
     !! afield = field_type(u0, dx)
     !! ```
     real, allocatable :: data(:, :, :)
     real :: dx
     !! Discrete mesh spacing
   contains
     procedure, public :: nx, ny, nz
     procedure, public :: is_equal, dump
     procedure(rhs_field), deferred :: rhs
     procedure, private :: field_add_field, field_sub_field, &
          & field_mul_real, field_add_real
     generic :: operator(+) => field_add_field, field_add_real
     generic :: operator(-) => field_sub_field
     generic :: operator(*) => field_mul_real
  end type field_type

  interface
     pure function rhs_field(self)
       import :: field_type
       class(field_type), intent(in) :: self
       real, allocatable :: rhs_field(:, :, :)
     end function rhs_field
  end interface

contains

  pure integer function nx(self)
    !! Returns domain size in \(\mathbf{x}\) direction
    class(field_type), intent(in) :: self
    nx = size(self%data, 1)
  end function nx

  pure integer function ny(self)
    !! Returns domain size in \(\mathbf{y}\) direction
    class(field_type), intent(in) :: self
    ny = size(self%data, 2)
  end function ny

  pure integer function nz(self)
    !! Returns domain size in \(\mathbf{z}\) direction
    class(field_type), intent(in) :: self
    nz = size(self%data, 3)
  end function nz

  pure logical function is_equal(self, lhs, tol)
    !! Compare two field_type instance based on their data value
    !! ```f90
    !! f1 = field_type(u0, dx)
    !! f2 = field_type(u0, dx2)
    !! f1%is_equal(f2) ! true
    !! ```
    class(field_type), intent(in) :: self !! Right hand side of comparison
    class(field_type), intent(in) :: lhs !! Left hand side of comparison
    real, intent(in) :: tol !! Absolute tolerance when comparing
                            !! fields values
    logical, allocatable :: elmt_is_equal(:, :, :)

    elmt_is_equal = abs(self%data - lhs%data) < tol
    is_equal = all(elmt_is_equal)
  end function is_equal

  subroutine dump(self, file_path, fmt)
    !! Write field data to ASCII file
    class(field_type), intent(in) :: self
    character(*), intent(in) :: file_path
    !! Relative path to file to output file
    character(*), optional :: fmt
    !! Format string
    integer :: fileunit, i, j, k
    if(.not. present(fmt)) fmt = 'f8.1'

    open(newunit=fileunit, file=file_path, action='write')
    do k = 1, size(self%data, 3)
       do j = 1, size(self%data, 2)
          do i = 1, size(self%data, 1)
             write(fileunit, fmt) self%data(i, j, k)
          end do
       end do
    end do
  end subroutine dump

  pure function field_add_field(self, afield)
    class(field_type), intent(in) :: self, afield
    class(field_type), allocatable :: field_add_field
    allocate(field_add_field, mold=self)
    field_add_field%data = self%data + afield%data
    field_add_field%dx = self%dx
  end function field_add_field

  pure function field_add_real(self, a)
    class(field_type), intent(in) :: self
    real, intent(in) :: a(:, :, :)
    class(field_type), allocatable :: field_add_real
    allocate(field_add_real, mold=self)
    field_add_real%data = self%data + a
    field_add_real%dx = self%dx
  end function field_add_real

  pure function field_sub_field(self, afield)
    class(field_type), intent(in) :: self, afield
    class(field_type), allocatable :: field_sub_field
    allocate(field_sub_field, mold=self)
    field_sub_field%data = self%data - afield%data
    field_sub_field%dx = self%dx
  end function field_sub_field

  pure function field_mul_real(self, a)
    !! Multiply a `field_cpu_type` instance by a `real` number.
    !! ```
    !! f1 = field_cpu_type(u0, dx)
    !! f2 = f1 * 1.3
    !! f2%is_equal(field_cpu_type(u0 * 1.3, dx)) ! true
    !! ```
    class(field_type), intent(in) :: self !! Left hand side
    real, intent(in) :: a !! Scalar to multiply field instance with
    class(field_type), allocatable :: field_mul_real
    allocate(field_mul_real, mold=self)
    field_mul_real%data = self%data * a
    field_mul_real%dx = self%dx
  end function field_mul_real

end module field