How to derive a polar type for holding a complex number?

Hello everyone :slightly_smiling_face:
I have a homework where we should solve the exercises 12 -2 to 12-7 from from Chapman, Fortran for Scientists and Engineers.I was able to implement the new type polar and I think I was able to get the operations to work as well. I need to program two functions/subroutines that allow the conversion of the ordinary complex number to a polar number and vice versa. I tried to do all this with the following module:

module complex
    use iso_fortran_env
    implicit none
    private
    public :: polar
    type :: polar
        
        real :: mag, theta
        contains
        procedure,private,pass(this)        :: complex_ord_to_polar
        !procedure,private,pass(this)        :: complex_polar_to_ord
        procedure,private,pass(this)        :: polar_multiply
        procedure,private,pass(this)        :: polar_divide

        generic,public               :: operator(*)          => polar_multiply
        generic,public               :: operator(/)          => polar_divide
        generic,public               :: assignment(=)        => complex_ord_to_polar!, complex_polar_to_ord
        !generic,public               :: assignment(=)        => complex_polar_to_ord
    end type polar

    
contains
subroutine complex_ord_to_polar(complex_ord,this)  
    complex,intent(in)               :: complex_ord    
    class(polar),intent(out)         :: this
    real                             :: real_part, im_part
        im_part     = AIMAG(complex_ord)
        real_part   = real(complex_ord)
        this%theta  = atan2(im_part, real_part)
        this%mag    = sqrt(real_part**2+im_part**2)
  end subroutine complex_ord_to_polar

  subroutine complex_polar_to_ord(this,complex_ord)  
    complex,intent(out)               :: complex_ord     
    class(polar),intent(in)           :: this
    real                              :: real_part, im_part
    select type(this)
      class is (polar)
        im_part     = this%mag * cos(this%theta)
        real_part   = this%mag * sin(this%theta)
        complex_ord = cmplx(real_part,im_part)
    end select
  end subroutine complex_polar_to_ord

  type(polar) function polar_multiply(this,b)   
      class(polar),intent(in)      :: this,b
      polar_multiply%mag = this%mag * b%mag
      polar_multiply%theta = this%theta + b%theta
      if ( (polar_multiply%theta >= 180).or.(polar_multiply%theta <= -180) ) then
        print*, 'The multiplication of the polar coordinates went wrong. '
      end if
  end function polar_multiply

  type(polar) function polar_divide(this,b)  
      class(polar),intent(in)      :: this,b
      polar_divide%mag = this%mag / b%mag
      polar_divide%theta = this%theta - b%theta
      if ( (polar_divide%theta >= 180).or.(polar_divide%theta <= -180) ) then
        print*, 'The diviation of the polar coordinates went wrong. '
      end if
  end function polar_divide
    
end module complex

I then have to test it using the following file:

`program ex_10_2
    use iso_fortran_env
    use complex
    implicit none
    real theta
    type(polar) ::  p1, pe, p3
    complex     ::  c1 = cmplx(-1.0,2.0), c2 = cmplx(2.0, -0.5)

    !test type conversion
    p1 = c1         !type conversion using overloaded =
    c2 = p1         !type conversion using overloaded =
    print*, p1      !should be 2.23606801 116.565041
    print*, c2/c1   !should be close to (1.0, 0.0)

    test mutiply and divide
    p2 = c2
    p3 = p1 * p2    !using overloaded *
    print*, p3      !should be 5.00000048 -126.869919
    p3 = p2 / p1    !using overloaded /
    print*, p3      !should be close to 1.0 0.0
end program ex_10_2`

However when I try to compile the module I get the following errror:

module_complex.f90:23:31:

   23 | subroutine complex_ord_to_polar(complex_ord,this)
      |                               1
Error: First argument of defined assignment at (1) must be INTENT(OUT) or INTENT(INOUT)

If I then comment this subroutine out I get the same error for the subroutine complex_polar_to_ord. What do I do wrong here? How can I fix this error?

I currently using the following gfortran version:

GNU Fortran (Ubuntu 11.1.0-1ubuntu1~18.04.1) 11.1.0

Many thanks already in advance for all รถyour suggestions and help.

Best regards

fidu13

Overloaded assignment of: a = b works like this:

subroutine assing_b_to_a( a, b )
     type(a_type), intent(inout) :: a  ! or intent(out)
     type(b_type), intent(in)    :: b  ! only intent(in) allowed
     ... actual implementation

You have them reversed :slight_smile:

Also note: sin() and cos() expect radians, not degrees

1 Like

Thank you very much for your help. I was able to resolve the error now.

However, I still do not get the correct results. Could you maybe give me a hint as to what I am doing wrong with the division and multiplication? I get the following results:

   2.23606801       116.565041    
  should be 2.23606801 116.565041
          (0.992538452,-0.121932223)
 should be close to (1.0, 0.0)
   5.00000000       46.1264648    
 should be 5.00000048 -126.869919
   1.00000000      -7.00362396    
 should be close to 1.0 0.0

I currently using the following module:

module complex
    use iso_fortran_env
    implicit none
    private
    public :: polar
    type :: polar
        
        real :: mag, theta
        contains
        procedure,private,pass(this)        :: complex_ord_to_polar
        procedure,private,pass(this)        :: complex_polar_to_ord
        procedure,private,pass(this)        :: polar_multiply
        procedure,private,pass(this)        :: polar_divide

        generic,public               :: operator(*)          => polar_multiply
        generic,public               :: operator(/)          => polar_divide
        generic,public               :: assignment(=)        => complex_ord_to_polar!, complex_polar_to_ord
        generic,public               :: assignment(=)        => complex_polar_to_ord
    end type polar
    real        :: Pi =  atan(1.)*4.

    
contains
subroutine complex_ord_to_polar(this,complex_ord)  
    complex,intent(in)               :: complex_ord    
    class(polar),intent(out)         :: this
    real                             :: real_part, im_part
        im_part     = AIMAG(complex_ord)
        real_part   = real(complex_ord)
        this%theta  = atan2(im_part, real_part)*180./Pi ! atan2(im_part, real_part)/(180*Pi)
        this%mag    = sqrt(real_part**2+im_part**2)
  end subroutine complex_ord_to_polar

  subroutine complex_polar_to_ord(complex_ord,this)  
    complex,intent(out)               :: complex_ord     
    class(polar),intent(in)           :: this
    real                              :: real_part, im_part
    select type(this)
      class is (polar)
        im_part     = this%mag * cos((this%theta)*180./Pi)
        real_part   = this%mag * sin((this%theta)*180./Pi)
        complex_ord = cmplx(real_part,im_part)
    end select
  end subroutine complex_polar_to_ord

  type(polar) function polar_multiply(this,b)   
      class(polar),intent(in)      :: this,b
      polar_multiply%mag = this%mag * b%mag
      polar_multiply%theta = this%theta + b%theta
      if ( (polar_multiply%theta >= 180)) then
        polar_multiply%theta = this%theta + b%theta - 180.
      else if (polar_multiply%theta <= -180) then 
        polar_multiply%theta = this%theta + b%theta + 180.
      end if
  end function polar_multiply

  type(polar) function polar_divide(this,b)  
      class(polar),intent(in)      :: this,b
      polar_divide%mag = this%mag / b%mag
      polar_divide%theta = this%theta - b%theta
      if ( (polar_divide%theta >= 180) ) then
        polar_divide%theta = this%theta - b%theta - 180.
      else if (polar_divide%theta <= -180) then 
        polar_divide%theta = this%theta - b%theta + 180.
      end if
  end function polar_divide
    
end module complex

And my test file is the following:

program ex_10_2
    use iso_fortran_env
    use complex
    implicit none
    type(polar) ::  p1, p2, p3
    complex     ::  c1 = cmplx(-1.0,2.0), c2 = cmplx(2.0, -0.5)

    !test type conversion
    p1 = c1         !type conversion using overloaded =
    c2 = p1         !type conversion using overloaded =
    print*, p1      
    print*,' should be 2.23606801 116.565041'
    print*, c2/c1   
    print*, 'should be close to (1.0, 0.0)'

    !test mutiply and divide
    p2 = c2
    p3 = p1 * p2    !using overloaded *
    print*, p3      
    print*, 'should be 5.00000048 -126.869919'
    p3 = p2 / p1    !using overloaded /
    print*, p3      
    print*, 'should be close to 1.0 0.0'
end program ex_10_2

You made a mistake in complex_polar_to_ord - see the factor :slight_smile:

[quote=โ€œfidu13, post:3, topic:2365โ€]

uld be close to 1.0 0.0'
end program ex_10_2

Thank you very much for your advice. However, I was not able of yet to correct the factor to fix the error. I am now using a separate module to do the conversion from degrees to radians:

! --------------------------------------------------------------------
! MODULE  MyTrigonometricFunctions:
!    This module provides the following functions and constants
!    (1) RadianToDegree()     - converts its argument in radian to
!                               degree
!    (2) DegreeToRadian()     - converts its argument in degree to
!                               radian
!    (3) MySIN()              - compute the sine of its argument in
!                               degree
!    (4) MyCOS()              - compute the cosine of its argument
!                               in degree
! --------------------------------------------------------------------

MODULE  MyTrigonometricFunctions
    IMPLICIT   NONE
 
    REAL, PARAMETER :: Pi        =  atan(1.)*4.      ! some constants
    REAL, PARAMETER :: Degree180 = 180.0
    REAL, PARAMETER :: R_to_D    = Degree180/PI
    REAL, PARAMETER :: D_to_R    = PI/Degree180
 
 CONTAINS
 
 ! --------------------------------------------------------------------
 ! FUNCTION  RadianToDegree():
 !    This function takes a REAL argument in radian and converts it to
 ! the equivalent degree.
 ! --------------------------------------------------------------------
 
    REAL FUNCTION  RadianToDegree(Radian)
       IMPLICIT  NONE
       REAL, INTENT(IN) :: Radian
 
       RadianToDegree = Radian * R_to_D
    END FUNCTION  RadianToDegree
 
 ! --------------------------------------------------------------------
 ! FUNCTION  DegreeToRadian():
 !    This function takes a REAL argument in degree and converts it to
 ! the equivalent radian.
 ! --------------------------------------------------------------------
 
    REAL FUNCTION  DegreeToRadian(Degree)
       IMPLICIT  NONE
       REAL, INTENT(IN) :: Degree
 
       DegreeToRadian = Degree * D_to_R
    END FUNCTION  DegreeToRadian
 
 ! --------------------------------------------------------------------
 ! FUNCTION  MySIN():
 !    This function takes a REAL argument in degree and computes its
 ! sine value.  It does the computation by converting its argument to
 ! radian and uses Fortran's sin().
 ! --------------------------------------------------------------------
 
    REAL FUNCTION  MySIN(x)
       IMPLICIT  NONE
       REAL, INTENT(IN) :: x
 
       MySIN = SIN(DegreeToRadian(x))
    END FUNCTION  MySIN
 
 ! --------------------------------------------------------------------
 ! FUNCTION  MySIN():
 !    This function takes a REAL argument in degree and computes its
 ! cosine value.  It does the computation by converting its argument to
 ! radian and uses Fortran's cos().
 ! --------------------------------------------------------------------
 
    REAL FUNCTION  MyCOS(x)
       IMPLICIT  NONE
       REAL, INTENT(IN) :: x
 
       MyCOS = COS(DegreeToRadian(x))
    END FUNCTION  MyCOS
 
 END MODULE  MyTrigonometricFunctions

I then make conversion of complex numbers in the following way:

module complex
  use iso_fortran_env
  use MyTrigonometricFunctions
  implicit none
  private
  public :: polar
  type :: polar
      
      real :: mag, theta
      contains
      procedure,private,pass(this)        :: complex_ord_to_polar
      procedure,private,pass(this)        :: complex_polar_to_ord
      procedure,private,pass(this)        :: polar_multiply
      procedure,private,pass(this)        :: polar_divide

      generic,public               :: operator(*)          => polar_multiply
      generic,public               :: operator(/)          => polar_divide
      generic,public               :: assignment(=)        => complex_ord_to_polar!, complex_polar_to_ord
      generic,public               :: assignment(=)        => complex_polar_to_ord
  end type polar
  !real        :: Pi =  atan(1.)*4.

  
contains
subroutine complex_ord_to_polar(this,complex_ord)  
  complex,intent(in)               :: complex_ord    
  class(polar),intent(out)         :: this
  real                             :: real_part, im_part
      im_part     = AIMAG(complex_ord)
      real_part   = real(complex_ord)
      this%theta  = RadianToDegree(atan2(im_part, real_part)) ! atan2(im_part, real_part)/(180*Pi)
      this%mag    = sqrt(real_part**2+im_part**2)
end subroutine complex_ord_to_polar

subroutine complex_polar_to_ord(complex_ord,this)  
  complex,intent(out)               :: complex_ord     
  class(polar),intent(in)           :: this
  real                              :: real_part, im_part
  select type(this)
    class is (polar)
      im_part     = this%mag * cos(DegreeToRadian(this%theta))
      real_part   = this%mag * sin(DegreeToRadian(this%theta))
      complex_ord = cmplx(real_part,im_part)
  end select
end subroutine complex_polar_to_ord

type(polar) function polar_multiply(this,b)   
    class(polar),intent(in)      :: this,b
    polar_multiply%mag = this%mag * b%mag
    polar_multiply%theta = this%theta + b%theta
    if ( (polar_multiply%theta >= 180)) then
      polar_multiply%theta = this%theta + b%theta - 180.
    else if (polar_multiply%theta <= -180) then 
      polar_multiply%theta = this%theta + b%theta + 180.
    end if
end function polar_multiply

type(polar) function polar_divide(this,b)  
    class(polar),intent(in)      :: this,b
    polar_divide%mag = this%mag / b%mag
    polar_divide%theta = this%theta - b%theta
    if ( (polar_divide%theta >= 180) ) then
      polar_divide%theta = this%theta - b%theta - 180.
    else if (polar_divide%theta <= -180) then 
      polar_divide%theta = this%theta - b%theta + 180.
    end if
end function polar_divide
  
end module complex

But I still get not the right result:

   2.23606801       116.565041    
  should be 2.23606801 116.565041
         (-0.799999833,-0.600000262)
 should be close to (1.0, 0.0)
   5.00000048       90.0000076    
 should be 5.00000048 -126.869919
   1.00000012      -143.130066    
 should be close to 1.0 0.0

After running the provided programm:

program ex_10_2
    use iso_fortran_env
    use complex
    use MyTrigonometricFunctions
    implicit none
    type(polar) ::  p1, p2, p3
    complex     ::  c1 = cmplx(-1.0,2.0), c2 = cmplx(2.0, -0.5)

    !test type conversion
    p1 = c1         !type conversion using overloaded =
    c2 = p1         !type conversion using overloaded =
    print*, p1      
    print*,' should be 2.23606801 116.565041'
    print*, c2/c1   
    print*, 'should be close to (1.0, 0.0)'

    !test mutiply and divide
    p2 = c2
    p3 = p1 * p2    !using overloaded *
    print*, p3      
    print*, 'should be 5.00000048 -126.869919'
    p3 = p2 / p1    !using overloaded /
    print*, p3      
    print*, 'should be close to 1.0 0.0'
end program ex_10_2

How do I need to adjust the factor to get the corrrect result? Or did I also do something else wrong?

I will have a look - nothing that jumps out though :wink:

By printing c2 after โ€œc2 = p1โ€, I saw that that particular is going wrong. You mixed up cos and sin.

There is yet another mistake, but I will let you sort that out :slight_smile:

1 Like

Thank you very much and I was now able to resolve this too. Many thanks for all your help!!!