vector_module.f90 Source File


This file depends on

sourcefile~~vector_module.f90~~EfferentGraph sourcefile~vector_module.f90 vector_module.f90 sourcefile~kind_module.f90 kind_module.F90 sourcefile~vector_module.f90->sourcefile~kind_module.f90 sourcefile~numbers_module.f90 numbers_module.f90 sourcefile~vector_module.f90->sourcefile~numbers_module.f90 sourcefile~numbers_module.f90->sourcefile~kind_module.f90

Files dependent on this one

sourcefile~~vector_module.f90~~AfferentGraph sourcefile~vector_module.f90 vector_module.f90 sourcefile~bplane_module.f90 bplane_module.f90 sourcefile~bplane_module.f90->sourcefile~vector_module.f90 sourcefile~fortran_astrodynamics_toolkit.f90 fortran_astrodynamics_toolkit.f90 sourcefile~fortran_astrodynamics_toolkit.f90->sourcefile~vector_module.f90 sourcefile~fortran_astrodynamics_toolkit.f90->sourcefile~bplane_module.f90 sourcefile~geometry_module.f90 geometry_module.f90 sourcefile~fortran_astrodynamics_toolkit.f90->sourcefile~geometry_module.f90 sourcefile~geopotential_module.f90 geopotential_module.f90 sourcefile~fortran_astrodynamics_toolkit.f90->sourcefile~geopotential_module.f90 sourcefile~iau_orientation_module.f90 iau_orientation_module.f90 sourcefile~fortran_astrodynamics_toolkit.f90->sourcefile~iau_orientation_module.f90 sourcefile~lambert_module.f90 lambert_module.f90 sourcefile~fortran_astrodynamics_toolkit.f90->sourcefile~lambert_module.f90 sourcefile~modified_equinoctial_module.f90 modified_equinoctial_module.f90 sourcefile~fortran_astrodynamics_toolkit.f90->sourcefile~modified_equinoctial_module.f90 sourcefile~orbital_mechanics_module.f90 orbital_mechanics_module.f90 sourcefile~fortran_astrodynamics_toolkit.f90->sourcefile~orbital_mechanics_module.f90 sourcefile~relative_motion_module.f90 relative_motion_module.f90 sourcefile~fortran_astrodynamics_toolkit.f90->sourcefile~relative_motion_module.f90 sourcefile~transformation_module.f90 transformation_module.f90 sourcefile~fortran_astrodynamics_toolkit.f90->sourcefile~transformation_module.f90 sourcefile~c_interface_module.f90 c_interface_module.f90 sourcefile~fortran_astrodynamics_toolkit.f90->sourcefile~c_interface_module.f90 sourcefile~rk_module_variable_step.f90 rk_module_variable_step.f90 sourcefile~fortran_astrodynamics_toolkit.f90->sourcefile~rk_module_variable_step.f90 sourcefile~geometry_module.f90->sourcefile~vector_module.f90 sourcefile~geopotential_module.f90->sourcefile~vector_module.f90 sourcefile~iau_orientation_module.f90->sourcefile~vector_module.f90 sourcefile~lambert_module.f90->sourcefile~vector_module.f90 sourcefile~modified_equinoctial_module.f90->sourcefile~vector_module.f90 sourcefile~orbital_mechanics_module.f90->sourcefile~vector_module.f90 sourcefile~relative_motion_module.f90->sourcefile~vector_module.f90 sourcefile~transformation_module.f90->sourcefile~vector_module.f90 sourcefile~transformation_module.f90->sourcefile~iau_orientation_module.f90 sourcefile~c_interface_module.f90->sourcefile~geopotential_module.f90 sourcefile~rk_module_variable_step.f90->sourcefile~orbital_mechanics_module.f90

Source Code

!*****************************************************************************************
!> author: Jacob Williams
!
!  Routines for the manipulation of vectors.

    module vector_module

    use kind_module,    only: wp
    use numbers_module, only: one,zero,pi

    implicit none

    private

    integer,parameter,public :: x_axis = 1
    integer,parameter,public :: y_axis = 2
    integer,parameter,public :: z_axis = 3

    real(wp),dimension(3),parameter,public :: x_unit = [one,zero,zero] !! x-axis unit vector
    real(wp),dimension(3),parameter,public :: y_unit = [zero,one,zero] !! y-axis unit vector
    real(wp),dimension(3),parameter,public :: z_unit = [zero,zero,one] !! z-axis unit vector

    public :: cross
    public :: unit
    public :: uhat_dot
    public :: ucross
    public :: axis_angle_rotation
    public :: cross_matrix
    public :: outer_product
    public :: box_product
    public :: vector_projection
    public :: vector_projection_on_plane
    public :: axis_angle_rotation_to_rotation_matrix
    public :: spherical_to_cartesian
    public :: cartesian_to_spherical
    public :: rotation_matrix
    public :: rotation_matrix_dot
    public :: angle_between_vectors

    interface fill_vector
        module procedure :: fill_vector_with_vector, fill_vector_with_scalar, &
                            fill_char_vector_with_vector, fill_char_vector_with_scalar
    end interface
    public :: fill_vector

    interface extract_vector
        module procedure :: extract_vector_from_vector, extract_scalar_from_vector
    end interface
    public :: extract_vector

    !test routine:
    public :: vector_test

    contains
!*****************************************************************************************

!*****************************************************************************************
!> author: Jacob Williams
!
!  Cross product of two 3x1 vectors

    pure function cross(r,v) result(rxv)

    implicit none

    real(wp),dimension(3),intent(in) :: r
    real(wp),dimension(3),intent(in) :: v
    real(wp),dimension(3)            :: rxv

    rxv = [r(2)*v(3) - v(2)*r(3), &
           r(3)*v(1) - v(3)*r(1), &
           r(1)*v(2) - v(1)*r(2) ]

    end function cross
!*****************************************************************************************

!*****************************************************************************************
!> author: Jacob Williams
!
!  Unit vector

    pure function unit(r) result(u)

    implicit none

    real(wp),dimension(:),intent(in) :: r
    real(wp),dimension(size(r))      :: u

    real(wp) :: rmag

    rmag = norm2(r)

    if (rmag==zero) then
        u = zero
    else
        u = r / rmag
    end if

    end function unit
!*****************************************************************************************

!*****************************************************************************************
!> author: Jacob Williams
!
!  Time derivative of a unit vector.

    pure function uhat_dot(u,udot) result(uhatd)

    implicit none

    real(wp),dimension(3),intent(in) :: u      !! vector [`u`]
    real(wp),dimension(3),intent(in) :: udot   !! derivative of vector [`du/dt`]
    real(wp),dimension(3)            :: uhatd  !! derivative of unit vector [`d(uhat)/dt`]

    real(wp)              :: umag  !! vector magnitude
    real(wp),dimension(3) :: uhat  !! unit vector

    umag = norm2(u)

    if (umag == zero) then  !singularity
        uhatd = zero
    else
        uhat = u / umag
        uhatd = ( udot - dot_product(uhat,udot)*uhat ) / umag
    end if

    end function uhat_dot
!*****************************************************************************************

!*****************************************************************************************
!> author: Jacob Williams
!
!  Unit vector of the cross product of two 3x1 vectors

    pure function ucross(v1,v2) result(u)

    implicit none

    real(wp),dimension(3),intent(in) :: v1
    real(wp),dimension(3),intent(in) :: v2
    real(wp),dimension(3)            :: u

    u = unit(cross(v1,v2))

    end function ucross
!*****************************************************************************************

!*****************************************************************************************
!> author: Jacob Williams
!  date: 7/20/2014
!
!  Rotate a 3x1 vector in space, given an axis and angle of rotation.
!
!# Reference
!   * [Wikipedia](http://en.wikipedia.org/wiki/Rodrigues%27_rotation_formula)

    pure subroutine axis_angle_rotation(v,k,theta,vrot)

    implicit none

    real(wp),dimension(3),intent(in)  :: v      !! vector to rotate
    real(wp),dimension(3),intent(in)  :: k      !! rotation axis
    real(wp),intent(in)               :: theta  !! rotation angle [rad]
    real(wp),dimension(3),intent(out) :: vrot   !! result

    real(wp),dimension(3) :: khat
    real(wp) :: ct,st

    ct = cos(theta)
    st = sin(theta)
    khat = unit(k)   !rotation axis unit vector

    vrot = v*ct + cross(khat,v)*st + khat*dot_product(khat,v)*(one-ct)

    end subroutine axis_angle_rotation
!*****************************************************************************************

!*****************************************************************************************
!> author: Jacob Williams
!  date: 7/20/2014
!
!  Computes the cross product matrix, where:
!  ``cross(a,b) == matmul(cross_matrix(a),b)``

    pure function cross_matrix(r) result(rcross)

    implicit none

    real(wp),dimension(3),intent(in) :: r
    real(wp),dimension(3,3)          :: rcross

    rcross(:,1) = [zero,r(3),-r(2)]
    rcross(:,2) = [-r(3),zero,r(1)]
    rcross(:,3) = [r(2),-r(1),zero]

    end function cross_matrix
!*****************************************************************************************

!*****************************************************************************************
!> author: Jacob Williams
!  date: 7/21/2014
!
!  Computes the outer product of the two vectors.

    pure function outer_product(a,b) result(c)

    implicit none

    real(wp),dimension(:),intent(in)    :: a
    real(wp),dimension(:),intent(in)    :: b
    real(wp),dimension(size(a),size(b)) :: c

    integer :: i

    do i=1,size(b)
        c(:,i) = a*b(i)
    end do

    end function outer_product
!*****************************************************************************************

!*****************************************************************************************
!> author: Jacob Williams
!  date: 7/21/2014
!
!  Computes the box product (scalar triple product) of the three vectors.

    pure function box_product(a,b,c) result(d)

    implicit none

    real(wp),dimension(:),intent(in) :: a
    real(wp),dimension(:),intent(in) :: b
    real(wp),dimension(:),intent(in) :: c
    real(wp) :: d

    d = dot_product(a,cross(b,c))

    end function box_product
!*****************************************************************************************

!*****************************************************************************************
!> author: Jacob Williams
!  date: 7/21/2014
!
!  The projection of one vector onto another vector.
!
!# Reference
!   * [Wikipedia](http://en.wikipedia.org/wiki/Gram-Schmidt_process)

    pure function vector_projection(a,b) result(c)

    implicit none

    real(wp),dimension(:),intent(in)       :: a  !! the original vector
    real(wp),dimension(size(a)),intent(in) :: b  !! the vector to project on to
    real(wp),dimension(size(a))            :: c  !! the projection of a onto b

    real(wp) :: bmag2

    bmag2 = dot_product(b,b)

    if (bmag2==zero) then
        c = zero
    else
        c = b * dot_product(a,b) / bmag2
    end if

    end function vector_projection
!*****************************************************************************************

!*****************************************************************************************
!>
!  Project a vector onto a plane.
!
!# Reference
!   * [Projection of a Vector onto a Plane](http://www.maplesoft.com/support/help/Maple/view.aspx?path=MathApps/ProjectionOfVectorOntoPlane)

    pure subroutine vector_projection_on_plane(a,b,c)

    implicit none

    real(wp),dimension(3),intent(in)  :: a !! the original vector
    real(wp),dimension(3),intent(in)  :: b !! the plane to project on to (a normal vector)
    real(wp),dimension(3),intent(out) :: c !! the projection of a onto the b plane

    c = a - vector_projection(a,b)

    end subroutine vector_projection_on_plane
!*****************************************************************************************

!*****************************************************************************************
!> author: Jacob Williams
!  date: 7/20/2014
!
!  Computes the rotation matrix that corresponds to a
!  rotation about the axis `k` by an angle `theta`.

    pure subroutine axis_angle_rotation_to_rotation_matrix(k,theta,rotmat)

    implicit none

    real(wp),dimension(3),intent(in)    :: k        !! rotation axis
    real(wp),intent(in)                 :: theta    !! rotation angle [rad]
    real(wp),dimension(3,3),intent(out) :: rotmat   !! rotation matrix

    real(wp),dimension(3,3),parameter :: I = &
            reshape([one,zero,zero,zero,one,zero,zero,zero,one],[3,3]) !! 3x3 identity matrix

    real(wp),dimension(3,3) :: w
    real(wp),dimension(3) :: khat
    real(wp) :: ct,st

    ct = cos(theta)
    st = sin(theta)
    khat = unit(k)
    w  = cross_matrix(khat)

    rotmat = I + w*st + matmul(w,w)*(one-ct)

    end subroutine axis_angle_rotation_to_rotation_matrix
!*****************************************************************************************

!*****************************************************************************************
!> author: Jacob Williams
!  date: 9/24/2014
!
!  Convert spherical (r,alpha,beta) to Cartesian (x,y,z).

    pure function spherical_to_cartesian(r,alpha,beta) result(rvec)

    implicit none

    real(wp),intent(in)   :: r        !! magnitude
    real(wp),intent(in)   :: alpha    !! right ascension [rad]
    real(wp),intent(in)   :: beta     !! declination [rad]
    real(wp),dimension(3) :: rvec     !! [x,y,z] vector

    rvec(1) = r * cos(alpha) * cos(beta)
    rvec(2) = r * sin(alpha) * cos(beta)
    rvec(3) = r * sin(beta)

    end function spherical_to_cartesian
!*****************************************************************************************

!*****************************************************************************************
!> author: Jacob Williams
!  date: 3/6/2016
!
!  Convert Cartesian (x,y,z) to spherical (r,alpha,beta).

    pure subroutine cartesian_to_spherical(rvec,r,alpha,beta)

    implicit none

    real(wp),dimension(3),intent(in) :: rvec     !! [x,y,z] vector
    real(wp),intent(out)             :: r        !! magnitude
    real(wp),intent(out)             :: alpha    !! right ascension [rad]
    real(wp),intent(out)             :: beta     !! declination [rad]

    real(wp) :: r1

    r1 = rvec(1)*rvec(1)+rvec(2)*rvec(2)
    r  = sqrt(r1+rvec(3)*rvec(3))

    if (r/=zero) then
        beta = atan2(rvec(3),sqrt(r1))
        if (r1/=zero) then
            alpha = atan2(rvec(2),rvec(1))
        else
            alpha = zero
        end if
    else
        alpha = zero
        beta = zero
    end if

    end subroutine cartesian_to_spherical
!*****************************************************************************************

!*****************************************************************************************
!> author: Jacob Williams
!  date: 2/3/2015
!
!  The 3x3 rotation matrix for a rotation about the x, y, or z-axis.
!
!  EXAMPLE
!```Fortran
!    real(wp),dimension(3,3) :: rotmat
!    real(wp),dimension(3) :: vec,vec2
!    real(wp) :: ang
!    ang = pi / 4.0_wp
!    vec = [1.414_wp, 0.0_wp, 0.0_wp]
!    rotmat = rotation_matrix(z_axis,ang)
!    vec2 = matmul(rotmat,vec)
!```

    pure function rotation_matrix(axis,angle) result(rotmat)

    implicit none

    real(wp),dimension(3,3) :: rotmat   !! the rotation matrix
    integer,intent(in)      :: axis     !! x_axis, y_axis, or z_axis
    real(wp),intent(in)     :: angle    !! angle in radians

    real(wp) :: c,s

    !precompute these:
    c = cos(angle)
    s = sin(angle)

    select case (axis)
    case(x_axis); rotmat = reshape([one, zero, zero, zero, c, -s, zero, s, c],[3,3])
    case(y_axis); rotmat = reshape([c, zero, s, zero, one, zero, -s, zero, c],[3,3])
    case(z_axis); rotmat = reshape([c, -s, zero, s, c, zero, zero, zero, one],[3,3])
    case default; rotmat = zero
    end select

    end function rotation_matrix
!*****************************************************************************************

!*****************************************************************************************
!> author: Jacob Williams
!  date: 3/19/2016
!
!  Time derivative of the 3x3 rotation matrix
!  for a rotation about the x, y, or z-axis.

    pure function rotation_matrix_dot(axis,angle,angledot) result(rotmatdot)

    implicit none

    real(wp),dimension(3,3) :: rotmatdot   !! the rotation matrix derivative \( d \mathbf{C} / d t \)
    integer,intent(in)      :: axis        !! x_axis, y_axis, or z_axis
    real(wp),intent(in)     :: angle       !! angle in radians
    real(wp),intent(in)     :: angledot    !! time derivative of angle in radians/sec

    real(wp) :: c,s

    !precompute these:
    c = cos(angle)
    s = sin(angle)

    !first compute d[C]/da (time derivate w.r.t. the angle):
    select case (axis)
    case(x_axis); rotmatdot = reshape([zero, zero, zero, zero, -s, -c, zero, c, -s],[3,3])
    case(y_axis); rotmatdot = reshape([-s, zero, c, zero, zero, zero, -c, zero, -s],[3,3])
    case(z_axis); rotmatdot = reshape([-s, -c, zero, c, -s, zero, zero, zero, zero],[3,3])
    case default
        rotmatdot = zero
        return
    end select

    rotmatdot = rotmatdot * angledot    ! d[C]/dt = d[C]/da * da/dt

    end function rotation_matrix_dot
!*****************************************************************************************

!*****************************************************************************************
!> author: Jacob Williams
!  date: 3/13/2015
!
!  The angle between two vectors (in radians).

    pure function angle_between_vectors(v1,v2) result(ang)

    implicit none

    real(wp)                         :: ang  !! [rad]
    real(wp),dimension(3),intent(in) :: v1
    real(wp),dimension(3),intent(in) :: v2

    real(wp) :: d,c

    d   = dot_product(v1,v2)
    c   = norm2(cross(v1,v2))
    ang = atan2(c,d)

    end function angle_between_vectors
!*****************************************************************************************

!*****************************************************************************************
!>
!  Put the vector in the vector and update the index

    subroutine fill_vector_with_vector(x, vals, i)

    implicit none

    real(wp),dimension(:),intent(inout) :: x
    real(wp),dimension(:),intent(in) :: vals
    integer,intent(inout) :: i !! should be initialized to 0 before the first call

    integer :: j !! counter

    do j = 1, size(vals)
        call fill_vector(x,vals(j),i)
    end do

    end subroutine fill_vector_with_vector
!*****************************************************************************************

!*****************************************************************************************
!>
!  Put the value in the vector and update the index

    subroutine fill_vector_with_scalar(x, val, i)

    implicit none

    real(wp),dimension(:),intent(inout) :: x
    real(wp),intent(in) :: val
    integer,intent(inout) :: i !! should be initialized to 0 before the first call

    i = i + 1
    if (i>size(x)) error stop 'error in fill_vector: x is not large enough.'
    x(i) = val

    end subroutine fill_vector_with_scalar
!*****************************************************************************************

!*****************************************************************************************
!>
!  Put the vector in the vector and update the index (character version)

    subroutine fill_char_vector_with_vector(x, vals, i)

    implicit none

    character(len=*),dimension(:),intent(inout) :: x
    character(len=*),dimension(:),intent(in) :: vals
    integer,intent(inout) :: i !! should be initialized to 0 before the first call

    integer :: j !! counter

    do j = 1, size(vals)
        call fill_vector(x,vals(j),i)
    end do

    end subroutine fill_char_vector_with_vector
!*****************************************************************************************

!*****************************************************************************************
!>
!  Put the value in the vector and update the index (character version)

    subroutine fill_char_vector_with_scalar(x, val, i)

    implicit none

    character(len=*),dimension(:),intent(inout) :: x
    character(len=*),intent(in) :: val
    integer,intent(inout) :: i !! should be initialized to 0 before the first call

    i = i + 1
    if (i>size(x)) error stop 'error in fill_vector: x is not large enough.'
    x(i) = val

    end subroutine fill_char_vector_with_scalar
!*****************************************************************************************

!*****************************************************************************************
!>
!  Extract a vector from the vector and update the index

    subroutine extract_vector_from_vector(vals, x, i)

    implicit none

    real(wp),dimension(:),intent(out) :: vals
    real(wp),dimension(:),intent(in) :: x
    integer,intent(inout) :: i !! should be initialized to 0 before the first call

    integer :: j !! counter

    do j = 1, size(vals)
        call extract_vector(vals(j),x,i)
    end do

    end subroutine extract_vector_from_vector
!*****************************************************************************************

!*****************************************************************************************
!>
!  Extract the value from the vector and update the index

    subroutine extract_scalar_from_vector(val, x, i)

    implicit none

    real(wp),intent(out) :: val
    real(wp),dimension(:),intent(in) :: x
    integer,intent(inout) :: i !! should be initialized to 0 before the first call

    i = i + 1
    if (i>size(x)) error stop 'error in extract_vector: x is not large enough.'
    val = x(i)

    end subroutine extract_scalar_from_vector
!*****************************************************************************************

!*****************************************************************************************
!> author: Jacob Williams
!  date: 7/20/2014
!
!  Unit test routine for the [[vector_module]].

    subroutine vector_test()

    implicit none

    integer :: i
    real(wp) :: theta
    real(wp),dimension(3) :: v,k,v2,v3
    real(wp),dimension(3,3) :: rotmat

    write(*,*) ''
    write(*,*) '---------------'
    write(*,*) ' vector_test'
    write(*,*) '---------------'
    write(*,*) ''

    v = [1.2_wp, 3.0_wp, -5.0_wp]
    k = [-0.1_wp, 16.2_wp, 2.1_wp]
    theta = 0.123_wp

    call axis_angle_rotation(v,k,theta,v2)

    call axis_angle_rotation_to_rotation_matrix(k,theta,rotmat)
    v3 = matmul(rotmat,v)

    write(*,*) 'Single test:'
    write(*,*) ''
    write(*,*) '  v1   :', v
    write(*,*) '  v2   :', v2
    write(*,*) '  v3   :', v3
    write(*,*) '  Error:', v3-v2

    write(*,*) ''
    write(*,*) '0-360 test:'
    write(*,*) ''
    do i=0,360,10

        theta = i * 180.0_wp/pi

        call axis_angle_rotation(v,k,theta,v2)

        call axis_angle_rotation_to_rotation_matrix(k,theta,rotmat)
        v3 = matmul(rotmat,v)

        write(*,*) 'Error:', norm2(v3-v2)

    end do

    !z-axis rotation test:
    theta = pi / 4.0_wp
    v = [one/cos(theta), 0.0_wp, 0.0_wp]
    rotmat = rotation_matrix(z_axis,theta)
    v2 = matmul(rotmat,v)
    write(*,*) v2    !should be [1, -1, 0]


    end subroutine vector_test
 !****************************************************************************************

!*****************************************************************************************
    end module vector_module
!*****************************************************************************************