kinetic_stencil_templates_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 (kinetic_stencil_templates) kinetic_stencil_templates_procedures
!! author: Stefan Mijin 
!! 
!! Contains kinetic stencil template procedures

implicit none

!-----------------------------------------------------------------------------------------------------------------------------------
contains
!-----------------------------------------------------------------------------------------------------------------------------------
module subroutine initKineticStencilTemplate(stencilTemplateObj,envObj,jsonPrefix,evolvedVar,implicitVar,mbData)
    !! Initialize kineti stencil template based on existing model, environment object, and JSON file

    type(StencilTemplate)            ,intent(inout) :: stencilTemplateObj
    type(EnvironmentWrapper)         ,intent(inout) :: envObj
    character(*)                     ,intent(in)    :: jsonPrefix !! prefix for JSON keys for this template
    character(*)                     ,intent(in)    :: evolvedVar
    character(*)                     ,intent(in)    :: implicitVar
    class(ModelboundData) ,optional ,intent(in)     :: mbData


    type(NamedString) ,dimension(1) :: stencilType 

    stencilType(1) = NamedString(jsonPrefix//"."//keyStencilData//"."//keyStencilType,"")
    call envObj%jsonCont%load(stencilType)
    call envObj%jsonCont%output(stencilType)

    select case(stencilType(1)%value)
    case (keyDiagonal)
        call initKinDiagonalStencilTemplate(stencilTemplateObj,envObj,jsonPrefix,evolvedVar,implicitVar)
    case (keyMomentStencil)
        call initMomentStencilTemplate(stencilTemplateObj,envObj,jsonPrefix,evolvedVar,implicitVar)
    case (keyKinSpatialDiffStencil)
        call initSpatialDiffStencilTemplate(stencilTemplateObj,envObj,jsonPrefix,evolvedVar,implicitVar)
    case (keyDDVStencil)
        call initDDVStencilTemplate(stencilTemplateObj,envObj,jsonPrefix,evolvedVar,implicitVar)
    case (keyVDiffStencil)
        call initVelDiffusionStencilTemplate(stencilTemplateObj,envObj,jsonPrefix,evolvedVar,implicitVar)
    case (keyShkarofskyIJ)
        call initIJStencilTemplate(stencilTemplateObj,envObj,jsonPrefix,evolvedVar,implicitVar)
    case (keyFixedBoltzmann)
        call assert(present(mbData),"Modelbound data required for "//keyFixedBoltzmann)

        select type(mbData)
        type is (ModelboundCRMData)
            call initFixedBoltzmannStencil(stencilTemplateObj,envObj,jsonPrefix,evolvedVar,implicitVar,mbData)
        class default
            error stop "Unexpected modelbound data type detected for fixed Boltzmann stencil template"
        end select
    case (keyVariableBoltzmann)
        call assert(present(mbData),"Modelbound data required for "//keyVariableBoltzmann)

        select type(mbData)
        type is (ModelboundCRMData)
            call initVariableBoltzmannStencil(stencilTemplateObj,envObj,jsonPrefix,evolvedVar,implicitVar,mbData)
        class default
            error stop "Unexpected modelbound data type detected for fixed Boltzmann stencil template"
        end select

    case (keyScalingLBC)
        call initScalingLBCStencil(stencilTemplateObj,envObj,jsonPrefix,evolvedVar,implicitVar)
    case (keyTermMomentStencil)
        call initTermMomentStencil(stencilTemplateObj,envObj,jsonPrefix,evolvedVar,implicitVar)
    case default
        error stop "Unsupported stencil type detected by initKineticStencilTemplate"
    end select

end subroutine initKineticStencilTemplate
!-----------------------------------------------------------------------------------------------------------------------------------
module subroutine initKinDiagonalStencilTemplate(stencilTemplateObj,envObj,jsonPrefix,evolvedVar,implicitVar)
    !! Initialize diagonal stencil template based on environment object and JSON file

    type(StencilTemplate)      ,intent(inout) :: stencilTemplateObj
    type(EnvironmentWrapper)   ,intent(inout) :: envObj
    character(*)               ,intent(in)    :: jsonPrefix !! prefix for JSON keys for this template
    character(*)               ,intent(in)    :: evolvedVar
    character(*)               ,intent(in)    :: implicitVar

    type(NamedIntegerArray) ,dimension(1) :: evolvedHarmonics,evolvedVelCells,evolvedXCells

    integer(ik) :: i

 
    evolvedXCells(1)%name = jsonPrefix//"."//keyStencilData//"."//keyEvolvedXCells
    allocate(evolvedXCells(1)%values(0))
    evolvedHarmonics(1) = NamedIntegerArray(jsonPrefix//"."//keyStencilData//"."//keyEvolvedHarmonics,&
                                            [(i,i=1,envObj%gridObj%getNumH())])
    evolvedVelCells(1) = NamedIntegerArray(jsonPrefix//"."//keyStencilData//"."//keyEvolvedVCells,&
                                            [(i,i=1,envObj%gridObj%getNumV())])

    call envObj%jsonCont%load(evolvedXCells)
    call envObj%jsonCont%output(evolvedXCells)
    call envObj%jsonCont%load(evolvedHarmonics)
    call envObj%jsonCont%output(evolvedHarmonics)
    call envObj%jsonCont%load(evolvedVelCells)
    call envObj%jsonCont%output(evolvedVelCells)

    call initKinDiagonalStencilTemplateDirect(stencilTemplateObj,envObj,evolvedVar,implicitVar,&
                                              evolvedXCells(1)%values,evolvedHarmonics(1)%values,evolvedVelCells(1)%values)

end subroutine initKinDiagonalStencilTemplate
!-----------------------------------------------------------------------------------------------------------------------------------
module subroutine initKinDiagonalStencilTemplateDirect(stencilTemplateObj,envObj,evolvedVar,implicitVar,&
                                                       evolvedXCells,evolvedHarmonics,evolvedVCells)
    !! Initialize diagonal stencil template based on environment object and JSON file

    type(StencilTemplate)      ,intent(inout) :: stencilTemplateObj
    type(EnvironmentWrapper)   ,intent(inout) :: envObj
    character(*)               ,intent(in)    :: evolvedVar
    character(*)               ,intent(in)    :: implicitVar
    integer(ik) ,dimension(:)  ,intent(in)    :: evolvedXCells
    integer(ik) ,dimension(:)  ,intent(in)    :: evolvedHarmonics
    integer(ik) ,dimension(:)  ,intent(in)    :: evolvedVCells

    type(InterpStencilGenerator) :: interpStencilGen

    integer(ik) :: i
    logical     :: pGrid, staggeredRowVar ,staggeredColVar , interpolationRequired

    logical ,allocatable ,dimension(:) :: oddL

    integer(ik) ,allocatable ,dimension(:) :: lGrid ,validXCoords ,usedCoords ,usedHCoords, usedVCoords

    type(MultiplicativeGeneratorCore) ,allocatable :: genCore 
    type(MultiplicativeStencilGen) :: multStencilGen

    if (assertions .or. assertionLvl >= 0) call assert(envObj%externalVars%isVarDist(envObj%externalVars%getVarIndex(evolvedVar)),&
                                "initKinDiagonalStencilTemplateDirect must be invoked with evolvedVar being a distribution")

    staggeredRowVar = envObj%externalVars%isVarOnDualGrid(evolvedVar)
    staggeredColVar = envObj%externalVars%isVarOnDualGrid(implicitVar)

    if (assertions .or. assertionLvl >= 0) then
        if (.not. staggeredRowVar) call assert(.not. staggeredColVar,"If the evolved distribution in &
        &initKinDiagonalStencilTemplateDirect is not staggered the column variable cannot be staggered either")
    end if

    lGrid = envObj%gridObj%getLGrid()
    
    oddL = mod(lGrid(evolvedHarmonics),2) == 1

    if (staggeredRowVar) then 
        call assert(all(oddL) .or. all(.not. oddL),"If the evolved distribution is staggered then the evolved harmonics in &
        &initKinDiagonalStencilTemplate must either all have odd l or all have even l")
    end if

    pGrid = envObj%geometryObj%isPeriodic()

    validXCoords = [(i,i=1,envObj%gridObj%getNumX())]
    if (staggeredRowVar .and. (.not. pGrid) .and. all(oddL)) &
    validXCoords = [(i,i=1,envObj%gridObj%getNumX()-1)]

    if (size(evolvedXCells)>0) then 
        usedCoords = evolvedXCells
    else
        usedCoords = validXCoords
    end if 

    usedHCoords = [(i,i=1,envObj%gridObj%getNumH())]
    if (size(evolvedHarmonics)>0) usedHCoords = evolvedHarmonics

    usedVCoords = [(i,i=1,envObj%gridObj%getNumV())]
    if (size(evolvedVCells)>0) usedVCoords = evolvedVCells   

    if (assertions .or. assertionLvl >= 0) then 
        do i = 1,size(evolvedXCells)
            call assert(any(validXCoords == usedCoords(i)),&
            "Disallowed value detected in evolvedXCells in initDiagonalStencilTemplateDirect")
        end do
    end if

    stencilTemplateObj%fixedStencil = .true.
    stencilTemplateObj%rowCoords = allCombinations([IntArray(usedCoords),&
                                                   IntArray(usedHCoords),&
                                                   IntArray(usedVCoords)])

    interpolationRequired = .not. envObj%externalVars%isVarDist(envObj%externalVars%getVarIndex(implicitVar)) &
                            .and. staggeredRowVar .and. (all(.not. oddL) .eqv. staggeredColVar)
    if (.not. interpolationRequired) then 

        call stencilTemplateObj%defaultStencil%init(xPeriodic=pGrid,&
             mapToDist=envObj%externalVars%isVarDist(envObj%externalVars%getVarIndex(implicitVar)))

        ! No stencil gen since default multConst is all ones
    else

        if (staggeredColVar) then 

            call stencilTemplateObj%defaultStencil%init(xStencil=[-1,0],xPeriodic=pGrid)
            if (.not. pGrid) then !Handle extrapolation points

                stencilTemplateObj%overridingStencilCoords = allCombinations([IntArray([1,envObj%gridObj%getNumX()]),&
                                                             IntArray(usedHCoords),&
                                                             IntArray(usedVCoords)])
                
                allocate(stencilTemplateObj%overridingStencils(size(stencilTemplateObj%overridingStencilCoords,2)))
                do i = 1, size(stencilTemplateObj%overridingStencils)
                    if (stencilTemplateObj%overridingStencilCoords(1,i) == 1) then
                        call stencilTemplateObj%overridingStencils(i)%init(xStencil=[0,1],xPeriodic=pGrid)
                    else
                        call stencilTemplateObj%overridingStencils(i)%init(xStencil=[-2,-1],xPeriodic=pGrid)

                    end if
                end do

            end if
        else

            call stencilTemplateObj%defaultStencil%init(xStencil=[0,1],xPeriodic=pGrid)

        end if

        call interpStencilGen%init(envObj%partitionObj,envObj%geometryObj,envObj%mpiCont%getWorldRank(),&
                                   staggeredGridMode=staggeredColVar)

        allocate(genCore)
        call genCore%init(envObj%gridObj,envObj%partitionObj,envObj%mpiCont%getWorldRank()&
                         ,stencilTemplateObj%rowCoords,fluidCol=.true.)

        call multStencilGen%init(genCore,fluidCol=.true.)

        call multStencilGen%setXGen(interpStencilGen)

        allocate(stencilTemplateObj%stencilGen,source=multStencilGen)
        
    end if

end subroutine initKinDiagonalStencilTemplateDirect
!-----------------------------------------------------------------------------------------------------------------------------------
module subroutine initMomentStencilTemplate(stencilTemplateObj,envObj,jsonPrefix,evolvedVar,implicitVar)
    !! Initialize distribution moment stencil template based on environment object and JSON file

    type(StencilTemplate)      ,intent(inout) :: stencilTemplateObj
    type(EnvironmentWrapper)   ,intent(inout) :: envObj
    character(*)               ,intent(in)    :: jsonPrefix !! prefix for JSON keys for this template
    character(*)               ,intent(in)    :: evolvedVar
    character(*)               ,intent(in)    :: implicitVar

    type(NamedInteger) ,dimension(1) :: momentHarmonic, momentOrder


    momentHarmonic(1) = NamedInteger(jsonPrefix//"."//keyStencilData//"."//keyMomentHarmonic,0)
    momentOrder(1) = NamedInteger(jsonPrefix//"."//keyStencilData//"."//keyMomentOrder,0)

    call envObj%jsonCont%load(momentHarmonic)
    call envObj%jsonCont%output(momentHarmonic)
    call envObj%jsonCont%load(momentOrder)
    call envObj%jsonCont%output(momentOrder)

    call initMomentStencilTemplateDirect(stencilTemplateObj,envObj,evolvedVar,implicitVar,&
                                         momentHarmonic(1)%value,momentOrder(1)%value)

end subroutine initMomentStencilTemplate
!-----------------------------------------------------------------------------------------------------------------------------------
module subroutine initMomentStencilTemplateDirect(stencilTemplateObj,envObj,evolvedVar,implicitVar,momentHarmonic,momentOrder)
    !! Initialize distribution moment stencil template based on direct input data

    type(StencilTemplate)      ,intent(inout) :: stencilTemplateObj
    type(EnvironmentWrapper)   ,intent(inout) :: envObj
    character(*)               ,intent(in)    :: evolvedVar
    character(*)               ,intent(in)    :: implicitVar
    integer(ik)                ,intent(in)    :: momentHarmonic
    integer(ik)                ,intent(in)    :: momentOrder

    type(InterpStencilGenerator) :: interpStencilGen

    integer(ik) :: i ,usedLocNumX
    logical     :: pGrid, oddL, staggeredRowVar ,staggeredColVar ,interpolationRequired

    integer(ik) ,allocatable ,dimension(:) :: lGrid ,locNumX

    type(MultiplicativeGeneratorCore) ,allocatable :: genCore 
    type(MultiplicativeStencilGen) :: multStencilGen

    if (assertions .or. assertionLvl >= 0) then 
        call assert(envObj%externalVars%isVarDist(envObj%externalVars%getVarIndex(implicitVar)),&
                                "initMomentStencilTemplateDirect must be invoked with implicitVar being a distribution")
        call assert(.not. envObj%externalVars%isVarDist(envObj%externalVars%getVarIndex(evolvedVar)),&
                                "initMomentStencilTemplateDirect must be invoked with evolvedVar being a fluid variable")
    end if

    staggeredRowVar = envObj%externalVars%isVarOnDualGrid(evolvedVar)
    staggeredColVar = envObj%externalVars%isVarOnDualGrid(implicitVar)

    if (assertions .or. assertionLvl >= 0) then
        if (.not. staggeredColVar) call assert(.not. staggeredRowVar,"If the implicit distribution in &
        &initMomentStencilTemplateDirect is not staggered the evolved variable cannot be staggered either")
    end if

    lGrid = envObj%gridObj%getLGrid()

    oddL = mod(lGrid(momentHarmonic),2)==1

    stencilTemplateObj%fixedStencil = .true.
    stencilTemplateObj%rowCoords = allCombinations([IntArray([(i,i=1,envObj%gridObj%getNumX())])])

    pGrid = envObj%geometryObj%isPeriodic()
    if (staggeredRowVar .and. (.not. pGrid)) &
    stencilTemplateObj%rowCoords = allCombinations([IntArray([(i,i=1,envObj%gridObj%getNumX()-1)])]) !Remove last spatial row in staggered non-periodic case

    interpolationRequired = staggeredColVar .and. (oddL .neqv. staggeredRowVar)

    locNumX = envObj%partitionObj%getLocNumX()
    usedLocNumX = locNumX(envObj%mpiCont%getWorldRank()+1)

    if (envObj%partitionObj%getMaxHAtInd(envObj%mpiCont%getWorldRank()+1)==envObj%gridObj%getNumX()) then 
        if (staggeredRowVar .and. (.not. pGrid)) usedLocNumX = usedLocNumX - 1
    end if
    allocate(genCore)
    call genCore%init(envObj%gridObj,envObj%partitionObj,envObj%mpiCont%getWorldRank()&
                         ,stencilTemplateObj%rowCoords,&
                         fixedVVals=4*pi*envObj%gridObj%getVGrid()**(2+momentOrder)*envObj%vSpaceObj%getVCellWidths())

    call multStencilGen%init(genCore,&
                        initXVals=jaggedArray(reshape(real([(1,i=1,usedLocNumX)],kind=rk),[1,usedLocNumX])),&
                        initHVals=jaggedArray(reshape(real([(1,i=1,1)],kind=rk),[1,1])),&
                    initVVals=jaggedArray(reshape(real([(1,i=1,envObj%gridObj%getNumV())],kind=rk),[1,envObj%gridObj%getNumV()])))

    if (interpolationRequired) then 

        if (oddL) then 

            call stencilTemplateObj%defaultStencil%init(xStencil=[-1,0],hStencil=[momentHarmonic],&
                                                       vStencil=[(i,i=1,envObj%gridObj%getNumV())],xPeriodic=pGrid,mapToDist=.true.)
            if (.not. pGrid) then !Handle extrapolation points

                stencilTemplateObj%overridingStencilCoords = allCombinations([IntArray([1,envObj%gridObj%getNumX()])])
                
                allocate(stencilTemplateObj%overridingStencils(size(stencilTemplateObj%overridingStencilCoords,2)))
                do i = 1, size(stencilTemplateObj%overridingStencils)
                    if (stencilTemplateObj%overridingStencilCoords(1,i) == 1) then
                        call stencilTemplateObj%overridingStencils(i)%init(xStencil=[0,1],hStencil=[momentHarmonic],&
                                                    vStencil=[(i,i=1,envObj%gridObj%getNumV())],xPeriodic=pGrid,mapToDist=.true.)
                    else
                        call stencilTemplateObj%overridingStencils(i)%init(xStencil=[-2,-1],hStencil=[momentHarmonic],&
                                                    vStencil=[(i,i=1,envObj%gridObj%getNumV())],xPeriodic=pGrid,mapToDist=.true.)
                    end if
                end do

            end if
        else

            call stencilTemplateObj%defaultStencil%init(xStencil=[0,1],hStencil=[momentHarmonic],&
                                                    vStencil=[(i,i=1,envObj%gridObj%getNumV())],xPeriodic=pGrid,mapToDist=.true.)

        end if

        call interpStencilGen%init(envObj%partitionObj,envObj%geometryObj,envObj%mpiCont%getWorldRank(),&
                                   staggeredGridMode=oddL)

        call multStencilGen%setXGen(interpStencilGen)
        
    else

        call stencilTemplateObj%defaultStencil%init(xStencil=[0],hStencil=[momentHarmonic],&
                                                    vStencil=[(i,i=1,envObj%gridObj%getNumV())],xPeriodic=pGrid,mapToDist=.true.)

    end if

    allocate(stencilTemplateObj%stencilGen,source=multStencilGen)


end subroutine initMomentStencilTemplateDirect
!-----------------------------------------------------------------------------------------------------------------------------------
module subroutine initSpatialDiffStencilTemplate(stencilTemplateObj,envObj,jsonPrefix,evolvedVar,implicitVar)
    !! Initialize d/dx kinetic stencil template based on environment object and JSON file. 

    type(StencilTemplate)      ,intent(inout) :: stencilTemplateObj
    type(EnvironmentWrapper)   ,intent(inout) :: envObj
    character(*)               ,intent(in)    :: jsonPrefix !! prefix for JSON keys for this template
    character(*)               ,intent(in)    :: evolvedVar
    character(*)               ,intent(in)    :: implicitVar

    type(NamedInteger) ,dimension(1) :: rowHarmonic, colHarmonic

    rowHarmonic(1) = NamedInteger(jsonPrefix//"."//keyStencilData//"."//keyRowHarmonic,0)
    colHarmonic(1) = NamedInteger(jsonPrefix//"."//keyStencilData//"."//keyColHarmonic,0)

    call envObj%jsonCont%load(rowHarmonic)
    call envObj%jsonCont%output(rowHarmonic)
    call envObj%jsonCont%load(colHarmonic)
    call envObj%jsonCont%output(colHarmonic)

    call initSpatialDiffStencilTemplateDirect(stencilTemplateObj,envObj,evolvedVar,implicitVar,&
                                              rowHarmonic(1)%value,colHarmonic(1)%value)

end subroutine initSpatialDiffStencilTemplate
!-----------------------------------------------------------------------------------------------------------------------------------
module subroutine initSpatialDiffStencilTemplateDirect(stencilTemplateObj,envObj,evolvedVar,implicitVar,rowHarmonic,colHarmonic)
    !! Initialize d/dx kinetic stencil template based on direct input. 

    type(StencilTemplate)      ,intent(inout) :: stencilTemplateObj
    type(EnvironmentWrapper)   ,intent(inout) :: envObj
    character(*)               ,intent(in)    :: evolvedVar
    character(*)               ,intent(in)    :: implicitVar
    integer(ik)                ,intent(in)    :: rowHarmonic
    integer(ik)                ,intent(in)    :: colHarmonic

    integer(ik) :: i  ,spatialStencilCase
    logical     :: pGrid, oddRowL ,oddColL, staggeredRowVar ,staggeredColVar

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

    real(rk) ,allocatable ,dimension(:) :: dx ,linInterp ,outerJ ,innerJ

    type(MultiplicativeGeneratorCore) ,allocatable :: genCore 
    type(MultiplicativeStencilGen) :: multStencilGen

    type(UWCDiffStencilValGenerator) :: centralDiffGen
    type(FBDiffStencilValGenerator)  :: staggeredDiffGen

    if (assertions .or. assertionLvl >= 0) then 
        call assert(envObj%externalVars%isVarDist(envObj%externalVars%getVarIndex(implicitVar)),&
                                "initSpatialDiffStencilTemplateDirect must be invoked with implicitVar being a distribution")
        call assert(envObj%externalVars%isVarDist(envObj%externalVars%getVarIndex(evolvedVar)),&
                                "initSpatialDiffStencilTemplateDirect must be invoked with evolvedVar being a distributione")
    end if

    staggeredRowVar = envObj%externalVars%isVarOnDualGrid(evolvedVar)
    staggeredColVar = envObj%externalVars%isVarOnDualGrid(implicitVar)

    if (assertions .or. assertionLvl >= 0) call assert(staggeredColVar .eqv. staggeredRowVar,&
    "initSpatialDiffStencilTemplateDirect expects both row and column variables to have the same staggered status")

    lGrid = envObj%gridObj%getLGrid()

    oddRowL = mod(lGrid(rowHarmonic),2)==1
    oddColL = mod(lGrid(colHarmonic),2)==1

    stencilTemplateObj%fixedStencil = .true.
    stencilTemplateObj%rowCoords = allCombinations([IntArray([(i,i=1,envObj%gridObj%getNumX())]),&
                                                    IntArray([rowHarmonic]),&
                                                    IntArray([(i,i=1,envObj%gridObj%getNumV())])])

    pGrid = envObj%geometryObj%isPeriodic()
    if (staggeredRowVar .and. (.not. pGrid) .and. oddRowL) &
    stencilTemplateObj%rowCoords = allCombinations([IntArray([(i,i=1,envObj%gridObj%getNumX()-1)]),&
                                                    IntArray([rowHarmonic]),&
                                                    IntArray([(i,i=1,envObj%gridObj%getNumV())])]) !Remove last spatial row in staggered non-periodic case

    allocate(linInterp,source=envObj%geometryObj%getLinInterp(dualGrid=staggeredRowVar))
    dx = envObj%geometryObj%getCellWidths(dualGrid=staggeredRowVar .and. oddRowL,extendedBoundaryCells=.false.)
    
    !Ignore Jacobian data by default
    outerJ = 1/dx
    innerJ = [(real(1,kind=rk),i=1,size(outerJ))]

    allocate(genCore)
    call genCore%init(envObj%gridObj,envObj%partitionObj,envObj%mpiCont%getWorldRank()&
                         ,stencilTemplateObj%rowCoords)

    call multStencilGen%init(genCore,&
                        initHVals=jaggedArray(reshape(real([(1,i=1,1)],kind=rk),[1,1])),&
                    initVVals=jaggedArray(reshape(real([(1,i=1,envObj%gridObj%getNumV())],kind=rk),[1,envObj%gridObj%getNumV()])))

    spatialStencilCase = 0 !Central differencing 

    if (staggeredRowVar .and. (oddColL .neqv. oddRowL)) then 
        spatialStencilCase = 1 !Forward diff
        if (oddColL) spatialStencilCase = 2 !Backwards differencing
    end if

    select case (spatialStencilCase) 

    case (0)
        call stencilTemplateObj%defaultStencil%init(xStencil = [-1,0,1],hStencil=[colHarmonic-rowHarmonic]&
                                                   ,xPeriodic=pGrid,mapToDist=.true.)

        call centralDiffGen%init(envObj%partitionObj,envObj%mpiCont%getWorldRank(),innerJ,outerJ,linInterp,xPeriodic=pGrid,&
                                                   staggeredGridMode=staggeredRowVar) 

        call multStencilGen%setXGen(centralDiffGen)
        
    case (1)
        call stencilTemplateObj%defaultStencil%init(xStencil = [0,1],hStencil=[colHarmonic-rowHarmonic]&
                                                   ,xPeriodic=pGrid,mapToDist=.true.)

        call staggeredDiffGen%init(envObj%partitionObj,envObj%mpiCont%getWorldRank(),innerJ,outerJ,&
                             xPeriodic=pGrid,staggeredGridMode=.true.) 

        call multStencilGen%setXGen(staggeredDiffGen)

    case (2)
        call stencilTemplateObj%defaultStencil%init(xStencil = [-1,0],hStencil=[colHarmonic-rowHarmonic]&
                                                   ,xPeriodic=pGrid,mapToDist=.true.)

        call staggeredDiffGen%init(envObj%partitionObj,envObj%mpiCont%getWorldRank(),innerJ,outerJ,&
                                  xPeriodic=pGrid,staggeredGridMode=.true.,backwardsDiff=.true.) 

        call multStencilGen%setXGen(staggeredDiffGen)

    end select

    allocate(stencilTemplateObj%stencilGen,source=multStencilGen)

end subroutine initSpatialDiffStencilTemplateDirect
!-----------------------------------------------------------------------------------------------------------------------------------
module subroutine initDDVStencilTemplate(stencilTemplateObj,envObj,jsonPrefix,evolvedVar,implicitVar)
    !! Initialize d/dv kinetic stencil template based on environment object and JSON file. 

    type(StencilTemplate)      ,intent(inout) :: stencilTemplateObj
    type(EnvironmentWrapper)   ,intent(inout) :: envObj
    character(*)               ,intent(in)    :: jsonPrefix !! prefix for JSON keys for this template
    character(*)               ,intent(in)    :: evolvedVar
    character(*)               ,intent(in)    :: implicitVar

    type(NamedRealArray) ,dimension(1) :: fixedC, fixedInterp ,cfAtZero 
    type(NamedString) ,dimension(1) :: modelboundC ,modelboundInterp
    type(NamedInteger) ,dimension(1) :: rowHarmonic, colHarmonic

    integer(ik) :: i

    fixedC(1) = NamedRealArray(jsonPrefix//"."//keyStencilData//"."//keyFixedC,[(real(1,kind=rk),i=1,envObj%gridObj%getNumV())])
    fixedInterp(1) = NamedRealArray(jsonPrefix//"."//keyStencilData//"."//keyFixedInterp,envObj%vSpaceObj%getVLinInterp())

    cfAtZero(1) = NamedRealArray(jsonPrefix//"."//keyStencilData//"."//keyCFAtZero,real([0,0],kind=rk))

    modelboundC(1) = NamedString(jsonPrefix//"."//keyStencilData//"."//keyModelboundC,keyNone)
    modelboundInterp(1) = NamedString(jsonPrefix//"."//keyStencilData//"."//keyModelboundInterp,keyNone)

    rowHarmonic(1) = NamedInteger(jsonPrefix//"."//keyStencilData//"."//keyRowHarmonic,0)
    colHarmonic(1) = NamedInteger(jsonPrefix//"."//keyStencilData//"."//keyColHarmonic,0)

    call envObj%jsonCont%load(rowHarmonic)
    call envObj%jsonCont%output(rowHarmonic)
    call envObj%jsonCont%load(colHarmonic)
    call envObj%jsonCont%output(colHarmonic)
    call envObj%jsonCont%load(fixedC)
    call envObj%jsonCont%output(fixedC)
    call envObj%jsonCont%load(fixedInterp)
    call envObj%jsonCont%output(fixedInterp)
    call envObj%jsonCont%load(cfAtZero)
    call envObj%jsonCont%output(cfAtZero)
    call envObj%jsonCont%load(modelboundC)
    call envObj%jsonCont%output(modelboundC)
    call envObj%jsonCont%load(modelboundInterp)
    call envObj%jsonCont%output(modelboundInterp)

    if (modelboundC(1)%value /= keyNone) then 

        if (modelboundInterp(1)%value /= keyNone) then
            call initDDVStencilTemplateDirect(stencilTemplateObj,envObj,evolvedVar,implicitVar,&
                                              rowHarmonic(1)%value,colHarmonic(1)%value,fixedC=fixedC(1)%values,&
                                              fixedInterp=fixedInterp(1)%values,cfAtZero=cfAtZero(1)%values,&
                                              modelboundC=modelboundC(1)%value,modelboundInterp=modelboundInterp(1)%value)
        else
            call initDDVStencilTemplateDirect(stencilTemplateObj,envObj,evolvedVar,implicitVar,&
                                                rowHarmonic(1)%value,colHarmonic(1)%value,fixedC=fixedC(1)%values,&
                                                fixedInterp=fixedInterp(1)%values,cfAtZero=cfAtZero(1)%values,&
                                                modelboundC=modelboundC(1)%value)
        end if 

    else

        if (modelboundInterp(1)%value /= keyNone) then
            call initDDVStencilTemplateDirect(stencilTemplateObj,envObj,evolvedVar,implicitVar,&
                                                rowHarmonic(1)%value,colHarmonic(1)%value,fixedC=fixedC(1)%values,&
                                                fixedInterp=fixedInterp(1)%values,cfAtZero=cfAtZero(1)%values,&
                                                modelboundInterp=modelboundInterp(1)%value)
        else
            call initDDVStencilTemplateDirect(stencilTemplateObj,envObj,evolvedVar,implicitVar,&
                                                rowHarmonic(1)%value,colHarmonic(1)%value,fixedC=fixedC(1)%values,&
                                                fixedInterp=fixedInterp(1)%values,cfAtZero=cfAtZero(1)%values)
        end if 

    end if


end subroutine initDDVStencilTemplate
!-----------------------------------------------------------------------------------------------------------------------------------
module subroutine initDDVStencilTemplateDirect(stencilTemplateObj,envObj,evolvedVar,implicitVar,rowHarmonic,colHarmonic,&
                                              fixedC,fixedInterp,cfAtZero,modelboundC,modelboundInterp)
    !! Initialize d/dv kinetic stencil template based on direct input. 

    type(StencilTemplate)            ,intent(inout) :: stencilTemplateObj
    type(EnvironmentWrapper)         ,intent(inout) :: envObj
    character(*)                     ,intent(in)    :: evolvedVar
    character(*)                     ,intent(in)    :: implicitVar
    integer(ik)                      ,intent(in)    :: rowHarmonic
    integer(ik)                      ,intent(in)    :: colHarmonic
    real(rk) ,optional ,dimension(:) ,intent(in)    :: fixedC
    real(rk) ,optional ,dimension(:) ,intent(in)    :: fixedInterp
    real(rk) ,optional ,dimension(2) ,intent(in)    :: cfAtZero
    character(*) ,optional           ,intent(in)    :: modelboundC
    character(*) ,optional           ,intent(in)    :: modelboundInterp

    integer(ik) :: i  
    logical     :: pGrid, oddRowL ,oddColL, staggeredRowVar ,staggeredColVar ,interpolationRequired

    integer(ik) ,allocatable ,dimension(:) :: lGrid ,activeXGrid

    real(rk) ,allocatable ,dimension(:) :: usedFixedC ,usedFixedInterp 
    real(rk) ,dimension(2)              :: usedCFAtZero

    type(InterpStencilGenerator) :: interpStencilGen
    type(DDVStencilGenerator) :: ddvStencilGen
    type(MultiplicativeGeneratorCore) ,allocatable :: genCore 
    type(MultiplicativeStencilGen) :: multStencilGen

    if (assertions .or. assertionLvl >= 0) then 
        call assert(envObj%externalVars%isVarDist(envObj%externalVars%getVarIndex(implicitVar)),&
                                "initDDVStencilTemplateDirect must be invoked with implicitVar being a distribution")
        call assert(envObj%externalVars%isVarDist(envObj%externalVars%getVarIndex(evolvedVar)),&
                                "initDDVStencilTemplateDirect must be invoked with evolvedVar being a distributione")
    end if

    staggeredRowVar = envObj%externalVars%isVarOnDualGrid(evolvedVar)
    staggeredColVar = envObj%externalVars%isVarOnDualGrid(implicitVar)

    if (assertions .or. assertionLvl >= 0) call assert(staggeredColVar .eqv. staggeredRowVar,&
    "initDDVStencilTemplateDirect expects both row and column variables to have the same staggered status")

    if (present(fixedC)) then 
        usedFixedC = fixedC
    else
        usedFixedC = [(real(1,kind=rk),i=1,envObj%gridObj%getNumV())]
    end if

    if (present(fixedInterp)) then 
        usedFixedInterp = fixedInterp
    else
        usedFixedInterp = envObj%vSpaceObj%getVLinInterp()
    end if

    if (present(cfAtZero)) then 
        usedCFAtZero = cfAtZero
    else
        usedCFAtZero = real([0,0],kind=rk)
    end if

    if (assertions .or. assertionLvl >= 0) then 

        call assert(size(usedFixedC)==envObj%gridObj%getNumV(),&
                    "fixedC passed to initDDVStencilTemplateDirect does not conform with velocity grid size")
        call assert(size(usedFixedInterp)==envObj%gridObj%getNumV(),&
                    "fixedInterp passed to initDDVStencilTemplateDircet does not conform with velocity grid size")

        call assert(size(usedCFAtZero)==2,&
                    "cfAtZero passed to initDDVStencilTemplateDirect should have length 2")

    end if

    pGrid = envObj%geometryObj%isPeriodic()

    lGrid = envObj%gridObj%getLGrid()

    oddRowL = mod(lGrid(rowHarmonic),2)==1
    oddColL = mod(lGrid(colHarmonic),2)==1

    stencilTemplateObj%fixedStencil = .true.

    if (present(modelboundC) .or. present(modelboundInterp)) stencilTemplateObj%fixedStencil = .false.

    interpolationRequired = staggeredRowVar .and. (oddColL .neqv. oddRowL)

    activeXGrid = [(i,i=1,envObj%gridObj%getNumX())]

    if (staggeredRowVar .and. (.not. pGrid) .and. oddRowL) &
    activeXGrid = [(i,i=1,envObj%gridObj%getNumX()-1)]

    stencilTemplateObj%rowCoords = allCombinations([IntArray(activeXGrid),&
                                                    IntArray([rowHarmonic]),&
                                                    IntArray([(i,i=1,envObj%gridObj%getNumV())])])

    if (present(modelboundC)) then 

        if (present(modelboundInterp)) then
            call ddvStencilGen%init(envObj%partitionObj,envObj%vSpaceObj,envObj%mpiCont%getWorldRank(),activeXGrid,&
                                    fixedC=usedFixedC,fixedInterp=usedFixedInterp,cfAtZero=usedCFAtZero,&
                                    mbC=modelboundC,mbInterp=modelboundInterp)
        else
            call ddvStencilGen%init(envObj%partitionObj,envObj%vSpaceObj,envObj%mpiCont%getWorldRank(),activeXGrid,&
                                    fixedC=usedFixedC,fixedInterp=usedFixedInterp,cfAtZero=usedCFAtZero,&
                                    mbC=modelboundC)
        end if 

    else

        if (present(modelboundInterp)) then
            call ddvStencilGen%init(envObj%partitionObj,envObj%vSpaceObj,envObj%mpiCont%getWorldRank(),activeXGrid,&
                                    fixedC=usedFixedC,fixedInterp=usedFixedInterp,cfAtZero=usedCFAtZero,&
                                    mbInterp=modelboundInterp)
        else
            call ddvStencilGen%init(envObj%partitionObj,envObj%vSpaceObj,envObj%mpiCont%getWorldRank(),activeXGrid,&
            fixedC=usedFixedC,fixedInterp=usedFixedInterp,cfAtZero=usedCFAtZero)
        end if 

    end if

    if (interpolationRequired) then 

        allocate(genCore)
        call genCore%init(envObj%gridObj,envObj%partitionObj,envObj%mpiCont%getWorldRank()&
                            ,stencilTemplateObj%rowCoords,vValsDependOnX=.true.)

        call multStencilGen%init(genCore,&
                            initHVals=jaggedArray(reshape(real([(1,i=1,1)],kind=rk),[1,1])))

        if (oddColL) then 

            call stencilTemplateObj%defaultStencil%init(xStencil=[-1,0],hStencil=[colHarmonic-rowHarmonic]&
                                                       ,vStencil=[-1,0,1],xPeriodic=pGrid,mapToDist=.true.)
            if (.not. pGrid) then !Handle extrapolation points

                stencilTemplateObj%overridingStencilCoords = allCombinations([IntArray([1,envObj%gridObj%getNumX()]),&
                                                                            IntArray([rowHarmonic]),&
                                                                            IntArray([(i,i=1,envObj%gridObj%getNumV())])])
                
                allocate(stencilTemplateObj%overridingStencils(size(stencilTemplateObj%overridingStencilCoords,2)))
                do i = 1, size(stencilTemplateObj%overridingStencils)
                    if (stencilTemplateObj%overridingStencilCoords(1,i) == 1) then
                        call stencilTemplateObj%overridingStencils(i)%init(xStencil=[0,1],&
                                                    hStencil=[colHarmonic-rowHarmonic],&
                                                    vStencil=[-1,0,1],xPeriodic=pGrid,mapToDist=.true.)
                    else
                        call stencilTemplateObj%overridingStencils(i)%init(xStencil=[-2,-1],&
                                                    hStencil=[colHarmonic-rowHarmonic],&
                                                    vStencil=[-1,0,1],xPeriodic=pGrid,mapToDist=.true.)
                    end if
                end do

            end if
        else

            call stencilTemplateObj%defaultStencil%init(xStencil=[0,1],hStencil=[colHarmonic-rowHarmonic],&
                                                        vStencil=[-1,0,1],xPeriodic=pGrid,mapToDist=.true.)

        end if

        call interpStencilGen%init(envObj%partitionObj,envObj%geometryObj,envObj%mpiCont%getWorldRank(),&
                                   staggeredGridMode=oddColL)

        call multStencilGen%setXGen(interpStencilGen)
        call multStencilGen%setVGen(ddvStencilGen)

        allocate(stencilTemplateObj%stencilGen,source=multStencilGen)


    else
        call stencilTemplateObj%defaultStencil%init(hStencil=[colHarmonic-rowHarmonic],&
                                                    vStencil=[-1,0,1],xPeriodic=pGrid,mapToDist=.true.)

        allocate(stencilTemplateObj%stencilGen,source=ddvStencilGen)
        
    end if

end subroutine initDDVStencilTemplateDirect
!-----------------------------------------------------------------------------------------------------------------------------------
module subroutine initVelDiffusionStencilTemplate(stencilTemplateObj,envObj,jsonPrefix,evolvedVar,implicitVar)
    !! Initialize d^2/d^2v kinetic stencil template based on environment object and JSON file. 

    type(StencilTemplate)      ,intent(inout) :: stencilTemplateObj
    type(EnvironmentWrapper)   ,intent(inout) :: envObj
    character(*)               ,intent(in)    :: jsonPrefix !! prefix for JSON keys for this template
    character(*)               ,intent(in)    :: evolvedVar
    character(*)               ,intent(in)    :: implicitVar

    integer(ik) :: i  

    type(NamedRealArray) ,dimension(1) :: fixedA ,adfAtZero 
    type(NamedString) ,dimension(1) :: modelboundA 
    type(NamedInteger) ,dimension(1) :: rowHarmonic, colHarmonic

    fixeda(1) = NamedRealArray(jsonPrefix//"."//keyStencilData//"."//keyFixedA,[(real(1,kind=rk),i=1,envObj%gridObj%getNumV())])

    adfAtZero(1) = NamedRealArray(jsonPrefix//"."//keyStencilData//"."//keyADFAtZero,real([0,0],kind=rk))

    modelboundA(1) = NamedString(jsonPrefix//"."//keyStencilData//"."//keyModelboundA,keyNone)

    rowHarmonic(1) = NamedInteger(jsonPrefix//"."//keyStencilData//"."//keyRowHarmonic,0)
    colHarmonic(1) = NamedInteger(jsonPrefix//"."//keyStencilData//"."//keyColHarmonic,0)

    call envObj%jsonCont%load(rowHarmonic)
    call envObj%jsonCont%output(rowHarmonic)
    call envObj%jsonCont%load(colHarmonic)
    call envObj%jsonCont%output(colHarmonic)
    call envObj%jsonCont%load(fixedA)
    call envObj%jsonCont%output(fixedA)
    call envObj%jsonCont%load(adfAtZero)
    call envObj%jsonCont%output(adfAtZero)
    call envObj%jsonCont%load(modelboundA)
    call envObj%jsonCont%output(modelboundA)

    if (modelboundA(1)%value /= keyNone) then 

        call initVelDiffusionStencilTemplateDirect(stencilTemplateObj,envObj,evolvedVar,implicitVar,rowHarmonic(1)%value,&
                                                   colHarmonic(1)%value,fixedA=fixedA(1)%values,adfAtZero=adfAtZero(1)%values,&
                                                   modelboundA=modelboundA(1)%value)


    else

        call initVelDiffusionStencilTemplateDirect(stencilTemplateObj,envObj,evolvedVar,implicitVar,rowHarmonic(1)%value,&
                                                   colHarmonic(1)%value,fixedA=fixedA(1)%values,adfAtZero=adfAtZero(1)%values)

    end if

end subroutine initVelDiffusionStencilTemplate
!-----------------------------------------------------------------------------------------------------------------------------------
module subroutine initVelDiffusionStencilTemplateDirect(stencilTemplateObj,envObj,evolvedVar,implicitVar,rowHarmonic,colHarmonic,&
                                                        fixedA,adfAtZero,modelboundA)
    !! Initialize d^2/d^2v kinetic stencil template based on direct inputs. 

    type(StencilTemplate)            ,intent(inout) :: stencilTemplateObj
    type(EnvironmentWrapper)         ,intent(inout) :: envObj
    character(*)                     ,intent(in)    :: evolvedVar
    character(*)                     ,intent(in)    :: implicitVar
    integer(ik)                      ,intent(in)    :: rowHarmonic
    integer(ik)                      ,intent(in)    :: colHarmonic
    real(rk) ,optional ,dimension(:) ,intent(in)    :: fixedA
    real(rk) ,optional ,dimension(2) ,intent(in)    :: adfAtZero
    character(*) ,optional           ,intent(in)    :: modelboundA

    integer(ik) :: i  
    logical     :: pGrid, oddRowL ,oddColL, staggeredRowVar ,staggeredColVar ,interpolationRequired

    integer(ik) ,allocatable ,dimension(:) :: lGrid ,activeXGrid

    real(rk) ,allocatable ,dimension(:) :: usedFixedA
    real(rk) ,dimension(2)              :: usedADFAtZero

    type(InterpStencilGenerator) :: interpStencilGen
    type(VDiffStencilGen) :: vdStencilGEn
    type(MultiplicativeGeneratorCore) ,allocatable :: genCore 
    type(MultiplicativeStencilGen) :: multStencilGen

    if (assertions .or. assertionLvl >= 0) then 
        call assert(envObj%externalVars%isVarDist(envObj%externalVars%getVarIndex(implicitVar)),&
                                "initVelDiffusionStencilTemplateDirect must be invoked with implicitVar being a distribution")
        call assert(envObj%externalVars%isVarDist(envObj%externalVars%getVarIndex(evolvedVar)),&
                                "initVelDiffusionStencilTemplateDirect must be invoked with evolvedVar being a distributione")
    end if

    staggeredRowVar = envObj%externalVars%isVarOnDualGrid(evolvedVar)
    staggeredColVar = envObj%externalVars%isVarOnDualGrid(implicitVar)

    if (assertions .or. assertionLvl >= 0) call assert(staggeredColVar .eqv. staggeredRowVar,&
    "initVelDiffusionStencilTemplateDirect expects both row and column variables to have the same staggered status")

    if (present(fixedA)) then 
        usedFixedA = fixedA
    else
        usedFixedA = [(real(1,kind=rk),i=1,envObj%gridObj%getNumV())]
    end if 

    if (present(adfAtZero)) then 
        usedADFAtZero = adfAtZero
    else
        usedADFAtZero = real([0,0],kind=rk)
    end if
    
    if (assertions .or. assertionLvl >= 0) then 

        call assert(size(usedFixedA)==envObj%gridObj%getNumV(),& 
                    "fixedA passed to initVelDiffusionStencilTemplateDirect does not conform with velocity grid size")

        call assert(size(usedADFAtZero)==2,&
                    "adfAtZero passed to initVelDiffusionStencilTemplateDirect should have length 2")

    end if
    pGrid = envObj%geometryObj%isPeriodic()

    lGrid = envObj%gridObj%getLGrid()

    oddRowL = mod(lGrid(rowHarmonic),2)==1
    oddColL = mod(lGrid(colHarmonic),2)==1

    stencilTemplateObj%fixedStencil = .true.

    if (present(modelboundA)) stencilTemplateObj%fixedStencil = .false.

    interpolationRequired = staggeredRowVar .and. (oddColL .neqv. oddRowL)

    activeXGrid = [(i,i=1,envObj%gridObj%getNumX())]

    if (staggeredRowVar .and. (.not. pGrid) .and. oddRowL) &
    activeXGrid = [(i,i=1,envObj%gridObj%getNumX()-1)]

    stencilTemplateObj%rowCoords = allCombinations([IntArray(activeXGrid),&
                                                    IntArray([rowHarmonic]),&
                                                    IntArray([(i,i=1,envObj%gridObj%getNumV())])])

    if (present(modelboundA)) then 


        call vdStencilGEn%init(envObj%partitionObj,envObj%vSpaceObj,envObj%mpiCont%getWorldRank(),activeXGrid,&
                                    fixedA=usedFixedA,adfAtZero=usedADFAtZero,&
                                    mbA=modelboundA)


    else

        call vdStencilGEn%init(envObj%partitionObj,envObj%vSpaceObj,envObj%mpiCont%getWorldRank(),activeXGrid,&
                                    fixedA=usedFixedA,adfAtZero=usedADFAtZero)

    end if

    if (interpolationRequired) then 

        allocate(genCore)
        call genCore%init(envObj%gridObj,envObj%partitionObj,envObj%mpiCont%getWorldRank()&
                            ,stencilTemplateObj%rowCoords,vValsDependOnX=.true.)

        call multStencilGen%init(genCore,&
                            initHVals=jaggedArray(reshape(real([(1,i=1,1)],kind=rk),[1,1])))

        if (oddColL) then 

            call stencilTemplateObj%defaultStencil%init(xStencil=[-1,0],hStencil=[colHarmonic-rowHarmonic]&
                                                        ,vStencil=[-1,0,1],xPeriodic=pGrid,mapToDist=.true.)
            if (.not. pGrid) then !Handle extrapolation points

                stencilTemplateObj%overridingStencilCoords = allCombinations([IntArray([1,envObj%gridObj%getNumX()]),&
                                                                            IntArray([rowHarmonic]),&
                                                                            IntArray([(i,i=1,envObj%gridObj%getNumV())])])
                
                allocate(stencilTemplateObj%overridingStencils(size(stencilTemplateObj%overridingStencilCoords,2)))
                do i = 1, size(stencilTemplateObj%overridingStencils)
                    if (stencilTemplateObj%overridingStencilCoords(1,i) == 1) then
                        call stencilTemplateObj%overridingStencils(i)%init(xStencil=[0,1],&
                                                    hStencil=[colHarmonic-rowHarmonic],&
                                                    vStencil=[-1,0,1],xPeriodic=pGrid,mapToDist=.true.)
                    else
                        call stencilTemplateObj%overridingStencils(i)%init(xStencil=[-2,-1],&
                                                    hStencil=[colHarmonic-rowHarmonic],&
                                                    vStencil=[-1,0,1],xPeriodic=pGrid,mapToDist=.true.)
                    end if
                end do

            end if
        else

            call stencilTemplateObj%defaultStencil%init(xStencil=[0,1],hStencil=[colHarmonic-rowHarmonic],&
                                                        vStencil=[-1,0,1],xPeriodic=pGrid,mapToDist=.true.)

        end if

        call interpStencilGen%init(envObj%partitionObj,envObj%geometryObj,envObj%mpiCont%getWorldRank(),&
                                   staggeredGridMode=oddColL)

        call multStencilGen%setXGen(interpStencilGen)
        call multStencilGen%setVGen(vdStencilGEn)

        allocate(stencilTemplateObj%stencilGen,source=multStencilGen)


    else
        call stencilTemplateObj%defaultStencil%init(hStencil=[colHarmonic-rowHarmonic]&
                                                    ,vStencil=[-1,0,1],xPeriodic=pGrid,mapToDist=.true.)

        allocate(stencilTemplateObj%stencilGen,source=vdStencilGEn)
        
    end if

end subroutine initVelDiffusionStencilTemplateDirect
!-----------------------------------------------------------------------------------------------------------------------------------
module subroutine initIJStencilTemplate(stencilTemplateObj,envObj,jsonPrefix,evolvedVar,implicitVar)
    !! Initialize Shkarofsky I/J integral kinetic stencil template based on environment object and JSON file. 

    type(StencilTemplate)      ,intent(inout) :: stencilTemplateObj
    type(EnvironmentWrapper)   ,intent(inout) :: envObj
    character(*)               ,intent(in)    :: jsonPrefix !! prefix for JSON keys for this template
    character(*)               ,intent(in)    :: evolvedVar
    character(*)               ,intent(in)    :: implicitVar

    type(NamedInteger) ,dimension(1) :: rowHarmonic, colHarmonic ,integralIndex
    type(NamedLogical) ,dimension(1) :: jIntegral

    rowHarmonic(1) = NamedInteger(jsonPrefix//"."//keyStencilData//"."//keyRowHarmonic,0)
    colHarmonic(1) = NamedInteger(jsonPrefix//"."//keyStencilData//"."//keyColHarmonic,0)
    integralIndex(1) = NamedInteger(jsonPrefix//"."//keyStencilData//"."//keyIntegralIndex,0)

    jIntegral(1) = NamedLogical(jsonPrefix//"."//keyStencilData//"."//keyJInt,.false.)

    call envObj%jsonCont%load(rowHarmonic)
    call envObj%jsonCont%output(rowHarmonic)
    call envObj%jsonCont%load(colHarmonic)
    call envObj%jsonCont%output(colHarmonic)
    call envObj%jsonCont%load(integralIndex)
    call envObj%jsonCont%output(integralIndex)
    call envObj%jsonCont%load(jIntegral)
    call envObj%jsonCont%output(jIntegral)

    call initIJStencilTemplateDirect(stencilTemplateObj,envObj,evolvedVar,implicitVar,rowHarmonic(1)%value,colHarmonic(1)%value,&
                                     integralIndex(1)%value,jIntegral(1)%value)

end subroutine initIJStencilTemplate
!-----------------------------------------------------------------------------------------------------------------------------------
module subroutine initIJStencilTemplateDirect(stencilTemplateObj,envObj,evolvedVar,implicitVar,&
                                              rowHarmonic,colHarmonic,integralIndex,jIntegral)
    !! Initialize Shkarofsky I/J integral kinetic stencil template based on environment object and JSON file. 

    type(StencilTemplate)      ,intent(inout) :: stencilTemplateObj
    type(EnvironmentWrapper)   ,intent(inout) :: envObj
    character(*)               ,intent(in)    :: evolvedVar
    character(*)               ,intent(in)    :: implicitVar
    integer(ik)                ,intent(in)    :: rowHarmonic
    integer(ik)                ,intent(in)    :: colHarmonic
    integer(ik)                ,intent(in)    :: integralIndex
    logical                    ,intent(in)    :: jIntegral

    integer(ik) :: i ,usedLocNumX
    logical     :: pGrid, oddRowL ,oddColL, staggeredRowVar ,staggeredColVar ,interpolationRequired

    integer(ik) ,allocatable ,dimension(:) :: lGrid ,activeXGrid ,locNumX

    type(InterpStencilGenerator) :: interpStencilGen
    type(MultiplicativeGeneratorCore) ,allocatable :: genCore 
    type(MultiplicativeStencilGen) :: multStencilGen

    type(SparseRowData) :: stencilMat

    if (assertions .or. assertionLvl >= 0) then 
        call assert(envObj%externalVars%isVarDist(envObj%externalVars%getVarIndex(implicitVar)),&
                                "initIJStencilTemplateDirect must be invoked with implicitVar being a distribution")
        call assert(envObj%externalVars%isVarDist(envObj%externalVars%getVarIndex(evolvedVar)),&
                                "initIJStencilTemplateDirect must be invoked with evolvedVar being a distributione")
    end if

    staggeredRowVar = envObj%externalVars%isVarOnDualGrid(evolvedVar)
    staggeredColVar = envObj%externalVars%isVarOnDualGrid(implicitVar)

    if (assertions .or. assertionLvl >= 0) call assert(staggeredColVar .eqv. staggeredRowVar,&
    "initIJStencilTemplateDirect expects both row and column variables to have the same staggered status")

    pGrid = envObj%geometryObj%isPeriodic()

    lGrid = envObj%gridObj%getLGrid()

    oddRowL = mod(lGrid(rowHarmonic),2)==1
    oddColL = mod(lGrid(colHarmonic),2)==1

    stencilTemplateObj%fixedStencil = .true.

    interpolationRequired = staggeredRowVar .and. (oddColL .neqv. oddRowL)

    activeXGrid = [(i,i=1,envObj%gridObj%getNumX())]

    if (staggeredRowVar .and. (.not. pGrid) .and. oddRowL) &
    activeXGrid = [(i,i=1,envObj%gridObj%getNumX()-1)]

    stencilTemplateObj%rowCoords = allCombinations([IntArray(activeXGrid),&
                                                    IntArray([rowHarmonic]),&
                                                    IntArray([(i,i=1,envObj%gridObj%getNumV())])])

    allocate(genCore)
    call genCore%init(envObj%gridObj,envObj%partitionObj,envObj%mpiCont%getWorldRank()&
                        ,stencilTemplateObj%rowCoords)

    if (jIntegral) then
        stencilMat = envObj%vSpaceObj%getShkarofskyJMat(integralIndex)
    else
        stencilMat = envObj%vSpaceObj%getShkarofskyIMat(integralIndex)
    end if

    locNumX = envObj%partitionObj%getLocNumX()
    usedLocNumX = locNumX(envObj%mpiCont%getWorldRank()+1)

    if (envObj%partitionObj%getMaxHAtInd(envObj%mpiCont%getWorldRank()+1)==envObj%gridObj%getNumX()) then 
        if (staggeredRowVar .and. (.not. pGrid) .and. oddRowL) usedLocNumX = usedLocNumX - 1
    end if

    call multStencilGen%init(genCore,&
                        initXVals=jaggedArray(reshape(real([(1,i=1,usedLocNumX)],kind=rk),[1,usedLocNumX])),&
                        initHVals=jaggedArray(reshape(real([(1,i=1,1)],kind=rk),[1,1])),&
                        initVVals = stencilMat%values)

    if (interpolationRequired) then 

        if (oddColL) then 
            call stencilTemplateObj%defaultStencil%init(xStencil=[-1,0],hStencil=[colHarmonic-rowHarmonic]&
                                                        ,vStencilFixed=stencilMat%columnVector,xPeriodic=pGrid,mapToDist=.true.)
            if (.not. pGrid) then !Handle extrapolation points
                stencilTemplateObj%overridingStencilCoords = allCombinations([IntArray([1,envObj%gridObj%getNumX()]),&
                                                                            IntArray([rowHarmonic]),&
                                                                            IntArray([(i,i=1,envObj%gridObj%getNumV())])])
                
                allocate(stencilTemplateObj%overridingStencils(size(stencilTemplateObj%overridingStencilCoords,2)))
                do i = 1, size(stencilTemplateObj%overridingStencils)
                    if (stencilTemplateObj%overridingStencilCoords(1,i) == 1) then
                        call stencilTemplateObj%overridingStencils(i)%init(xStencil=[0,1],&
                                                    hStencil=[colHarmonic-rowHarmonic],&
                                                    vStencilFixed=stencilMat%columnVector,xPeriodic=pGrid,mapToDist=.true.)
                    else
                        call stencilTemplateObj%overridingStencils(i)%init(xStencil=[-2,-1],&
                                                    hStencil=[colHarmonic-rowHarmonic],&
                                                    vStencilFixed=stencilMat%columnVector,xPeriodic=pGrid,mapToDist=.true.)
                    end if
                end do

            end if
        else

            call stencilTemplateObj%defaultStencil%init(xStencil=[0,1],hStencil=[colHarmonic-rowHarmonic],&
                                                        vStencilFixed=stencilMat%columnVector,xPeriodic=pGrid,mapToDist=.true.)

        end if

        call interpStencilGen%init(envObj%partitionObj,envObj%geometryObj,envObj%mpiCont%getWorldRank(),&
                                   staggeredGridMode=oddColL)

        call multStencilGen%setXGen(interpStencilGen)


    else
        call stencilTemplateObj%defaultStencil%init(hStencil=[colHarmonic-rowHarmonic]&
                                                    ,vStencilFixed=stencilMat%columnVector,xPeriodic=pGrid,mapToDist=.true.)
        
    end if

    allocate(stencilTemplateObj%stencilGen,source=multStencilGen)


end subroutine initIJStencilTemplateDirect
!-----------------------------------------------------------------------------------------------------------------------------------
module subroutine initFixedBoltzmannStencil(stencilTemplateObj,envObj,jsonPrefix,evolvedVar,implicitVar,mbData)
    !! Initialize fixed mapping Boltzmann kinetic stencil template based on environment object and JSON file. 

    type(StencilTemplate)      ,intent(inout) :: stencilTemplateObj
    type(EnvironmentWrapper)   ,intent(inout) :: envObj
    character(*)               ,intent(in)    :: jsonPrefix !! prefix for JSON keys for this template
    character(*)               ,intent(in)    :: evolvedVar
    character(*)               ,intent(in)    :: implicitVar
    class(ModelboundCRMData)   ,intent(in)    :: mbData

    type(NamedInteger) ,dimension(1) :: rowHarmonic ,transitionIndex, fixedEnergyIndex
    type(NamedLogical) ,dimension(1) :: absorptionTerm ,dbTerm

    rowHarmonic(1) = NamedInteger(jsonPrefix//"."//keyStencilData//"."//keyRowHarmonic,0)
    transitionIndex(1) = NamedInteger(jsonPrefix//"."//keyStencilData//"."//keyTransitionIndex,0)
    fixedEnergyIndex(1) = NamedInteger(jsonPrefix//"."//keyStencilData//"."//keyFixedEnergyIndex,0)

    absorptionTerm(1) = NamedLogical(jsonPrefix//"."//keyStencilData//"."//keyAbsorptionTerm,.false.)
    dbTerm(1) = NamedLogical(jsonPrefix//"."//keyStencilData//"."//keyDBTerm,.false.)

    call envObj%jsonCont%load(rowHarmonic)
    call envObj%jsonCont%output(rowHarmonic)
    call envObj%jsonCont%load(transitionIndex)
    call envObj%jsonCont%output(transitionIndex)
    call envObj%jsonCont%load(fixedEnergyIndex)
    call envObj%jsonCont%output(fixedEnergyIndex)
    call envObj%jsonCont%load(absorptionTerm)
    call envObj%jsonCont%output(absorptionTerm)
    call envObj%jsonCont%load(dbTerm)
    call envObj%jsonCont%output(dbTerm)

    call initFixedBoltzmannStencilDirect(stencilTemplateObj,envObj,evolvedVar,implicitVar,mbData,rowHarmonic(1)%value,&
                                         transitionIndex(1)%value,fixedEnergyIndex(1)%value,absorptionTerm(1)%value,dbTerm(1)%value)

end subroutine initFixedBoltzmannStencil
!-----------------------------------------------------------------------------------------------------------------------------------
module subroutine initFixedBoltzmannStencilDirect(stencilTemplateObj,envObj,evolvedVar,implicitVar,mbData,&
                                           evolvedHarmonic,transitionIndex,fixedEnergyIndex,absorptionTerm,dbTerm)
    !! Initialize fixed mapping Boltzmann kinetic stencil template based on environment non-JSON inputs. 

    type(StencilTemplate)      ,intent(inout) :: stencilTemplateObj
    type(EnvironmentWrapper)   ,intent(inout) :: envObj
    character(*)               ,intent(in)    :: evolvedVar
    character(*)               ,intent(in)    :: implicitVar
    class(ModelboundCRMData)   ,intent(in)    :: mbData
    integer(ik)                ,intent(in)    :: evolvedHarmonic
    integer(ik)                ,intent(in)    :: transitionIndex
    integer(ik)                ,intent(in)    :: fixedEnergyIndex
    logical                    ,intent(in)    :: absorptionTerm
    logical                    ,intent(in)    :: dbTerm

    integer(ik) :: i ,usedLocNumX
    logical     :: pGrid, oddRowL, staggeredRowVar

    integer(ik) ,allocatable ,dimension(:) :: lGrid ,activeXGrid ,locNumX

    type(FixedBoltzmannStencilGen) :: boltzStencilGen
    type(MultiplicativeGeneratorCore) ,allocatable :: genCore 
    type(MultiplicativeStencilGen) :: multStencilGen
    type(IntArray) ,allocatable ,dimension(:) :: fixedVStencil

    type(SparseRowData) :: stencilMat

    if (assertions .or. assertionLvl >= 0) then 
        call assert(envObj%externalVars%isVarDist(envObj%externalVars%getVarIndex(implicitVar)),&
                                "initFixedBoltzmannStencilDirect must be invoked with implicitVar being a distribution")
        call assert(envObj%externalVars%isVarDist(envObj%externalVars%getVarIndex(evolvedVar)),&
                                "initFixedBoltzmannStencilDirect must be invoked with evolvedVar being a distributione")
    end if

    staggeredRowVar = envObj%externalVars%isVarOnDualGrid(evolvedVar)

    pGrid = envObj%geometryObj%isPeriodic()
    
    lGrid = envObj%gridObj%getLGrid()

    oddRowL = mod(lGrid(evolvedHarmonic),2)==1

    stencilTemplateObj%fixedStencil = .not. dbTerm

    activeXGrid = [(i,i=1,envObj%gridObj%getNumX())]

    if (staggeredRowVar .and. (.not. pGrid) .and. oddRowL) &
    activeXGrid = [(i,i=1,envObj%gridObj%getNumX()-1)]

    stencilTemplateObj%rowCoords = allCombinations([IntArray(activeXGrid),&
                                                    IntArray([evolvedHarmonic]),&
                                                    IntArray([(i,i=1,envObj%gridObj%getNumV())])])

    allocate(genCore)
    call genCore%init(envObj%gridObj,envObj%partitionObj,envObj%mpiCont%getWorldRank()&
                        ,stencilTemplateObj%rowCoords,vValsDependOnX=dbTerm)

    if (absorptionTerm) then
        stencilMat = mbData%getFixedW(fixedEnergyIndex)
        allocate(fixedVStencil(envObj%gridObj%getNumV()))
        do i = 1,envObj%gridObj%getNumV()
            allocate(fixedVStencil(i)%entry(0))
        end do

        do i = 1,size(stencilMat%rowIndex)
            fixedVStencil(stencilMat%rowIndex(i))%entry = stencilMat%columnVector(i)%entry
        end do
        call stencilTemplateObj%defaultStencil%init(vStencilFixed=fixedVStencil,mapToDist=.true.)  
    else
        call stencilTemplateObj%defaultStencil%init(mapToDist=.true.)  
    end if

    locNumX = envObj%partitionObj%getLocNumX()
    usedLocNumX = locNumX(envObj%mpiCont%getWorldRank()+1)

    if (envObj%partitionObj%getMaxHAtInd(envObj%mpiCont%getWorldRank()+1)==envObj%gridObj%getNumX()) then 
        if (staggeredRowVar .and. (.not. pGrid) .and. oddRowL) usedLocNumX = usedLocNumX - 1
    end if

    call multStencilGen%init(genCore,&
                        initXVals=jaggedArray(reshape(real([(1,i=1,usedLocNumX)],kind=rk),[1,usedLocNumX])),&
                        initHVals=jaggedArray(reshape(real([(1,i=1,1)],kind=rk),[1,1])))

    call boltzStencilGen%init(envObj%vSpaceObj,transitionIndex,fixedEnergyIndex,&
                            lNum=lGrid(evolvedHarmonic),absorptionTerm=absorptionTerm,dbTerm=dbTerm)

    call multStencilGen%setVGen(boltzStencilGen)
    allocate(stencilTemplateObj%stencilGen,source=multStencilGen)

end subroutine initFixedBoltzmannStencilDirect
!-----------------------------------------------------------------------------------------------------------------------------------
module subroutine initScalingLBCStencil(stencilTemplateObj,envObj,jsonPrefix,evolvedVar,implicitVar)
    !! Initialize scaling logical boundary condition kinetic stencil template based on environment object and JSON file. 

    type(StencilTemplate)      ,intent(inout) :: stencilTemplateObj
    type(EnvironmentWrapper)   ,intent(inout) :: envObj
    character(*)               ,intent(in)    :: jsonPrefix !! prefix for JSON keys for this template
    character(*)               ,intent(in)    :: evolvedVar
    character(*)               ,intent(in)    :: implicitVar

    integer(ik) :: i 

    type(NamedInteger) ,dimension(1) :: rowHarmonic ,colHarmonic
    type(NamedLogical) ,dimension(1) :: leftBoundary
    type(NamedIntegerArray) ,dimension(1) :: decompHarmonics
    type(NamedStringArray) ,dimension(1) :: derivReqVars
    type(NamedString) ,dimension(1) :: derivName

    rowHarmonic(1) = NamedInteger(jsonPrefix//"."//keyStencilData//"."//keyRowHarmonic,0)
    colHarmonic(1) = NamedInteger(jsonPrefix//"."//keyStencilData//"."//keyColHarmonic,1)

    leftBoundary(1) = NamedLogical(jsonPrefix//"."//keyStencilData//"."//keyLeftBoundary,.false.)
    derivName(1) = NamedString(jsonPrefix//"."//keyStencilData//"."//keyRuleName,"")
    decompHarmonics(1) = NamedIntegerArray(jsonPrefix//"."//keyStencilData//"."//keyDecompHarmonics,&
                        [(i,i=1,envObj%gridObj%getNumH())])
    derivReqVars(1)%name = jsonPrefix//"."//keyStencilData//"."//keyReqVarNames
    allocate(derivReqVars(1)%values(0))

    call envObj%jsonCont%load(rowHarmonic)
    call envObj%jsonCont%output(rowHarmonic)
    call envObj%jsonCont%load(colHarmonic)
    call envObj%jsonCont%output(colHarmonic)
    call envObj%jsonCont%load(leftBoundary)
    call envObj%jsonCont%output(leftBoundary)
    call envObj%jsonCont%load(decompHarmonics)
    call envObj%jsonCont%output(decompHarmonics)
    call envObj%jsonCont%load(derivReqVars)
    call envObj%jsonCont%output(derivReqVars)
    call envObj%jsonCont%load(derivName)
    call envObj%jsonCont%output(derivName)

    call initScalingLBCStencilDirect(stencilTemplateObj,envObj,evolvedVar,implicitVar,rowHarmonic(1)%value,colHarmonic(1)%value,&
                                    decompHarmonics(1)%values,derivName(1)%value,derivReqVars(1)%values,leftBoundary(1)%value)
    
end subroutine initScalingLBCStencil
!-----------------------------------------------------------------------------------------------------------------------------------
module subroutine initScalingLBCStencilDirect(stencilTemplateObj,envObj,evolvedVar,implicitVar,rowHarmonic,colHarmonic,&
                                              decompHarmonics,derivName,derivReqVars,leftBoundary)
    !! Initialize scaling logical boundary condition kinetic stencil template based on direct input. 

    type(StencilTemplate)           ,intent(inout) :: stencilTemplateObj
    type(EnvironmentWrapper)        ,intent(inout) :: envObj
    character(*)                    ,intent(in)    :: evolvedVar
    character(*)                    ,intent(in)    :: implicitVar
    integer(ik)                     ,intent(in)    :: rowHarmonic
    integer(ik)                     ,intent(in)    :: colHarmonic
    integer(ik) ,dimension(:)       ,intent(in)    :: decompHarmonics
    character(*)                    ,intent(in)    :: derivName
    type(StringArray) ,dimension(:) ,intent(in)    :: derivReqVars
    logical ,optional               ,intent(in)    :: leftBoundary

    integer(ik) :: i ,xCoord ,xStencil
    logical     :: oddRowL, staggeredRowVar ,isActive ,lBoundary

    integer(ik) ,allocatable ,dimension(:) :: lGrid ,derivReqIndices

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

    type(ScalingLBCStencilGen) :: stencilGen

    class(MatDerivation) ,allocatable :: derivObj

    if (assertions .or. assertionLvl >= 0) &
    call assert(evolvedVar==implicitVar,"initScalingLBCStencilDirect requires that the evolved and implicit variables be the same")

    staggeredRowVar = envObj%externalVars%isVarOnDualGrid(evolvedVar)

    lBoundary = .false. 
    if (present(leftBoundary)) lBoundary = leftBoundary

    allocate(derivReqIndices(size(derivReqVars)))
    do i = 1,size(derivReqIndices)
        derivReqIndices(i) = envObj%externalVars%getVarIndex(derivReqVars(i)%string)
    end do

    lGrid = envObj%gridObj%getLGrid()

    oddRowL = mod(lGrid(rowHarmonic),2)==1

    call envObj%textbookObj%copyMatDerivation(derivName,derivObj)

    select type (derivObj)
    type is (FScalingDerivation)

        if (staggeredRowVar) then
            call assert(.not. oddRowL,"initScalingLBCStencilDirect cannot have odd row l numbers on a staggered grid")
            if (.not. lBoundary) &
            call assert(all(mod(lGrid(decompHarmonics),2)==1) .or. all(mod(lGrid(decompHarmonics),2)==0),&
            "If the right boundary is treated using initScalingLBCStencilDirect on a staggered grid, included decomposition &
            &harmonics must either all be even or all odd")
        end if

        xCoord = envObj%gridObj%getNumX()
        if (lBoundary) xCoord = 1
        stencilTemplateObj%rowCoords = allCombinations([IntArray([xCoord]),&
                                                    IntArray([rowHarmonic]),&
                                                    IntArray([(i,i=1,envObj%gridObj%getNumV())])])

        isActive = size(envObj%partitionObj%filterCoords(envObj%mpiCont%getWorldRank()+1,stencilTemplateObj%rowCoords),2) > 0

        xStencil = 0
        if (.not. lBoundary .and. all(mod(lGrid(decompHarmonics),2)==1)) xStencil = -1 
        call stencilTemplateObj%defaultStencil%init(xStencil=[xStencil], &
                                                    hStencil=decompHarmonics-rowHarmonic,& 
                                                    vStencil=[0,1],mapToDist=.true.)

        dx = envObj%geometryObj%getCellWidths()

        call stencilGen%init(envObj%vSpaceObj,isActive,derivObj,derivReqIndices,lGrid(colHarmonic),&
                                dx(xCoord),includedDecompHarmonics=decompHarmonics)

        allocate(stencilTemplateObj%stencilGen,source=stencilGen)
    class default
        error stop &
        "initScalingLBCStencilDirect requires passed matrix derivation &
        &name to correspond to a derivation of type FScalingDerivation"
    end select
    
end subroutine initScalingLBCStencilDirect
!-----------------------------------------------------------------------------------------------------------------------------------
module subroutine initTermMomentStencil(stencilTemplateObj,envObj,jsonPrefix,evolvedVar,implicitVar)
    !! Initialize term moment kinetic stencil template based on environment object and JSON file. 

    type(StencilTemplate)      ,intent(inout) :: stencilTemplateObj
    type(EnvironmentWrapper)   ,intent(inout) :: envObj
    character(*)               ,intent(in)    :: jsonPrefix !! prefix for JSON keys for this template
    character(*)               ,intent(in)    :: evolvedVar
    character(*)               ,intent(in)    :: implicitVar

    type(NamedString)  ,dimension(1) :: termName
    type(NamedInteger) ,dimension(1) :: momentOrder ,colHarmonic
    
    termName(1) = NamedString(jsonPrefix//"."//keyStencilData//"."//keyTermName,"")
    momentOrder(1) = NamedInteger(jsonPrefix//"."//keyStencilData//"."//keyMomentOrder,0)
    colHarmonic(1) = NamedInteger(jsonPrefix//"."//keyStencilData//"."//keyColHarmonic,1)

    call envObj%jsonCont%load(colHarmonic)
    call envObj%jsonCont%output(colHarmonic)
    call envObj%jsonCont%load(momentOrder)
    call envObj%jsonCont%output(momentOrder)
    call envObj%jsonCont%load(termName)
    call envObj%jsonCont%output(termName)

    call initTermMomentStencilDirect(stencilTemplateObj,envObj,evolvedVar,implicitVar,&
                                     momentOrder(1)%value,colHarmonic(1)%value,termName(1)%value)

end subroutine initTermMomentStencil
!-----------------------------------------------------------------------------------------------------------------------------------
module subroutine initTermMomentStencilDirect(stencilTemplateObj,envObj,evolvedVar,implicitVar,&
                                              momentOrder,colHarmonic,termName)
    !! Initialize term moment kinetic stencil template based on direct input.

    type(StencilTemplate)      ,intent(inout) :: stencilTemplateObj
    type(EnvironmentWrapper)   ,intent(inout) :: envObj
    character(*)               ,intent(in)    :: evolvedVar
    character(*)               ,intent(in)    :: implicitVar
    integer(ik)                ,intent(in)    :: momentOrder
    integer(ik)                ,intent(in)    :: colHarmonic
    character(*)               ,intent(in)    :: termName

    type(TermMomentStencilGenerator) :: stencilGen

    integer(ik) :: i ,usedLocNumX
    logical     :: pGrid, staggeredRowVar ,staggeredColVar ,oddL

    integer(ik) ,allocatable ,dimension(:) :: locNumX ,xStencil ,lGrid

    if (assertions .or. assertionLvl >= 0) then 
        call assert(.not. envObj%externalVars%isVarDist(envObj%externalVars%getVarIndex(evolvedVar)),&
                                "initTermMomentStencilDirect must be invoked with evolvedVar being a fluid variable")
    end if

    staggeredRowVar = envObj%externalVars%isVarOnDualGrid(evolvedVar)
    staggeredColVar = envObj%externalVars%isVarOnDualGrid(implicitVar)

    stencilTemplateObj%rowCoords = allCombinations([IntArray([(i,i=1,envObj%gridObj%getNumX())])])

    pGrid = envObj%geometryObj%isPeriodic()
    if (staggeredRowVar .and. (.not. pGrid)) &
    stencilTemplateObj%rowCoords = allCombinations([IntArray([(i,i=1,envObj%gridObj%getNumX()-1)])]) !Remove last spatial row in staggered non-periodic case
    xStencil = [0]

    if (.not. envObj%externalVars%isVarDist(envObj%externalVars%getVarIndex(implicitVar))) then
        if (staggeredRowVar .neqv. staggeredColVar) then
            xStencil = [-1,0]
            if (staggeredRowVar) xStencil = [0,1]
        end if
    else 
        lGrid = envObj%gridObj%getLGrid()
    
        oddL = mod(lGrid(colHarmonic),2) == 1
        if (assertions .or. assertionLvl >= 0) call assert(oddL .eqv. staggeredRowVar,&
        "If initTermMomentStencilDirect is called with an implcit &
        &distribution variable the row variable must be staggered if the implicit harmonic has odd l")
    end if
    call stencilTemplateObj%defaultStencil%init(xStencil=xStencil,hStencil=[colHarmonic],&
                                                    vStencil=[(i,i=1,envObj%gridObj%getNumV())],xPeriodic=pGrid,&
                                            mapToDist=envObj%externalVars%isVarDist(envObj%externalVars%getVarIndex(implicitVar)))

    if ((.not. pGrid) .and. size(xStencil) > 1) then !Handle extrapolation points
        stencilTemplateObj%overridingStencilCoords = allCombinations([IntArray([1,envObj%gridObj%getNumX()])])
        allocate(stencilTemplateObj%overridingStencils(size(stencilTemplateObj%overridingStencilCoords,2)))
        do i = 1, size(stencilTemplateObj%overridingStencils)
            if (stencilTemplateObj%overridingStencilCoords(1,i) == 1) then
                call stencilTemplateObj%overridingStencils(i)%init(xStencil=[0,1],xPeriodic=pGrid)
            else
                call stencilTemplateObj%overridingStencils(i)%init(xStencil=[-2,-1],xPeriodic=pGrid)

            end if
        end do
    end if
    call stencilGen%init(envObj%partitionObj,envObj%vSpaceObj,envObj%mpiCont%getWorldRank(),&
                         momentOrder,termName,removeLastCell=staggeredRowVar .and. (.not. pGrid))
    allocate(stencilTemplateObj%stencilGen,source=stencilGen)

end subroutine initTermMomentStencilDirect
!-----------------------------------------------------------------------------------------------------------------------------------
module subroutine initVariableBoltzmannStencil(stencilTemplateObj,envObj,jsonPrefix,evolvedVar,implicitVar,mbData)
    !! Initialize variable mapping Boltzmann kinetic stencil template based on environment object and JSON file. 

    type(StencilTemplate)      ,intent(inout) :: stencilTemplateObj
    type(EnvironmentWrapper)   ,intent(inout) :: envObj
    character(*)               ,intent(in)    :: jsonPrefix !! prefix for JSON keys for this template
    character(*)               ,intent(in)    :: evolvedVar
    character(*)               ,intent(in)    :: implicitVar
    class(ModelboundCRMData)   ,intent(in)    :: mbData
    
    type(NamedInteger) ,dimension(1) :: rowHarmonic ,transitionIndex
    type(NamedLogical) ,dimension(1) :: absorptionTerm ,superelasticTerm

    rowHarmonic(1) = NamedInteger(jsonPrefix//"."//keyStencilData//"."//keyRowHarmonic,0)
    transitionIndex(1) = NamedInteger(jsonPrefix//"."//keyStencilData//"."//keyTransitionIndex,0)

    absorptionTerm(1) = NamedLogical(jsonPrefix//"."//keyStencilData//"."//keyAbsorptionTerm,.false.)
    superelasticTerm(1) = NamedLogical(jsonPrefix//"."//keyStencilData//"."//keySuperelasticTerm,.false.)

    call envObj%jsonCont%load(rowHarmonic)
    call envObj%jsonCont%output(rowHarmonic)
    call envObj%jsonCont%load(transitionIndex)
    call envObj%jsonCont%output(transitionIndex)
    call envObj%jsonCont%load(absorptionTerm)
    call envObj%jsonCont%output(absorptionTerm)
    call envObj%jsonCont%load(superelasticTerm)
    call envObj%jsonCont%output(superelasticTerm)

    call initVariableBoltzmannStencilDirect(stencilTemplateObj,envObj,evolvedVar,implicitVar,mbData,rowHarmonic(1)%value,&
                                         transitionIndex(1)%value,absorptionTerm(1)%value,superelasticTerm(1)%value)

end subroutine initVariableBoltzmannStencil
!-----------------------------------------------------------------------------------------------------------------------------------
module subroutine initVariableBoltzmannStencilDirect(stencilTemplateObj,envObj,evolvedVar,implicitVar,mbData,&
    evolvedHarmonic,transitionIndex,absorptionTerm,superelasticTerm)
    !! Initialize variable mapping Boltzmann kinetic stencil template based on environment non-JSON inputs. 

    type(StencilTemplate)      ,intent(inout) :: stencilTemplateObj
    type(EnvironmentWrapper)   ,intent(inout) :: envObj
    character(*)               ,intent(in)    :: evolvedVar
    character(*)               ,intent(in)    :: implicitVar
    class(ModelboundCRMData)   ,intent(in)    :: mbData
    integer(ik)                ,intent(in)    :: evolvedHarmonic
    integer(ik)                ,intent(in)    :: transitionIndex
    logical                    ,intent(in)    :: absorptionTerm
    logical                    ,intent(in)    :: superelasticTerm

    integer(ik) :: i ,usedLocNumX
    logical     :: pGrid, oddRowL, staggeredRowVar

    integer(ik) ,allocatable ,dimension(:) :: lGrid ,activeXGrid ,locNumX

    type(VariableBoltzmannStencilGen) :: boltzStencilGen
    type(MultiplicativeGeneratorCore) ,allocatable :: genCore 
    type(MultiplicativeStencilGen) :: multStencilGen
    type(IntArray) ,allocatable ,dimension(:) :: fixedVStencil

    if (assertions .or. assertionLvl >= 0) then 
        call assert(envObj%externalVars%isVarDist(envObj%externalVars%getVarIndex(implicitVar)),&
                                "initVariableBoltzmannStencilDirect must be invoked with implicitVar being a distribution")
        call assert(envObj%externalVars%isVarDist(envObj%externalVars%getVarIndex(evolvedVar)),&
                                "initVariableBoltzmannStencilDirect must be invoked with evolvedVar being a distributione")
    end if

    staggeredRowVar = envObj%externalVars%isVarOnDualGrid(evolvedVar)

    pGrid = envObj%geometryObj%isPeriodic()
    
    lGrid = envObj%gridObj%getLGrid()

    oddRowL = mod(lGrid(evolvedHarmonic),2)==1

    activeXGrid = [(i,i=1,envObj%gridObj%getNumX())]

    if (staggeredRowVar .and. (.not. pGrid) .and. oddRowL) &
    activeXGrid = [(i,i=1,envObj%gridObj%getNumX()-1)]

    stencilTemplateObj%rowCoords = allCombinations([IntArray(activeXGrid),&
                                                    IntArray([evolvedHarmonic]),&
                                                    IntArray([(i,i=1,envObj%gridObj%getNumV())])])

    allocate(genCore)
    call genCore%init(envObj%gridObj,envObj%partitionObj,envObj%mpiCont%getWorldRank()&
                        ,stencilTemplateObj%rowCoords,vValsDependOnX=.true.)

    if (absorptionTerm) then
        fixedVStencil = triangularIntArray(envObj%gridObj%getNumV(),lower=superelasticTerm)
        call stencilTemplateObj%defaultStencil%init(vStencilFixed=fixedVStencil,mapToDist=.true.)  
    else
        call stencilTemplateObj%defaultStencil%init(mapToDist=.true.)  
    end if

    locNumX = envObj%partitionObj%getLocNumX()
    usedLocNumX = locNumX(envObj%mpiCont%getWorldRank()+1)

    if (envObj%partitionObj%getMaxHAtInd(envObj%mpiCont%getWorldRank()+1)==envObj%gridObj%getNumX()) then 
        if (staggeredRowVar .and. (.not. pGrid) .and. oddRowL) usedLocNumX = usedLocNumX - 1
    end if

    call multStencilGen%init(genCore,&
                        initXVals=jaggedArray(reshape(real([(1,i=1,usedLocNumX)],kind=rk),[1,usedLocNumX])),&
                        initHVals=jaggedArray(reshape(real([(1,i=1,1)],kind=rk),[1,1])))

    call boltzStencilGen%init(envObj%vSpaceObj,transitionIndex,&
                            lNum=lGrid(evolvedHarmonic),absorptionTerm=absorptionTerm,superelasticTerm=superelasticTerm)

    call multStencilGen%setVGen(boltzStencilGen)
    allocate(stencilTemplateObj%stencilGen,source=multStencilGen)

end subroutine initVariableBoltzmannStencilDirect 
!-----------------------------------------------------------------------------------------------------------------------------------
!-----------------------------------------------------------------------------------------------------------------------------------
end submodule kinetic_stencil_templates_procedures
!-----------------------------------------------------------------------------------------------------------------------------------