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

Hello everyone
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
``````

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

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

[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
!                               degree
!    (2) DegreeToRadian()     - converts its argument in degree to
!    (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

! --------------------------------------------------------------------
!    This function takes a REAL argument in radian and converts it to
! the equivalent degree.
! --------------------------------------------------------------------

IMPLICIT  NONE

! --------------------------------------------------------------------
!    This function takes a REAL argument in degree and converts it to
! --------------------------------------------------------------------

IMPLICIT  NONE
REAL, INTENT(IN) :: Degree

! --------------------------------------------------------------------
! 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

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

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)
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

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

1 Like

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