inelastic_mapping_procedures.f90 Source File


Source Code

!-----------------------------------------------------------------------------------------------------------------------------------
! This file is part of ReMKiT1D.
!
! ReMKiT1D is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as 
! published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
!
! ReMKiT1D is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of 
! MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
!
! You should have received a copy of the GNU General Public License along with ReMKiT1D. If not, see <https://www.gnu.org/licenses/>. 
!
! Copyright 2023 United Kingdom Atomic Energy Authority (stefan.mijin@ukaea.uk)
!-----------------------------------------------------------------------------------------------------------------------------------
submodule (inelastic_mapping) inelastic_mapping_procedures
    !! author: Stefan Mijin 
    !! 
    !! Contains module procedures associated with inelastic collision mappings

implicit none

!-----------------------------------------------------------------------------------------------------------------------------------
contains
!-----------------------------------------------------------------------------------------------------------------------------------
pure module function inelWeights(space,E) result(wMat)
    !! Calculate velocity space inelastic mapping weight matrix for transition with energy E on given velocity space. Assumes that 
    !! velocity and energy are suitably normalized (i.e. v^2 is an energy)

    type(VSpace)        ,intent(in) :: space !! Velocity space object to calculate the mapping on
    real(rk)            ,intent(in) :: E    !! Mapping transition energy
    type(SparseRowData)             :: wMat

    real(rk) ,allocatable ,dimension(:) :: vGrid ,vWidths ,eGrid ! vGrid data, eGrid is just vGrid**2

    logical ,allocatable ,dimension(:,:) :: wMap ! Logical map of emitters to absorbers, true where there is an allowed transition

    integer(ik) ,allocatable ,dimension(:,:) :: idealAbsorbers

    integer(ik) :: i, j, numV ,idealEmitter ,firstCell,secondCell ,numPairs 

    integer(ik) :: deltaFirst ,deltaSecond !Numbers of absorber pairs in which first/second ideal absorber are present

    type(IntArray) ,allocatable ,dimension(:) :: absorberList

    real(rk) :: aListEnergyWidth, & !Sum of energy widths of all cells in particular absorber list
                gammaP, & !Pair emission fraction
                eInterp 

    real(rk) ,allocatable ,dimension(:,:) :: denseWMat

    if (assertions) call assertPure(space%isDefined(),"Undefined vSpace passed to inelWeights function")

    vGrid = space%getVGrid()
    vWidths = space%getVCellWidths()
    eGrid = vGrid**2
    numV = size(vGrid)
    allocate(wMap(numV,numV))
    allocate(denseWMat(numV,numV))
    denseWMat = 0
    wMap = .false.
    
    !Find ideal absorbers for each eligible point and set wMap to true where both absorbers exist
    allocate(idealAbsorbers(2,numV))
    idealAbsorbers = 0
    do i = 1,numV 
        if (eGrid(i) >= E) idealAbsorbers(:,i) = space%getNearestPoints(sqrt(eGrid(i) - E))
        if (all(idealAbsorbers(:,i) > 0)) wMap(idealAbsorbers(:,i),i) = .true.
    end do

    !Find ideal emitters for each point and update wMap accordingly 
    
    do i  = 1,numV
        idealEmitter = 0
        if (eGrid(i) >= - E) then 
            idealEmitter = space%getContainingVCell(sqrt(eGrid(i) + E))
            ! If ideal emitter is found, make sure that it has enough energy to emit to cell i
            if (idealEmitter > 0) then 
                if (eGrid(idealEmitter)-eGrid(i) > E) then !if superelastic emitter doesn't have enough energy
                    idealEmitter = idealEmitter + (int(sign(real(1,kind=rk),E),kind=ik) - 1)/2 !if E is negative shift emitter to left 
                else !if inelastic emitter doesn't have enough energy
                    idealEmitter = idealEmitter + (int(sign(real(1,kind=rk),E),kind=ik) + 1)/2 !if E is positive shift emitter to
                end if
            end if
            !Correct for idealEmitter > numV case 
            if (idealEmitter > numV) idealEmitter = 0
        end if
        if (idealEmitter > 0) then 
            !Register i as an absorber of its ideal emitter
            wMap(i,idealEmitter) = .true.
        else
            !If no ideal emitter is found, remove cell i from all absorber pairs
            wMap(i,:) = .false. 
        end if
    end do

    !Final pass through wMap removes all emitters that have lost one of their ideal absorbers after last step

    do i = 1,numV 
        if (all(idealAbsorbers(:,i) > 0)) then 
            if (.not. all(wMap(idealAbsorbers(:,i),i))) wMap(:,i) = .false.
        else
            wMap(:,i) = .false.
        end if
    end do

    allocate(absorberList(numV))
    do i = 1,numV 
        !Fill out total absorber list
        absorberList(i)%entry = findIndices(wMap(:,i))
        numPairs = size(absorberList(i)%entry)
        if (numPairs > 0) numPairs = numPairs - 1
        if (numPairs > 0) then 
            aListEnergyWidth = 2*sum(vGrid(absorberList(i)%entry)*vWidths(absorberList(i)%entry))
            deltaFirst = count(wMap(idealAbsorbers(2,i):numV,i)) !Number of times first ideal absorber appears in a pair
            deltaSecond = count(wMap(1:idealAbsorbers(1,i),i))   !Number of times second ideal absorber appearts in a pair
            do j = 1, numPairs 
                if (absorberList(i)%entry(j) <= idealAbsorbers(1,i)) then !Pairs which contain points <= to the first ideal absorber
                    firstCell = absorberList(i)%entry(j)
                    secondCell = idealAbsorbers(2,i)
                    gammaP = 2 * vGrid(firstCell) * vWidths(firstCell)  !Contribution from first (non-ideal) absorber
                                                         
                    if (absorberList(i)%entry(j) == idealAbsorbers(1,i)) gammaP = gammaP / deltaFirst !Handle ideal absorber pair case
                    
                    gammaP = gammaP + & !Contribution from second (ideal) absorber
                    2 * vGrid(secondCell) * vWidths(secondCell) / deltaSecond
                    
                else !Pairs which include points greater than the second ideal absorber
                    firstCell = idealAbsorbers(1,i)
                    secondCell = absorberList(i)%entry(j+1)

                    gammaP = 2 * vGrid(firstCell) & !Contribution from first (ideal) absorber
                                                         * vWidths(firstCell) / deltaFirst

                    gammaP = gammaP + & !Contribution from second (non-ideal) absorber
                    2 * vGrid(secondCell) * vWidths(secondCell)
                end if

                gammaP = gammaP / aListEnergyWidth

                eInterp = (eGrid(i) - eGrid(firstCell) - E)/(eGrid(secondCell) - eGrid(firstCell))
                denseWMat(firstCell,i) = denseWMat(firstCell,i) & !Add contribution of first cell in this pair to total matrix
                + gammaP * eGrid(i)*vWidths(i) * (real(1.0d0,kind=rk) - eInterp) / (eGrid(firstCell)*vWidths(firstCell))

                denseWMat(secondCell,i) = denseWMat(secondCell,i) & !Add contribution of second cell in this pair to total matrix
                + gammaP * eGrid(i)*vWidths(i) * eInterp / (eGrid(secondCell)*vWidths(secondCell))
            end do
        end if
    end do

    call wMat%init()

    !Add rows to sparse matrix
    do i = 1, numV 
        if (any(wMap(i,:))) then 
            call wMat%addRow(i,findIndices(wMap(i,:)),denseWMat(i,findIndices(wMap(i,:))))
        end if
    end do

end function inelWeights
!-----------------------------------------------------------------------------------------------------------------------------------
!> Fills passed velocity space vector emit with zeros or ones, depending on whether there are any entries in the corresponding 
!> column of passed mapping weight matrix
!-----------------------------------------------------------------------------------------------------------------------------------
pure module subroutine fillEmissionVector(wMat,emit) 

    type(sparseRowData)    ,intent(in) :: wMat
    real(rk) ,dimension(:) ,intent(inout) :: emit

    integer(ik) :: i ,j

    if (assertions) call assertPure(wMat%isDefined(),"Undefined sparse matrix passed to fillEmissionVector")

    emit = 0
    do i = 1,size(emit)
        do j = 1,size(wMat%rowIndex)
            if (any(wMat%columnVector(j)%entry == i)) then 
                emit(i) = real(1,kind=rk) 
                exit
            end if
        end do
    end do

end subroutine fillEmissionVector
!-----------------------------------------------------------------------------------------------------------------------------------
end submodule inelastic_mapping_procedures
!-----------------------------------------------------------------------------------------------------------------------------------