fmm_m2m_ztranslate_work Subroutine

private subroutine fmm_m2m_ztranslate_work(z, p, vscales, vcnk, alpha, src_m, beta, dst_m, work)

Direct M2M translation over OZ axis

Compute the following matrix-vector product: \f[ \mathrm{dst} = \beta \mathrm{dst} + \alpha M_M \mathrm{src}, \f] where \f$ \mathrm{dst} \f$ is a vector of coefficients of output spherical harmonics, \f$ \mathrm{src} \f$ is a vector of coefficients of input spherical harmonics and \f$ M_M \f$ is a matrix of multipole-to-multipole translation over OZ axis.

@param[in] z: OZ coordinate from new to old centers of harmonics @param[in] src_r: Radius of old harmonics @param[in] dst_r: Radius of new harmonics @parma[in] p: Maximal degree of spherical harmonics @param[in] vscales: Normalization constants for harmonics @param[in] vcnk: Square roots of combinatorial numbers C_n^k @param[in] alpha: Scalar multipler for alpha @param[in] src_m: Expansion in old harmonics @param[in] beta: Scalar multipler for dst_m @param[inout] dst_m: Expansion in new harmonics @param[out] work: Temporary workspace of a size (2*(p+1))

Arguments

Type IntentOptional Attributes Name
real(kind=rp), intent(in) :: z
integer, intent(in) :: p
real(kind=rp), intent(in) :: vscales((p+1)*(p+1))
real(kind=rp), intent(in) :: vcnk((2*p+1)*(p+1))
real(kind=rp), intent(in) :: alpha
real(kind=rp), intent(in) :: src_m((p+1)*(p+1))
real(kind=rp), intent(in) :: beta
real(kind=rp), intent(inout) :: dst_m((p+1)*(p+1))
real(kind=rp), intent(out), target :: work(2*(p+1))

Called by

proc~~fmm_m2m_ztranslate_work~~CalledByGraph proc~fmm_m2m_ztranslate_work fmm_m2m_ztranslate_work proc~fmm_m2m_rotation_work fmm_m2m_rotation_work proc~fmm_m2m_rotation_work->proc~fmm_m2m_ztranslate_work proc~fmm_m2m fmm_m2m proc~fmm_m2m->proc~fmm_m2m_rotation_work proc~tree_p2m tree_p2m proc~tree_p2m->proc~fmm_m2m proc~tree_m2m tree_m2m proc~tree_m2m->proc~fmm_m2m proc~fmm_solve_for_multipoles fmm_solve_for_multipoles proc~fmm_solve_for_multipoles->proc~tree_p2m proc~fmm_solve_for_multipoles->proc~tree_m2m proc~fmm_solve fmm_solve proc~fmm_solve->proc~tree_m2m proc~prepare_fmm_ext_ipd prepare_fmm_ext_ipd proc~prepare_fmm_ext_ipd->proc~fmm_solve_for_multipoles proc~preapare_fmm_static preapare_fmm_static proc~preapare_fmm_static->proc~fmm_solve_for_multipoles proc~field_extd2d field_extD2D proc~field_extd2d->proc~prepare_fmm_ext_ipd proc~prepare_fmm_ipd prepare_fmm_ipd proc~prepare_fmm_ipd->proc~prepare_fmm_ext_ipd proc~elec_prop_m2m elec_prop_M2M proc~elec_prop_m2m->proc~preapare_fmm_static proc~elec_prop_m2d elec_prop_M2D proc~elec_prop_m2d->proc~preapare_fmm_static proc~tmatvec_otf TMatVec_otf proc~tmatvec_otf->proc~field_extd2d proc~elec_prop_d2m elec_prop_D2M proc~elec_prop_d2m->proc~prepare_fmm_ipd proc~prepare_polelec prepare_polelec proc~prepare_polelec->proc~elec_prop_m2d proc~prepare_polelec->proc~elec_prop_d2m proc~elec_prop_d2d elec_prop_D2D proc~prepare_polelec->proc~elec_prop_d2d proc~prepare_fixedelec prepare_fixedelec proc~prepare_fixedelec->proc~elec_prop_m2m proc~elec_prop_d2d->proc~prepare_fmm_ipd proc~polelec_geomgrad polelec_geomgrad proc~polelec_geomgrad->proc~prepare_polelec proc~ommp_get_polelec_energy ommp_get_polelec_energy proc~ommp_get_polelec_energy->proc~prepare_polelec proc~energy_mm_pol energy_MM_pol proc~ommp_get_polelec_energy->proc~energy_mm_pol proc~ommp_set_external_field ommp_set_external_field proc~ommp_set_external_field->proc~prepare_polelec proc~fixedelec_geomgrad fixedelec_geomgrad proc~fixedelec_geomgrad->proc~prepare_fixedelec proc~energy_mm_pol->proc~prepare_polelec proc~energy_mm_mm energy_MM_MM proc~energy_mm_mm->proc~prepare_fixedelec proc~ommp_polelec_geomgrad ommp_polelec_geomgrad proc~ommp_polelec_geomgrad->proc~polelec_geomgrad proc~c_ommp_get_polelec_energy C_ommp_get_polelec_energy proc~c_ommp_get_polelec_energy->proc~ommp_get_polelec_energy proc~ommp_get_full_ele_energy ommp_get_full_ele_energy proc~ommp_get_full_ele_energy->proc~ommp_get_polelec_energy proc~ommp_get_fixedelec_energy ommp_get_fixedelec_energy proc~ommp_get_full_ele_energy->proc~ommp_get_fixedelec_energy proc~ommp_set_external_field_nomm ommp_set_external_field_nomm proc~ommp_set_external_field_nomm->proc~ommp_set_external_field proc~ommp_full_geomgrad ommp_full_geomgrad proc~ommp_full_geomgrad->proc~polelec_geomgrad proc~ommp_full_geomgrad->proc~fixedelec_geomgrad proc~c_ommp_set_external_field_nomm C_ommp_set_external_field_nomm proc~c_ommp_set_external_field_nomm->proc~ommp_set_external_field proc~c_ommp_set_external_field C_ommp_set_external_field proc~c_ommp_set_external_field->proc~ommp_set_external_field proc~ommp_fixedelec_geomgrad ommp_fixedelec_geomgrad proc~ommp_fixedelec_geomgrad->proc~fixedelec_geomgrad proc~ommp_get_fixedelec_energy->proc~energy_mm_mm proc~c_ommp_polelec_geomgrad C_ommp_polelec_geomgrad proc~c_ommp_polelec_geomgrad->proc~ommp_polelec_geomgrad proc~c_ommp_full_geomgrad C_ommp_full_geomgrad proc~c_ommp_full_geomgrad->proc~ommp_full_geomgrad proc~c_ommp_get_full_ele_energy C_ommp_get_full_ele_energy proc~c_ommp_get_full_ele_energy->proc~ommp_get_full_ele_energy proc~c_ommp_fixedelec_geomgrad C_ommp_fixedelec_geomgrad proc~c_ommp_fixedelec_geomgrad->proc~ommp_fixedelec_geomgrad proc~ommp_get_full_energy ommp_get_full_energy proc~ommp_get_full_energy->proc~ommp_get_full_ele_energy proc~c_ommp_get_fixedelec_energy C_ommp_get_fixedelec_energy proc~c_ommp_get_fixedelec_energy->proc~ommp_get_fixedelec_energy proc~c_ommp_get_full_energy C_ommp_get_full_energy proc~c_ommp_get_full_energy->proc~ommp_get_full_energy

Contents


Source Code

    subroutine fmm_m2m_ztranslate_work(z, p, vscales, vcnk, alpha, &
        & src_m, beta, dst_m, work)
    implicit none
    ! Inputs
    integer, intent(in) :: p
    real(rp), intent(in) :: z, vscales((p+1)*(p+1)), &
        & vcnk((2*p+1)*(p+1)), alpha, src_m((p+1)*(p+1)), beta
    ! Output
    real(rp), intent(inout) :: dst_m((p+1)*(p+1))
    ! Temporary workspace
    real(rp), intent(out), target :: work(2*(p+1))
    ! Local variables
    real(rp) :: r2, tmp1, tmp2, tmp3, res1, res2
    integer :: j, k, n, indj, indjn, indjk1, indjk2
    ! Pointers for temporary values of powers
    real(rp), pointer :: pow_r2(:)
    ! In case alpha is 0.0 just do a proper scaling of output
    if (abs(alpha) < eps_rp) then
        if (abs(beta) < eps_rp) then
            dst_m = 0.0
        else
            dst_m = beta * dst_m
        end if
        return
    end if
    ! Now alpha is non-0.0
    ! If harmonics have different centers
    if (abs(z) > eps_rp) then
        ! Prepare pointers
        pow_r2(1:p+1) => work(1:p+1)
        ! Get ratios r1 and r2
        r2 = z 
        pow_r2(1) = 1.0
        do j = 2, p+1
            pow_r2(j) = pow_r2(j-1) * r2
        end do
        ! Do actual M2M
        ! Overwrite output if beta is 0.0
        if (abs(beta) < eps_rp) then
            do j = 0, p
                ! Offset for dst_m
                indj = j*j + j + 1
                ! k = 0
                tmp1 = alpha * vscales(indj)
                tmp2 = tmp1
                res1 = 0.0
                ! Offset for vcnk
                indjk1 = j*(j+1)/2 + 1
                do n = 0, j
                    ! Offset for src_m
                    indjn = (j-n)**2 + (j-n) + 1
                    tmp3 = pow_r2(n+1) / &
                        & vscales(indjn) * vcnk(indjk1+n)**2
                    res1 = res1 + tmp3*src_m(indjn)
                end do
                dst_m(indj) = tmp2 * res1
                ! k != 0
                do k = 1, j
                    tmp2 = tmp1
                    res1 = 0.0
                    res2 = 0.0
                    ! Offsets for vcnk
                    indjk1 = (j-k)*(j-k+1)/2 + 1
                    indjk2 = (j+k)*(j+k+1)/2 + 1
                    do n = 0, j-k
                        ! Offset for src_m
                        indjn = (j-n)**2 + (j-n) + 1
                        tmp3 = pow_r2(n+1) / &
                            & vscales(indjn) * vcnk(indjk1+n) * &
                            & vcnk(indjk2+n)
                        res1 = res1 + tmp3*src_m(indjn+k)
                        res2 = res2 + tmp3*src_m(indjn-k)
                    end do
                    dst_m(indj+k) = tmp2 * res1
                    dst_m(indj-k) = tmp2 * res2
                end do
            end do
        ! Update output if beta is non-0.0
        else
            do j = 0, p
                ! Offset for dst_m
                indj = j*j + j + 1
                ! k = 0
                tmp1 = alpha * vscales(indj)
                tmp2 = tmp1
                res1 = 0.0
                ! Offset for vcnk
                indjk1 = j * (j+1) /2 + 1
                do n = 0, j
                    ! Offset for src_m
                    indjn = (j-n)**2 + (j-n) + 1
                    tmp3 = pow_r2(n+1) / &
                        & vscales(indjn) * vcnk(indjk1+n)**2
                    res1 = res1 + tmp3*src_m(indjn)
                end do
                dst_m(indj) = beta*dst_m(indj) + tmp2*res1
                ! k != 0
                do k = 1, j
                    tmp2 = tmp1
                    res1 = 0.0
                    res2 = 0.0
                    ! Offsets for vcnk
                    indjk1 = (j-k)*(j-k+1)/2 + 1
                    indjk2 = (j+k)*(j+k+1)/2 + 1
                    do n = 0, j-k
                        ! Offset for src_m
                        indjn = (j-n)**2 + (j-n) + 1
                        tmp3 = pow_r2(n+1) / &
                            & vscales(indjn) * vcnk(indjk1+n) * &
                            & vcnk(indjk2+n)
                        res1 = res1 + tmp3*src_m(indjn+k)
                        res2 = res2 + tmp3*src_m(indjn-k)
                    end do
                    dst_m(indj+k) = beta*dst_m(indj+k) + tmp2*res1
                    dst_m(indj-k) = beta*dst_m(indj-k) + tmp2*res2
                end do
            end do
        end if
    ! If harmonics are located at the same point
    else
        ! Overwrite output if beta is 0.0
        if (abs(beta) < eps_rp) then
            tmp1 = alpha 
            do j = 0, p
                indj = j*j + j + 1
                do k = indj-j, indj+j
                    dst_m(k) = tmp1 * src_m(k)
                end do
            end do
        ! Update output if beta is non-0.0
        else
            tmp1 = alpha
            do j = 0, p
                indj = j*j + j + 1
                do k = indj-j, indj+j
                    dst_m(k) = beta*dst_m(k) + tmp1*src_m(k)
                end do
                tmp1 = tmp1
            end do
        end if
    end if
end subroutine fmm_m2m_ztranslate_work