OOP with Fortran

I am currently learning OOP with Fortran. Below is my trial that seeks advice:

module type_lm
  public :: lm 
  private :: beta

  type :: lm
    real(8), allocatable :: x(:)
    real(8), allocatable :: y(:)
    
    contains
    procedure :: beta  
  end type

  contains
  function beta(self) 
    class(lm) :: self
    real(8) :: beta
        
    real(8) :: sum_x, sum_y, sum_xx, sum_xy
    integer(8) :: i, n
  
    sum_x = 0d0
    sum_y = 0d0
    sum_xx = 0d0
    sum_xy = 0d0
    
    n = min(size(self%x), size(self%y)) 

    !$omp parallel do reduction(+:sum_x, sum_y, sum_xx, sum_xy)
    do i = 1, n
      sum_x = sum_x + self%x(i)
      sum_y = sum_y + self%y(i) 
      sum_xx = sum_xx + self%x(i)*self%x(i) 
      sum_xy = sum_xy + self%x(i)*self%y(i) 
    end do
    !$omp end parallel do

    beta = (n*sum_xy - sum_x*sum_y)/(n*sum_xx - sum_x*sum_x)
  end function
end module

program main
  use type_lm

  type(lm) :: type_lm_var

  allocate(type_lm_var%x(1000000))
  allocate(type_lm_var%y(1000000))
  
  call random_number(type_lm_var%x)
  call random_number(type_lm_var%y)
   
  print *, type_lm_var%beta()
end program

A few suggestions below, but none of them are related to your use of objects (which looks fine to me).

Obviously, your specific example is simple enough that derived types are not necessary. But in real codes with more variables, they really help to give some hierarchical structure and manage the complexity.

I suggest starting the module with:

module type_lm
  implicit none
  integer, parameter, public :: dp = SELECTED_REAL_KIND(15)
  integer, parameter, public :: ip = SELECTED_INT_KIND(15)

and then replacing real(8) with real(dp), and integer(8) with integer(ip).

That is because in Fortran, compilers don’t have to use 8 to refer to 64-bit integers or reals (although a number of them do).

Also I suggest adding implicit none to the main program.

program main
  use type_lm
  implicit none
2 Likes

Also, at the moment you are just setting up the type in the main program:

  allocate(type_lm_var%x(1000000))
  allocate(type_lm_var%y(1000000))
  
  call random_number(type_lm_var%x)
  call random_number(type_lm_var%y)

Because this example is small, IMO that’s fine. However as the setup complexity grows, you probably want to add a type-bound procedure to take care of those details, especially if you use many variables of type type_lm. Then you can just do, e.g., call type_lm_var%setup(1000000) in place of the 4 lines of code above.

2 Likes