coords_polar_m.f90 Source File


This file depends on

sourcefile~~coords_polar_m.f90~~EfferentGraph sourcefile~coords_polar_m.f90 coords_polar_m.f90 sourcefile~comm_handling_m.f90 comm_handling_m.f90 sourcefile~coords_polar_m.f90->sourcefile~comm_handling_m.f90 sourcefile~constants_m.f90 constants_m.f90 sourcefile~coords_polar_m.f90->sourcefile~constants_m.f90 sourcefile~equilibrium_m.f90 equilibrium_m.f90 sourcefile~coords_polar_m.f90->sourcefile~equilibrium_m.f90 sourcefile~precision_m.f90 precision_m.f90 sourcefile~coords_polar_m.f90->sourcefile~precision_m.f90 sourcefile~root_finding_m.f90 root_finding_m.f90 sourcefile~coords_polar_m.f90->sourcefile~root_finding_m.f90 sourcefile~screen_io_m.f90 screen_io_m.f90 sourcefile~coords_polar_m.f90->sourcefile~screen_io_m.f90 sourcefile~slab_equilibrium.f90 slab_equilibrium.f90 sourcefile~coords_polar_m.f90->sourcefile~slab_equilibrium.f90 sourcefile~constants_m.f90->sourcefile~precision_m.f90 sourcefile~equilibrium_m.f90->sourcefile~precision_m.f90 sourcefile~root_finding_m.f90->sourcefile~precision_m.f90 sourcefile~screen_io_m.f90->sourcefile~precision_m.f90 sourcefile~slab_equilibrium.f90->sourcefile~equilibrium_m.f90 sourcefile~slab_equilibrium.f90->sourcefile~precision_m.f90 sourcefile~slab_equilibrium.f90->sourcefile~screen_io_m.f90 sourcefile~descriptors_m.f90 descriptors_m.f90 sourcefile~slab_equilibrium.f90->sourcefile~descriptors_m.f90 sourcefile~params_equi_slab_m.f90 params_equi_slab_m.f90 sourcefile~slab_equilibrium.f90->sourcefile~params_equi_slab_m.f90 sourcefile~descriptors_m.f90->sourcefile~screen_io_m.f90 sourcefile~params_equi_slab_m.f90->sourcefile~precision_m.f90 sourcefile~params_equi_slab_m.f90->sourcefile~screen_io_m.f90 sourcefile~error_handling_m.f90 error_handling_m.f90 sourcefile~params_equi_slab_m.f90->sourcefile~error_handling_m.f90 sourcefile~status_codes_m.f90 status_codes_m.f90 sourcefile~params_equi_slab_m.f90->sourcefile~status_codes_m.f90 sourcefile~error_handling_m.f90->sourcefile~comm_handling_m.f90 sourcefile~error_handling_m.f90->sourcefile~precision_m.f90 sourcefile~error_handling_m.f90->sourcefile~screen_io_m.f90 sourcefile~error_handling_m.f90->sourcefile~status_codes_m.f90

Files dependent on this one

sourcefile~~coords_polar_m.f90~~AfferentGraph sourcefile~coords_polar_m.f90 coords_polar_m.f90 sourcefile~diagnose_poincare.f90 diagnose_poincare.f90 sourcefile~diagnose_poincare.f90->sourcefile~coords_polar_m.f90 sourcefile~polar_grid_m.f90 polar_grid_m.f90 sourcefile~polar_grid_m.f90->sourcefile~coords_polar_m.f90 sourcefile~polar_map_factory_m.f90 polar_map_factory_m.f90 sourcefile~polar_map_factory_m.f90->sourcefile~coords_polar_m.f90 sourcefile~polar_map_factory_m.f90->sourcefile~polar_grid_m.f90 sourcefile~safety_factor_m.f90 safety_factor_m.f90 sourcefile~safety_factor_m.f90->sourcefile~coords_polar_m.f90 sourcefile~zonal_averages_factory_m.f90 zonal_averages_factory_m.f90 sourcefile~zonal_averages_factory_m.f90->sourcefile~coords_polar_m.f90 sourcefile~zonal_averages_factory_m.f90->sourcefile~polar_grid_m.f90 sourcefile~polar_grid_s.f90 polar_grid_s.f90 sourcefile~polar_grid_s.f90->sourcefile~polar_grid_m.f90

Source Code

module coords_polar_m
    !! Coordinate transformations Cartesian (x,y) <-> Polar (rho, theta)
    !! rho is normalized flux surface label
    !! theta is geometric poloidal angle, measured ccw from +x with
    !!    respect to magnetic axis
    use precision_m, only: FP, DP
    use constants_m, only: PI
    use screen_io_m, only: get_stderr
    use comm_handling_m, only: is_master
    use equilibrium_m, only: equilibrium_t
    use slab_equilibrium_m, only: slab_t
    use root_finding_m, only: find_zero, func_1D_t
    implicit none

    public :: cart_to_polar
    public :: polar_to_cart
    public :: jacobian_polar
    private :: aux_ptc_zero
    private :: set_equilibrium

    type, private, extends(func_1D_t) :: func_1D_equi_t
        class(equilibrium_t), private, pointer :: equi => null()
    contains
        procedure :: func => aux_ptc_zero
        procedure, private :: set_equilibrium
    end type

contains

    subroutine cart_to_polar(equi, x, y, phi, rho, theta)
        !! Coordinate transformation from polar to Cartesian
        implicit none
        class(equilibrium_t), intent(inout) :: equi
        !! Equilibrium
        real(FP), intent(in) :: x
        !! Cartesian coordinate x
        real(FP), intent(in) :: y
        !! Cartesian coordinate y
        real(FP), intent(in) :: phi
        !! Toroidal angle
        real(FP), intent(out) :: rho
        !! Polar coordinate rho (normalized flux surface label)
        real(FP), intent(out) :: theta
        !! Polar coordinate theta (geometric poloidal angle)

        real(FP) :: xt, yt

        select type(equi)
            type is(slab_t)
                rho = x
                theta = 2.0_FP * PI * y / (equi%ymax - equi%ymin)
                return
        end select

       rho = equi%rho(x, y, phi)

       xt = x - equi%x0
       yt = y - equi%y0
       if (xt > 0.0_FP) then
            theta = atan(yt/xt)
        elseif (xt < 0.0_FP) then
            theta = atan(yt/xt) + PI
        else
            if (yt > 0.0_FP) then
                theta = PI/2.0_FP
            elseif (yt < 0.0_FP) then
                theta = PI*3.0_FP/2.0_FP
            else
                theta = 0.0_8
            endif
        endif
        theta = mod(theta, 2.0_FP * PI)
        if (theta < 0.0_FP) then
            theta = theta + 2.0_FP*PI
        endif

    end subroutine

    subroutine polar_to_cart(equi, rho, theta, phi, x, y)
        !! Coordinate transformation from Cartesian to polar
        ! Basic description of algorithm:
        ! - Searches along contours of given theta
        ! - Finds distance t to magnetic axis, where rho matches  via zero point search)
        ! - Interval of zero point search is gradually increased (tryloop, trying, e.g. to avoid to run of bspline grid for equilibria)
        ! Further notes:
        ! If an error is being returned that no point is found, try increasing
        ! either maximum_number_of_tries (more expensive) or
        ! increase_range_factor (more risky -- might run off the spline grid for
        ! NUMERICAL equilibrium)
        !
        ! If an error from NUMERICAL psi (calling off the spline grid) is raised
        ! from this region, try reducing the increase_range_factor
        implicit none
        class(equilibrium_t), intent(inout) :: equi
        !! Equilibrium
        real(FP), intent(in) :: rho
        !! Polar coordinate rho
        real(FP), intent(in) :: theta
        !! Polar coordinate theta (geometric poloidal angle)
        real(FP), intent(in) :: phi
        !! Toroidal angle
        real(FP), intent(out) :: x
        !! Cartesian coordinate x
        real(FP), intent(out) :: y
        !! Cartesian coordinate y

        integer, parameter :: ntry_max = 100
        ! Maximum number of intervalls searched
        real(DP), parameter :: tolerance = 1.0E-8_DP
        ! Tolerance for zero search
        real(DP), parameter :: intervall_increase_factor = 1.1_DP
        ! Factor that search intervall is increased at each search

        integer :: iuser(1), find_zero_status, try_count
        real(DP) :: t, ruser(3)
        real(DP) :: intervall_min, intervall_max
        real(DP) :: axis_x, axis_y

        type(func_1D_equi_t) :: aux_ptc_zero

        select type(equi)
            type is(slab_t)
                x = rho
                y = (equi%ymax - equi%ymin) * theta / (2.0_FP * PI)
                return
        end select
        if(rho < tolerance) then
            ! Return the magnetic axis if rho is smaller than the tolerance
            call equi%mag_axis_loc(phi, x, y)
            return
        endif

        call aux_ptc_zero%set_equilibrium(equi)

        ! start to search along contours of theta
        ruser(1) = real(rho, kind=DP)
        ruser(2) = real(theta, kind=DP)
        ruser(3) = real(phi, kind=DP)

        intervall_min = 0.0_DP
        intervall_max = 5.0E-2_DP
        try_count = 0

        tryloop: do

            call find_zero(intervall_min, intervall_max, tolerance, aux_ptc_zero, iuser, ruser, t, find_zero_status)

            if (find_zero_status == 0) then

                ! normal exit, point found
                call equi%mag_axis_loc(phi, axis_x, axis_y)
                x = axis_x + abs(real(t, kind=FP)) * cos(theta)
                y = axis_y + abs(real(t, kind=FP)) * sin(theta)
                exit tryloop

            elseif (try_count > ntry_max) then

                write(get_stderr(), *) &
                    'error(polar_to_cart): point not found (rho, theta):', &
                    rho, theta
                write(get_stderr(), *) &
                    '... reached interval_max= ', intervall_max
                error stop

            else

                try_count = try_count + 1
                intervall_max =  intervall_increase_factor * intervall_max

            endif

        enddo tryloop

    end subroutine polar_to_cart

    real(FP) function jacobian_polar(equi, x, y, phi)
        !! Jacobian of polar coordinate system
        !! To compute derivatives on rho, finite difference is used
        !! N.B. currently only axisymmetric (2D), proper inclusion of toroidal
        !!   phi will change this function
        implicit none
        class(equilibrium_t), intent(inout) :: equi
        !! Equilibrium
        real(FP), intent(in) :: x
        !! Cartesian coordinate x
        real(FP), intent(in) :: y
        !! Cartesian coordinate y
        real(FP), intent(in) :: phi
        !! Toroidal angle
        real(FP), parameter :: eps_findiff=1.0E-6_FP
        !! Finite distance to comput derivatives on rho

        real(FP) :: xa, ya, rho1,rho2, theta, drhodx, drhody
        real(FP) :: grhorho, gthetatheta, gphiphi, grhotheta

        select type(equi)
            type is(slab_t)
                jacobian_polar = (equi%ymax-equi%ymin) / (2*PI)
                return
        end select

        xa = x-equi%x0
        ya = y-equi%y0

        ! components of metric tensor
        gphiphi = 1.0_FP / (equi%jacobian(x, y, phi)**2)
        gthetatheta=1.0_FP/(xa**2+ya**2)

        call cart_to_polar(equi, x+eps_findiff, y, phi, rho1, theta )
        call cart_to_polar(equi, x-eps_findiff, y, phi, rho2, theta )
        drhodx = (rho1-rho2) / (2.0_FP*eps_findiff)
        call cart_to_polar(equi, x, y+eps_findiff, phi, rho1, theta )
        call cart_to_polar(equi, x, y-eps_findiff, phi, rho2, theta )
        drhody = (rho1-rho2) / (2.0_8*eps_findiff)

        grhorho=drhodx**2+drhody**2
        grhotheta=(-ya*drhodx + xa*drhody) / (xa**2+ya**2)

        ! polar jacobian
        jacobian_polar = grhorho * gthetatheta * gphiphi - gphiphi * grhotheta * grhotheta
        jacobian_polar = 1.0_FP/sqrt(jacobian_polar)

    end function

    subroutine set_equilibrium(this, equi)
        !! Sets equilibrium pointer for zero point search function
        class(func_1D_equi_t), intent(inout) :: this
        !! Instance of the type
        class(equilibrium_t), target, intent(in) :: equi
        !! Equilibrium

        this%equi => equi

    end subroutine set_equilibrium

    real(DP) function aux_ptc_zero(this, t, iuser, ruser)
        ! Auxiliary function for routine polar_to_cart, which evaluates zero if rho_target = rho for fixed theta
        class(func_1D_equi_t), intent(in) :: this
        !! Instance of the type
        real(DP),intent(in) :: t
        !! Distance to magnetic axis
        integer,intent(in) :: iuser(:)
        !! Dummy not used
        real(DP),intent(in) :: ruser(:)
        !! ruser(1) : rho_target, ruser(2) : theta, ruser(3) : phi

        real(FP) :: rho_target, theta, phi, x, y, rho_trial, theta_trial
        real(FP) :: axis_x, axis_y

        rho_target = real(ruser(1), kind=FP)
        theta      = real(ruser(2), kind=FP)
        phi        = real(ruser(3), kind=FP)

        call this%equi%mag_axis_loc(phi, axis_x, axis_y)
        x = axis_x + abs(t) * cos(theta)
        y = axis_y + abs(t) * sin(theta)

        call cart_to_polar(this%equi, x, y, phi, rho_trial, theta_trial)

        aux_ptc_zero = real(rho_trial-rho_target, kind=DP)

    end function

end module