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))
| Type | Intent | Optional | 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)) | 
    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