def computeUpwindSaturation(NWorldCoarse, boundarys, sT, fluxTF): d = np.size(NWorldCoarse) sTF = np.tile(sT[...,None], [1, 2*d]) basis = util.linearpIndexBasis(NWorldCoarse-1) for k in range(d): b = basis[k] N = np.array(NWorldCoarse-1) N[k] -= 1 TIndBase = util.lowerLeftpIndexMap(N, NWorldCoarse-1) N[k] = 0 TIndBottom = util.lowerLeftpIndexMap(N, NWorldCoarse-1) for face in [0,1]: faceInd = 2*k+face stepToFace = b*(face*(NWorldCoarse[k]-1)) TIndInterior = (1-face)*b + TIndBase TIndBoundary = stepToFace + TIndBottom # Interior TIndDst = TIndInterior[fluxTF[TIndInterior, faceInd] < 0] TIndSrc = TIndDst + (2*face - 1)*b sTF[TIndDst, faceInd] = sT[TIndSrc] # Boundary TIndDst = TIndBoundary[fluxTF[TIndBoundary, faceInd] < 0] sTF[TIndDst, faceInd] = boundarys[k, face] return sTF
def harmonicMeanOverFaces(NPatchCoarse, NCoarseElement, k, boundary, aPatch): NPatchFine = NPatchCoarse*NCoarseElement TPatchBasis = util.linearpIndexBasis(NPatchCoarse-1) NPatchBottom = np.array(NPatchCoarse) NPatchBottom[k] = 1 NtBottom = np.prod(NPatchBottom) NPatchBase = np.array(NPatchCoarse) NPatchBase[k] -= 1 TIndBase = util.lowerLeftpIndexMap(NPatchBase-1, NPatchCoarse-1) TIndBottom = util.lowerLeftpIndexMap(NPatchBottom-1, NPatchCoarse-1) t0Faces = faceElementIndices(NPatchCoarse, NCoarseElement, k, 0) t1Faces = faceElementIndices(NPatchCoarse, NCoarseElement, k, 1) aH0Faces = aPatch[t0Faces[TPatchBasis[k] + TIndBase]] aH1Faces = aPatch[t1Faces[TIndBase]] if boundary==0: abFaces = aPatch[t0Faces] abFaces[TPatchBasis[k] + TIndBase] = 2*aH0Faces*aH1Faces/(aH0Faces + aH1Faces) elif boundary==1: abFaces = aPatch[t1Faces] abFaces[TIndBase] = 2*aH0Faces*aH1Faces/(aH0Faces + aH1Faces) return abFaces
def computeJumps(NWorldCoarse, quantityT): d = np.size(NWorldCoarse) b = util.linearpIndexBasis(NWorldCoarse-1) jumpTF = np.tile(quantityT[...,None], [1, 2*d]) for k in range(d): NWorldBase = np.array(NWorldCoarse) NWorldBase[k] -= 1 TIndBase = util.lowerLeftpIndexMap(NWorldBase-1, NWorldCoarse-1) jump = quantityT[TIndBase] - quantityT[TIndBase + b[k]] jumpTF[TIndBase, 2*k + 1] = jump jumpTF[TIndBase + b[k], 2*k] = -jump return jumpTF
def computeConservativeFlux(world, fluxTF, f=None): # From Odsaeter et al. # No source terms implemented yet here. assert(f is None) boundaryConditions = world.boundaryConditions FLoc = world.FLocCoarse fluxTF = np.array(fluxTF) NWorldCoarse = world.NWorldCoarse d = np.size(NWorldCoarse) tBasis = util.linearpIndexBasis(NWorldCoarse-1) def applyNeumannBoundaryConditions(fluxTF): def applyNeumannBoundaryConditionsOneSide(fluxTF, k, boundary): stepToFace = (boundary)*tBasis[k]*(NWorldCoarse[k]-1) NWorldBottom = np.array(NWorldCoarse) NWorldBottom[k] = 1 TIndBoundary = stepToFace + util.lowerLeftpIndexMap(NWorldBottom-1, NWorldCoarse-1) fluxTF[TIndBoundary, 2*k+boundary] = 0 for k in range(d): for boundary in [0, 1]: if boundaryConditions[k, boundary] == 1: applyNeumannBoundaryConditionsOneSide(fluxTF, k, boundary) applyNeumannBoundaryConditions(fluxTF) boundaryMap = boundaryConditions==0 FC = fem.assembleFaceConnectivityMatrix(NWorldCoarse, FLoc, boundaryMap) r = -np.sum(fluxTF, axis=1) y = linalg.linSolve(FC, r) yJumpsTF = computeJumps(NWorldCoarse, y) # fluxTF is already scaled with face area. Scale the jumps before adding scaledyJumpsTF = yJumpsTF*FLoc[np.newaxis, ...] conservativeFluxTF = fluxTF + scaledyJumpsTF applyNeumannBoundaryConditions(conservativeFluxTF) return conservativeFluxTF
def faceElementIndices(NPatchCoarse, NCoarseElement, k, boundary): '''Return indices of fine elements adjacent to face (k, boundary) for all coarse triangles Return type shape: (NtCoarse, Number of elements of face) ''' NPatchFine = NPatchCoarse*NCoarseElement NFace = np.array(NCoarseElement) NFace[k] = 1 tPatchBasis = util.linearpIndexBasis(NPatchFine-1) tStepToFace = boundary*tPatchBasis[k]*(NCoarseElement[k]-1) tIndexMap = tStepToFace + util.lowerLeftpIndexMap(NFace-1, NPatchFine-1) tStartIndices = util.pIndexMap(NPatchCoarse-1, NPatchFine-1, NCoarseElement) tIndices = np.add.outer(tStartIndices, tIndexMap) return tIndices
def computeAverageFaceFlux(NWorldCoarse, fluxTF): # Note I: these velocities are not conservative and do not fulfill # strongly with Neumann boundary conditions # # Note II: the velocities are integrated and hence already scaled with face # area d = np.size(NWorldCoarse) b = util.linearpIndexBasis(NWorldCoarse-1) avgFluxTF = np.array(fluxTF) for k in range(d): NWorldBase = np.array(NWorldCoarse-1) NWorldBase[k] -= 1 TIndBase = util.lowerLeftpIndexMap(NWorldBase, NWorldCoarse-1) avg = 0.5*(fluxTF[TIndBase, 2*k + 1] - fluxTF[TIndBase + b[k], 2*k]) avgFluxTF[TIndBase, 2*k + 1] = avg avgFluxTF[TIndBase + b[k], 2*k] = -avg return avgFluxTF
def computeElementFaceFluxFromSigma(NWorldCoarse, sigmaFluxT): d = np.size(NWorldCoarse) NtCoarse = np.prod(NWorldCoarse) fluxTF = np.zeros([NtCoarse, 2*d]) pBasis = util.linearpIndexBasis(np.ones_like(NWorldCoarse)) for k in range(d): N = np.ones_like(NWorldCoarse) N[k] = 0 bottomp = util.lowerLeftpIndexMap(N, np.ones_like(N)) topp = bottomp + pBasis[k] NFace = np.array(NWorldCoarse) NFace[k] = 1 faceArea = np.prod(1./NFace) fluxTF[:,2*k] = faceArea*np.mean(-sigmaFluxT[:,bottomp], axis=1) fluxTF[:,2*k + 1] = faceArea*np.mean(-sigmaFluxT[:,topp], axis=1) return fluxTF
def assembleFaceConnectivityMatrix(NPatch, FLoc, boundaryMap=None): Nt = np.prod(NPatch) d = np.size(NPatch) if boundaryMap is None: boundaryMap = np.zeros([d, 2], dtype='bool') rowsList = [] colsList = [] valuesList = [] tBasis = util.linearpIndexBasis(NPatch - 1) for k in range(d): for boundary in [0, 1]: NPatchBase = np.array(NPatch) NPatchBase[k] -= 1 TIndInterior = (1 - boundary) * tBasis[k] + util.lowerLeftpIndexMap( NPatchBase - 1, NPatch - 1) nT = np.size(TIndInterior) FLocRepeated = np.repeat(FLoc[2 * k + boundary], nT) # Add diagonal elements rowsList.append(TIndInterior) colsList.append(TIndInterior) valuesList.append(FLocRepeated) # Add off-diagonal elements rowsList.append(TIndInterior + (2 * boundary - 1) * tBasis[k]) colsList.append(TIndInterior) valuesList.append(-FLocRepeated / 2.) rowsList.append(TIndInterior) colsList.append(TIndInterior + (2 * boundary - 1) * tBasis[k]) valuesList.append(-FLocRepeated / 2.) # Add boundary diagonal elements, if applies if boundaryMap[k, boundary]: NPatchBottom = np.array(NPatch) NPatchBottom[k] = 1 TIndBoundary = (boundary)*tBasis[k]*(NPatch[k]-1) + \ util.lowerLeftpIndexMap(NPatchBottom-1, NPatch-1) nT = np.size(TIndBoundary) FLocRepeatedBoundary = np.repeat(FLoc[2 * k + boundary], nT) rowsList.append(TIndBoundary) colsList.append(TIndBoundary) valuesList.append(FLocRepeatedBoundary) # Concatenate lists rows = np.hstack(rowsList) cols = np.hstack(colsList) values = np.hstack(valuesList) # Create sparse matrix FC = sparse.csc_matrix((values, (rows, cols)), shape=(Nt, Nt)) return FC
def __init__(self, NMax): self.NMax = NMax self.indexBasis = util.linearpIndexBasis(NMax) self.factorCache = dict()