db_transition_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 (db_transition_class) db_transition_procedures
!! author: Stefan Mijin 
!! 
!! Contains module procedures associated with the fixed energy detailed balance transition class

implicit none

!-----------------------------------------------------------------------------------------------------------------------------------
contains
!-----------------------------------------------------------------------------------------------------------------------------------
!-----------------------------------------------------------------------------------------------------------------------------------
pure module subroutine initDBTransition(this,locNumX,inStates,outStates,energy,distVarIndex,refVSpace, &
    directTransitionIndex, fixedWIndexDirect,fixedWIndex,&
    temperatureVarIndex, maxCSl,degeneracyRatio,&
    degeneracyFun,degeneracyFunIndices, momentumMoment ,l1Index ,csUpdatePriority,strictDB) 
    !! Initialization routine for DBTransition object

    class(DBTransition)                 ,intent(inout)  :: this
    integer(ik)                         ,intent(in)     :: locNumX !! Local number of spatial cells
    integer(ik)  ,dimension(:)          ,intent(in)     :: inStates !! Pre-transition states
    integer(ik)  ,dimension(:)          ,intent(in)     :: outStates !! Post-transition states
    real(rk)                            ,intent(in)     :: energy !! Transition energy
    integer(ik)                         ,intent(in)     :: distVarIndex !! Distribution function variable index
    type(VSpace) ,target                ,intent(inout)  :: refVSpace !! Target for the reference pointer
    integer(ik)                         ,intent(in)     :: directTransitionIndex !! Index of the direct transition in the host model data
    integer(ik)                         ,intent(in)     :: fixedWIndexDirect !! Index of the direct transition inelastic weight matrix in the host model inelastic data object
    integer(ik)                         ,intent(in)     :: fixedWIndex !! Index of this transition's inelastic weight matrix in the host model inelastic data object
    integer(ik)                         ,intent(in)     :: temperatureVarIndex !! Index of the temperature variable used to calculate the detailed balance cross-section
    integer(ik)                         ,intent(in)     :: maxCSl !! Highest harmonic of the cross-section to calculate
    real(rk)                            ,intent(in)     :: degeneracyRatio !! Ratio of the degeneracy of the initial and final states of the transition
    class(Derivation) ,optional         ,intent(in)     :: degeneracyFun !! Optional derivation object when the degeneracy is a function of variables in the variable container
    integer(ik) ,dimension(:) ,optional ,intent(in)     :: degeneracyFunIndices !! Variable indices needed for the degeneracy function calculation 
    logical ,optional                   ,intent(in)     :: momentumMoment !! Set to true if the momentum rate should be calculated
    integer(ik) ,optional               ,intent(in)     :: l1Index !! Index of the l=1 harmonic - must be provided if calculating momentum rate
    integer(ik) ,optional               ,intent(in)     :: csUpdatePriority !! Update priority for cross-section data. Defaults to highest priority (0)
    logical ,optional                   ,intent(in)     :: strictDB !! Set to false if strict detailed balance should not be enforced by scaling cross-sections. Defaults to true.

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

    if (assertions .or. assertionLvl >= 0) then 
        call assertPure(refVSpace%isDefined(),&
        "Undefined target VSpace object passed to fixed energy detailed balance transition constructor")
        if (present(momentumMoment)) then 
            if (momentumMoment) call assertPure(present(l1Index),"When momentumMoment is set to true in DBTransition &
            &cosntructor the l1Index must be provided")
        end if
        call assertPure(present(degeneracyFun) .eqv. present(degeneracyFunIndices),"If a degeneracy function derivation object &
        &is passed to the DBTransition so must the corresponding variable indices and vice versa")
    end if

    this%locNumX = locNumX 
    allocate(rateVec(locNumX))
    rateVec = 0 
    call this%setRate(rateVec)
    this%takeMomentumMoment = .false.
    this%distFunVarIndex = distVarIndex
    this%transitionEnergy = energy 
    if (present(momentumMoment)) then 
        if (momentumMoment) call this%setRateMomentum(rateVec)
        this%takeMomentumMoment = .true. 
        this%l1HarmonicInd = l1Index
    end if 

    this%directTransitionIndex = directTransitionIndex
    this%fixedWIndexDirect = fixedWIndexDirect 
    this%temperatureVarIndex = temperatureVarIndex
    this%maxCSl = maxCSl
    call this%setStates(inStates,outStates)

    this%vSpaceRef => refVSpace 

    this%degeneracyRatio = degeneracyRatio 
    if (present(degeneracyFun)) then 
        allocate(this%degeneracyFun,source=degeneracyFun)
        this%degeneracyFunIndices = degeneracyFunIndices 
    end if

    this%csUpdatePriority = 0

    if (present(csUpdatePriority)) this%csUpdatePriority = csUpdatePriority

    call this%setCSDim(2)

    call this%setIncludeElectronDensity(.true.)

    this%fixedWIndex = fixedWIndex 

    this%strictDB = .true.

    if (present(strictDB)) this%strictDB = strictDB

    call this%makeDefined()

end subroutine initDBTransition
!-----------------------------------------------------------------------------------------------------------------------------------
pure module function getEnergy(this) result(energyCost)
    !! Returns array representing energy cost of this transition 

    class(DBTransition)                ,intent(in) :: this 
    real(rk) ,allocatable ,dimension(:)            :: energyCost

    if (assertions) call assertPure(this%isDefined(),"getEnergy called on undefined DBTransition object")

    allocate(energyCost(1))
    energyCost = this%transitionEnergy

end function getEnergy
!-----------------------------------------------------------------------------------------------------------------------------------
module subroutine updateCSRates(this,varCont,hostModel,hostData,updatePriority)
    !! Update cross-section and transition and moment exchange rates (if applicable)

    class(DBTransition)             ,intent(inout)  :: this
    type(VariableContainer)         ,intent(in)     :: varCont !! Variable container used in update
    class(ModelSurrogate) ,optional ,intent(in)     :: hostModel !! Host model - unused
    class(ModelboundData) ,optional ,intent(in)     :: hostData !! Host data used to access direct transition data 
    integer(ik) ,optional           ,intent(in)     :: updatePriority !! Priority for this update call used to determine if cross-sections should be updated

    real(rk) ,allocatable ,dimension(:)   :: rateBuffer ,emissionVec ,dbCorrection
    real(rk) ,allocatable ,dimension(:)   :: directCrossSection , tempVar ,degenFun ,vGrid ,correctedCS
    real(rk) ,allocatable ,dimension(:,:) :: thisCrossSection 

    integer(ik) :: i, j ,l ,csIndex ,usedPriority

    if (assertions) then 
        call assert(this%isDefined(),"Attempted to update undefined DBTransition object")
        call assert(varCont%isDefined(),"Attempted to update DBTransition object using undefined variable container")
    end if

    ! Calculate cross-section harmonic using detailed balance and direct process data (see equation 4.41 in Mijin thesis)

    usedPriority = this%csUpdatePriority
    if (present(updatePriority)) usedPriority = updatePriority
    if (usedPriority >= this%csUpdatePriority) then 

        allocate(tempVar,source = varCont%variables(this%temperatureVarIndex)%entry)
        allocate(degenFun,mold=tempVar)
        degenFun = real(1,kind=rk)
        if (allocated(this%degeneracyFun)) degenFun = this%degeneracyFun%calculate(varCont%variables,this%degeneracyFunIndices)
        vGrid = this%VSpaceRef%getVGrid()
        select type (hostData)
        type is (ModelboundCRMData)
                    emissionVec = hostData%getFixedEmissionVector(this%fixedWIndex)
                class default 
                    error stop "incompatible modelbound data passed to DBTransition update routine"
        end select

        if (.not. allocated(this%directFixedW)) then 

            select type (hostData)
                type is (ModelboundCRMData)
                    allocate(this%directFixedW)
                    this%directFixedW = hostData%getFixedW(this%fixedWIndexDirect) ! Fixed weights of direct process
                class default 
                    error stop "incompatible modelbound data passed to DBTransition update routine"
            end select

            allocate(this%discreteEnergyErrors(size(this%directFixedW%rowIndex)))
            do j = 1,size(this%directFixedW%rowIndex)
                this%discreteEnergyErrors(j)%entry=vGrid(this%directFixedW%columnVector(j)%entry)**2&
                -vGrid(this%directFixedW%rowIndex(j))**2+this%transitionEnergy
            end do
        end if

        do l = 1, this%maxCSl+1

            select type (hostData)
            type is (ModelboundCRMData)
                directCrossSection = hostData%getTransitionCrossSection(l,this%directTransitionIndex)
            class default 
                error stop "incompatible modelbound data passed to DBTransition update routine"
            end select

            if (.not. allocated(thisCrossSection)) then 
                allocate(thisCrossSection(this%locNumX*size(directCrossSection),1+this%maxCSl))
            end if
            thisCrossSection = 0

            do i = 1, this%locNumX
                do j = 1,size(this%directFixedW%rowIndex)
                    csIndex = (i-1)*size(vGrid) + this%directFixedW%rowIndex(j)
                    thisCrossSection(csIndex,l) = dot_product(this%directFixedW%values(j)%entry,&
                    vGrid(this%directFixedW%columnVector(j)%entry)*directCrossSection(this%directFixedW%columnVector(j)%entry)&
                    *exp(-(this%discreteEnergyErrors(j)%entry)/tempVar(i)))
                end do
                thisCrossSection((i-1)*size(vGrid)+1:i*size(vGrid),l) = &
                thisCrossSection((i-1)*size(vGrid)+1:i*size(vGrid),l) * this%degeneracyRatio &
                * degenFun(i)  /vGrid 
            end do

        end do

        !Handle error in detailed balance due to some cells potentially not emitting by rescaling the cross-section locally
        correctedCS = thisCrossSection(:,1)
        do i = 1, this%locNumX
            correctedCS((i-1)*size(vGrid)+1:i*size(vGrid)) = &
            correctedCS((i-1)*size(vGrid)+1:i*size(vGrid)) * emissionVec
        end do  

        dbCorrection = this%vSpaceRef%calculateMoment(varCont%variables(this%distFunVarIndex)%entry,1,1,&
            thisCrossSection(:,1),.true.)&
            /this%vSpaceRef%calculateMoment(varCont%variables(this%distFunVarIndex)%entry,1,1,&
            correctedCS,.true.)
        
        if (.not. this%strictDB) dbCorrection = real(1,kind=rk)
        do l = 1, this%maxCSl+1

            do i = 1, this%locNumX
                thisCrossSection((i-1)*size(vGrid)+1:i*size(vGrid),l) = &
                thisCrossSection((i-1)*size(vGrid)+1:i*size(vGrid),l) * dbCorrection(i) * emissionVec
            end do
        end do
        call this%setCrossSection(thisCrossSection)

    end if
    
    rateBuffer = this%vSpaceRef%calculateMoment(varCont%variables(this%distFunVarIndex)%entry,1,1,&
                                                this%getCrossSectionCol(1),.true.)
    call this%setRate(rateBuffer)
    call this%setRateEnergy(rateBuffer*this%transitionEnergy) 

    if (this%takeMomentumMoment) then

        rateBuffer = this%vSpaceRef%calculateMoment(varCont%variables(this%distFunVarIndex)%entry,&
                                                    this%l1HarmonicInd,2,this%getCrossSectionCol(2),.true.)

        call this%setRateMomentum(rateBuffer)
        
    end if

end subroutine updateCSRates 
!-----------------------------------------------------------------------------------------------------------------------------------
elemental module subroutine finalizeDBTransition(this) 
    !! Deallocate pointer component

    type(DBTransition)                    ,intent(inout) :: this

    nullify(this%vSpaceRef)

end subroutine finalizeDBTransition 
!-----------------------------------------------------------------------------------------------------------------------------------
end submodule db_transition_procedures
!-----------------------------------------------------------------------------------------------------------------------------------