def addKernels(generator, aderdg): numberOf3DBasisFunctions = aderdg.numberOf3DBasisFunctions() numberOfQuantities = aderdg.numberOfQuantities() ## Point sources mStiffnessTensor = Tensor('stiffnessTensor', (3, 3, 3, 3)) mSlip = Tensor('mSlip', (3, )) mNormal = Tensor('mNormal', (3, )) mArea = Scalar('mArea') basisFunctionsAtPoint = Tensor('basisFunctionsAtPoint', (numberOf3DBasisFunctions, )) mInvJInvPhisAtSources = Tensor('mInvJInvPhisAtSources', (numberOf3DBasisFunctions, )) JInv = Scalar('JInv') generator.add( 'computeMInvJInvPhisAtSources', mInvJInvPhisAtSources['k'] <= JInv * aderdg.db.M3inv['kl'] * basisFunctionsAtPoint['l']) #extract the moment tensors entries in SeisSol ordering (xx, yy, zz, xy, yz, xz) assert (numberOfQuantities >= 6) momentToNRF_spp = np.zeros((numberOfQuantities, 3, 3)) momentToNRF_spp[0, 0, 0] = 1 momentToNRF_spp[1, 1, 1] = 1 momentToNRF_spp[2, 2, 2] = 1 momentToNRF_spp[3, 0, 1] = 1 momentToNRF_spp[4, 1, 2] = 1 momentToNRF_spp[5, 0, 2] = 1 momentToNRF = Tensor('momentToNRF', (numberOfQuantities, 3, 3), spp=momentToNRF_spp) momentNRFKernel = momentToNRF['tpq'] * mArea * mStiffnessTensor[ 'pqij'] * mSlip['i'] * mNormal['j'] if aderdg.Q.hasOptDim(): sourceNRF = aderdg.Q['kt'] <= aderdg.Q['kt'] + mInvJInvPhisAtSources[ 'k'] * momentNRFKernel * aderdg.oneSimToMultSim['s'] else: sourceNRF = aderdg.Q['kt'] <= aderdg.Q[ 'kt'] + mInvJInvPhisAtSources['k'] * momentNRFKernel generator.add('sourceNRF', sourceNRF) momentFSRM = Tensor('momentFSRM', (numberOfQuantities, )) stfIntegral = Scalar('stfIntegral') if aderdg.Q.hasOptDim(): sourceFSRM = aderdg.Q[ 'kp'] <= aderdg.Q['kp'] + stfIntegral * mInvJInvPhisAtSources[ 'k'] * momentFSRM['p'] * aderdg.oneSimToMultSim['s'] else: sourceFSRM = aderdg.Q['kp'] <= aderdg.Q[ 'kp'] + stfIntegral * mInvJInvPhisAtSources['k'] * momentFSRM['p'] generator.add('sourceFSRM', sourceFSRM) ## Receiver output QAtPoint = OptionalDimTensor('QAtPoint', aderdg.Q.optName(), aderdg.Q.optSize(), aderdg.Q.optPos(), (numberOfQuantities, )) evaluateDOFSAtPoint = QAtPoint[ 'p'] <= aderdg.Q['kp'] * basisFunctionsAtPoint['k'] generator.add('evaluateDOFSAtPoint', evaluateDOFSAtPoint)
def addInit(self, generator): fluxScale = Scalar('fluxScale') computeFluxSolverLocal = self.AplusT['ij'] <= fluxScale * self.Tinv[ 'ki'] * self.QgodLocal['kq'] * self.starMatrix( 0)['ql'] * self.T['jl'] generator.add('computeFluxSolverLocal', computeFluxSolverLocal) computeFluxSolverNeighbor = self.AminusT[ 'ij'] <= fluxScale * self.Tinv['ki'] * self.QgodNeighbor[ 'kq'] * self.starMatrix(0)['ql'] * self.T['jl'] generator.add('computeFluxSolverNeighbor', computeFluxSolverNeighbor) QFortran = Tensor( 'QFortran', (self.numberOf3DBasisFunctions(), self.numberOfQuantities())) multSimToFirstSim = Tensor('multSimToFirstSim', (self.Q.optSize(), ), spp={(0, ): '1.0'}) if self.Q.hasOptDim(): copyQToQFortran = QFortran[ 'kp'] <= self.Q['kp'] * multSimToFirstSim['s'] else: copyQToQFortran = QFortran['kp'] <= self.Q['kp'] generator.add('copyQToQFortran', copyQToQFortran) stiffnessTensor = Tensor('stiffnessTensor', (3, 3, 3, 3)) direction = Tensor('direction', (3, )) christoffel = Tensor('christoffel', (3, 3)) computeChristoffel = christoffel[ 'ik'] <= stiffnessTensor['ijkl'] * direction['j'] * direction['l'] generator.add('computeChristoffel', computeChristoffel)
def addTime(self, generator): qShape = (self.numberOf3DBasisFunctions(), self.numberOfQuantities()) dQ0 = OptionalDimTensor('dQ(0)', self.Q.optName(), self.Q.optSize(), self.Q.optPos(), qShape, alignStride=True) power = Scalar('power') derivatives = [dQ0] generator.add('derivativeTaylorExpansion(0)', self.I['kp'] <= power * dQ0['kp']) for i in range(1, self.order): derivativeSum = Add() if self.sourceMatrix(): derivativeSum += derivatives[-1]['kq'] * self.sourceMatrix( )['qp'] for j in range(3): derivativeSum += self.db.kDivMT[j][self.t( 'kl')] * derivatives[-1]['lq'] * self.starMatrix(j)['qp'] derivativeSum = DeduceIndices( self.Q['kp'].indices).visit(derivativeSum) derivativeSum = EquivalentSparsityPattern().visit(derivativeSum) dQ = OptionalDimTensor('dQ({})'.format(i), self.Q.optName(), self.Q.optSize(), self.Q.optPos(), qShape, spp=derivativeSum.eqspp(), alignStride=True) generator.add('derivative({})'.format(i), dQ['kp'] <= derivativeSum) generator.add('derivativeTaylorExpansion({})'.format(i), self.I['kp'] <= self.I['kp'] + power * dQ['kp']) derivatives.append(dQ)
def addTime(self, generator): qShape = (self.numberOf3DBasisFunctions(), self.numberOfQuantities()) dQ = [OptionalDimTensor('dQ({})'.format(d), self.Q.optName(), self.Q.optSize(), self.Q.optPos(), qShape, alignStride=True) for d in range(self.order)] dQext = [OptionalDimTensor('dQext({})'.format(d), self.Q.optName(), self.Q.optSize(), self.Q.optPos(), self._qShapeExtended, alignStride=True) for d in range(self.order)] dQane = [OptionalDimTensor('dQane({})'.format(d), self.Q.optName(), self.Q.optSize(), self.Q.optPos(), self._qShapeAnelastic, alignStride=True) for d in range(self.order)] power = Scalar('power') derivativeTaylorExpansionEla = lambda d: (self.I['kp'] <= self.I['kp'] + power * dQ[d]['kp']) if d > 0 else (self.I['kp'] <= power * dQ[0]['kp']) derivativeTaylorExpansionAne = lambda d: (self.Iane['kpm'] <= self.Iane['kpm'] + power * dQane[d]['kpm']) if d > 0 else (self.Iane['kpm'] <= power * dQane[0]['kpm']) def derivative(kthDer): derivativeSum = Add() for j in range(3): derivativeSum += self.db.kDivMT[j][self.t('kl')] * dQ[kthDer-1]['lq'] * self.db.star[j]['qp'] return derivativeSum generator.addFamily('derivative', parameterSpaceFromRanges(range(1,self.order)), lambda d: [ dQext[d]['kp'] <= derivative(d), dQ[d]['kp'] <= dQext[d]['kq'] * self.selectEla['qp'] + dQane[d-1]['kqm'] * self.E['qmp'], dQane[d]['kpm'] <= self.w['m'] * dQext[d]['kq'] * self.selectAne['qp'] + dQane[d-1]['kpl'] * self.W['lm'] ]) generator.addFamily('derivativeTaylorExpansion', simpleParameterSpace(self.order), lambda d: [ derivativeTaylorExpansionEla(d), derivativeTaylorExpansionAne(d) ]) generator.addFamily('derivativeTaylorExpansionEla', simpleParameterSpace(self.order), derivativeTaylorExpansionEla)
def addKernels(generator, aderdg): numberOf3DBasisFunctions = aderdg.numberOf3DBasisFunctions() numberOfQuantities = aderdg.numberOfQuantities() ## Point sources mInvJInvPhisAtSources = Tensor('mInvJInvPhisAtSources', (numberOf3DBasisFunctions,)) momentNRF = Tensor('momentNRF', (numberOfQuantities,), spp=np.array([1]*6 + [0]*(numberOfQuantities-6), dtype=bool)) if aderdg.Q.hasOptDim(): sourceNRF = aderdg.Q['kp'] <= aderdg.Q['kp'] - mInvJInvPhisAtSources['k'] * momentNRF['p'] * aderdg.oneSimToMultSim['s'] else: sourceNRF = aderdg.Q['kp'] <= aderdg.Q['kp'] - mInvJInvPhisAtSources['k'] * momentNRF['p'] generator.add('sourceNRF', sourceNRF) momentFSRM = Tensor('momentFSRM', (numberOfQuantities,)) stfIntegral = Scalar('stfIntegral') if aderdg.Q.hasOptDim(): sourceFSRM = aderdg.Q['kp'] <= aderdg.Q['kp'] + stfIntegral * mInvJInvPhisAtSources['k'] * momentFSRM['p'] * aderdg.oneSimToMultSim['s'] else: sourceFSRM = aderdg.Q['kp'] <= aderdg.Q['kp'] + stfIntegral * mInvJInvPhisAtSources['k'] * momentFSRM['p'] generator.add('sourceFSRM', sourceFSRM) ## Receiver output basisFunctionsAtPoint = Tensor('basisFunctions', (numberOf3DBasisFunctions,)) QAtPoint = OptionalDimTensor('QAtPoint', aderdg.Q.optName(), aderdg.Q.optSize(), aderdg.Q.optPos(), (numberOfQuantities,)) evaluateDOFSAtPoint = QAtPoint['p'] <= aderdg.Q['kp'] * basisFunctionsAtPoint['k'] generator.add('evaluateDOFSAtPoint', evaluateDOFSAtPoint)
def addKernels(generator, aderdg, include_tensors, matricesDir, dynamicRuptureMethod): easi_ident_map = np.stack([np.eye(aderdg.numberOfQuantities())] * aderdg.numberOf2DBasisFunctions(), axis=2) assert(easi_ident_map.shape == (aderdg.numberOfQuantities(), aderdg.numberOfQuantities(), aderdg.numberOf2DBasisFunctions())) easi_ident_map = Tensor('easiIdentMap', easi_ident_map.shape, easi_ident_map, alignStride=False) easi_boundary_constant = Tensor('easiBoundaryConstant', (aderdg.numberOfQuantities(), aderdg.numberOf2DBasisFunctions()), alignStride=False) easi_boundary_map = Tensor('easiBoundaryMap', (aderdg.numberOfQuantities(), aderdg.numberOfQuantities(), aderdg.numberOf2DBasisFunctions(),), alignStride=False) create_easi_boundary_ghost_cells = ( aderdg.INodal['la'] <= easi_boundary_map['abl'] * aderdg.INodal['lb'] + easi_ident_map['abl'] * easi_boundary_constant['bl'] ) generator.add('createEasiBoundaryGhostCells', create_easi_boundary_ghost_cells) projectToNodalBoundary = lambda j: aderdg.INodal['kp'] <= aderdg.db.V3mTo2nFace[j]['km'] * aderdg.I['mp'] generator.addFamily('projectToNodalBoundary', simpleParameterSpace(4), projectToNodalBoundary) projectToNodalBoundaryRotated = lambda j: aderdg.INodal['kp'] <= aderdg.db.V3mTo2nFace[j]['kl'] \ * aderdg.I['lm'] \ * aderdg.Tinv['pm'] generator.addFamily('projectToNodalBoundaryRotated', simpleParameterSpace(4), projectToNodalBoundaryRotated) # To be used as Tinv in flux solver - this way we can save two rotations for # Dirichlet boundary, as ghost cell dofs are already rotated identity_rotation = np.double(aderdg.transformation_spp()) identity_rotation[0:9, 0:9] = np.eye(9) identity_rotation = Tensor('identityT', aderdg.transformation_spp().shape, identity_rotation, ) include_tensors.add(identity_rotation) aderdg.INodalUpdate = OptionalDimTensor('INodalUpdate', aderdg.INodal.optName(), aderdg.INodal.optSize(), aderdg.INodal.optPos(), (aderdg.numberOf2DBasisFunctions(), aderdg.numberOfQuantities()), alignStride=True) factor = Scalar('factor') updateINodal = aderdg.INodal['kp'] <= aderdg.INodal['kp'] + factor * aderdg.INodalUpdate['kp'] generator.add('updateINodal', updateINodal)
def addKernels(generator, aderdg, matricesDir, dynamicRuptureMethod): if dynamicRuptureMethod == 'quadrature': numberOfPoints = (aderdg.order+1)**2 elif dynamicRuptureMethod == 'cellaverage': numberOfPoints = int(4**math.ceil(math.log(aderdg.order*(aderdg.order+1)/2,4))) else: raise ValueError('Unknown dynamic rupture method.') clones = dict() # Load matrices db = parseJSONMatrixFile('{}/dr_{}_matrices_{}.json'.format(matricesDir, dynamicRuptureMethod, aderdg.order), clones, alignStride=aderdg.alignStride, transpose=aderdg.transpose) # Determine matrices # Note: This does only work because the flux does not depend on the mechanisms in the case of viscoelastic attenuation godShape = (aderdg.numberOfQuantities(), aderdg.numberOfQuantities()) godunovMatrix = Tensor('godunovMatrix', godShape) fluxSolverShape = (aderdg.numberOfQuantities(), aderdg.numberOfExtendedQuantities()) fluxSolver = Tensor('fluxSolver', fluxSolverShape) gShape = (numberOfPoints, aderdg.numberOfQuantities()) godunovState = OptionalDimTensor('godunovState', aderdg.Q.optName(), aderdg.Q.optSize(), aderdg.Q.optPos(), gShape, alignStride=True) generator.add('rotateGodunovStateLocal', godunovMatrix['qp'] <= aderdg.Tinv['kq'] * aderdg.QgodLocal['kp']) generator.add('rotateGodunovStateNeighbor', godunovMatrix['qp'] <= aderdg.Tinv['kq'] * aderdg.QgodNeighbor['kp']) fluxScale = Scalar('fluxScale') generator.add('rotateFluxMatrix', fluxSolver['qp'] <= fluxScale * aderdg.starMatrix(0)['qk'] * aderdg.T['pk']) def godunovStateGenerator(i,h): target = godunovState['kp'] term = db.V3mTo2n[i,h][aderdg.t('kl')] * aderdg.Q['lq'] * godunovMatrix['qp'] if h == 0: return target <= term return target <= target + term godunovStatePrefetch = lambda i,h: godunovState generator.addFamily('godunovState', simpleParameterSpace(4,4), godunovStateGenerator, godunovStatePrefetch) nodalFluxGenerator = lambda i,h: aderdg.extendedQTensor()['kp'] <= aderdg.extendedQTensor()['kp'] + db.V3mTo2nTWDivM[i,h][aderdg.t('kl')] * godunovState['lq'] * fluxSolver['qp'] nodalFluxPrefetch = lambda i,h: aderdg.I generator.addFamily('nodalFlux', simpleParameterSpace(4,4), nodalFluxGenerator, nodalFluxPrefetch)
def addKernels(generator, aderdg, matricesDir, dynamicRuptureMethod): if dynamicRuptureMethod == 'quadrature': numberOfPoints = (aderdg.order + 1)**2 elif dynamicRuptureMethod == 'cellaverage': numberOfPoints = int(4**math.ceil( math.log(aderdg.order * (aderdg.order + 1) / 2, 4))) else: raise ValueError('Unknown dynamic rupture method.') clones = dict() # Load matrices db = parseJSONMatrixFile('{}/dr_{}_matrices_{}.json'.format( matricesDir, dynamicRuptureMethod, aderdg.order), clones, alignStride=aderdg.alignStride, transpose=aderdg.transpose) db.update( parseJSONMatrixFile('{}/resample_{}.json'.format( matricesDir, aderdg.order))) # Determine matrices # Note: This does only work because the flux does not depend on the mechanisms in the case of viscoelastic attenuation trans_inv_spp_T = aderdg.transformation_inv_spp().transpose() TinvT = Tensor('TinvT', trans_inv_spp_T.shape, spp=trans_inv_spp_T) flux_solver_spp = aderdg.flux_solver_spp() fluxSolver = Tensor('fluxSolver', flux_solver_spp.shape, spp=flux_solver_spp) gShape = (numberOfPoints, aderdg.numberOfQuantities()) QInterpolated = OptionalDimTensor('QInterpolated', aderdg.Q.optName(), aderdg.Q.optSize(), aderdg.Q.optPos(), gShape, alignStride=True) generator.add('transposeTinv', TinvT['ij'] <= aderdg.Tinv['ji']) fluxScale = Scalar('fluxScale') generator.add( 'rotateFluxMatrix', fluxSolver['qp'] <= fluxScale * aderdg.starMatrix(0)['qk'] * aderdg.T['pk']) def interpolateQGenerator(i, h): return QInterpolated['kp'] <= db.V3mTo2n[i, h][aderdg.t( 'kl')] * aderdg.Q['lq'] * TinvT['qp'] interpolateQPrefetch = lambda i, h: QInterpolated generator.addFamily('evaluateAndRotateQAtInterpolationPoints', simpleParameterSpace(4, 4), interpolateQGenerator, interpolateQPrefetch) nodalFluxGenerator = lambda i, h: aderdg.extendedQTensor()[ 'kp'] <= aderdg.extendedQTensor()['kp'] + db.V3mTo2nTWDivM[i, h][ aderdg.t('kl')] * QInterpolated['lq'] * fluxSolver['qp'] nodalFluxPrefetch = lambda i, h: aderdg.I generator.addFamily('nodalFlux', simpleParameterSpace(4, 4), nodalFluxGenerator, nodalFluxPrefetch) return {db.resample}
def addKernels(generator, aderdg, include_tensors, matricesDir, dynamicRuptureMethod): easi_ident_map = np.stack([np.eye(aderdg.numberOfQuantities())] * aderdg.numberOf2DBasisFunctions(), axis=2) assert(easi_ident_map.shape == (aderdg.numberOfQuantities(), aderdg.numberOfQuantities(), aderdg.numberOf2DBasisFunctions())) easi_ident_map = Tensor('easiIdentMap', easi_ident_map.shape, easi_ident_map, alignStride=False) easi_boundary_constant = Tensor('easiBoundaryConstant', (aderdg.numberOfQuantities(), aderdg.numberOf2DBasisFunctions()), alignStride=False) easi_boundary_map = Tensor('easiBoundaryMap', (aderdg.numberOfQuantities(), aderdg.numberOfQuantities(), aderdg.numberOf2DBasisFunctions(),), alignStride=False) create_easi_boundary_ghost_cells = ( aderdg.INodal['la'] <= easi_boundary_map['abl'] * aderdg.INodal['lb'] + easi_ident_map['abl'] * easi_boundary_constant['bl'] ) generator.add('createEasiBoundaryGhostCells', create_easi_boundary_ghost_cells) projectToNodalBoundary = lambda j: aderdg.INodal['kp'] <= aderdg.db.V3mTo2nFace[j]['km'] * aderdg.I['mp'] generator.addFamily('projectToNodalBoundary', simpleParameterSpace(4), projectToNodalBoundary) projectToNodalBoundaryRotated = lambda j: aderdg.INodal['kp'] <= aderdg.db.V3mTo2nFace[j]['kl'] \ * aderdg.I['lm'] \ * aderdg.Tinv['pm'] generator.addFamily('projectToNodalBoundaryRotated', simpleParameterSpace(4), projectToNodalBoundaryRotated) # To be used as Tinv in flux solver - this way we can save two rotations for # Dirichlet boundary, as ghost cell dofs are already rotated identity_rotation = np.double(aderdg.transformation_spp()) identity_rotation[0:9, 0:9] = np.eye(9) identity_rotation = Tensor('identityT', aderdg.transformation_spp().shape, identity_rotation, ) include_tensors.add(identity_rotation) project2nFaceTo3m = tensor_collection_from_constant_expression( base_name='project2nFaceTo3m', expressions=lambda i: aderdg.db.rDivM[i]['jk'] * aderdg.db.V2nTo2m['kl'], group_indices=range(4), target_indices='jl') aderdg.db.update(project2nFaceTo3m) selectZDisplacementFromQuantities = np.zeros(aderdg.numberOfQuantities()) selectZDisplacementFromQuantities[8] = 1 selectZDisplacementFromQuantities= Tensor('selectZDisplacementFromQuantities', selectZDisplacementFromQuantities.shape, selectZDisplacementFromQuantities, ) selectZDisplacementFromDisplacements = np.zeros(3) selectZDisplacementFromDisplacements[2] = 1 selectZDisplacementFromDisplacements = Tensor('selectZDisplacementFromDisplacements', selectZDisplacementFromDisplacements.shape, selectZDisplacementFromDisplacements, ) aderdg.INodalDisplacement = OptionalDimTensor('INodalDisplacement', aderdg.Q.optName(), aderdg.Q.optSize(), aderdg.Q.optPos(), (aderdg.numberOf2DBasisFunctions(),), alignStride=True) displacement = OptionalDimTensor('displacement', aderdg.Q.optName(), aderdg.Q.optSize(), aderdg.Q.optPos(), (aderdg.numberOf3DBasisFunctions(), 3), alignStride=True) dt = Scalar('dt') displacementAvgNodal = lambda side: aderdg.INodalDisplacement['i'] <= \ aderdg.db.V3mTo2nFace[side]['ij'] * aderdg.I['jk'] * selectZDisplacementFromQuantities['k'] \ + dt * aderdg.db.V3mTo2nFace[side]['ij'] * displacement['jk'] * selectZDisplacementFromDisplacements['k'] generator.addFamily('displacementAvgNodal', simpleParameterSpace(4), displacementAvgNodal) aderdg.INodalUpdate = OptionalDimTensor('INodalUpdate', aderdg.INodal.optName(), aderdg.INodal.optSize(), aderdg.INodal.optPos(), (aderdg.numberOf2DBasisFunctions(), aderdg.numberOfQuantities()), alignStride=True) factor = Scalar('factor') updateINodal = aderdg.INodal['kp'] <= aderdg.INodal['kp'] + factor * aderdg.INodalUpdate['kp'] generator.add('updateINodal', updateINodal)
def addTime(self, generator, targets): super().addTime(generator, targets) stiffnessValues = [ self.db.kDivMT[d].values_as_ndarray() for d in range(3) ] fullShape = (self.numberOf3DBasisFunctions(), self.numberOf3DBasisFunctions()) qShape = (self.numberOf3DBasisFunctions(), self.numberOfQuantities()) stpShape = (self.numberOf3DBasisFunctions(), self.numberOfQuantities(), self.order) quantityShape = (self.numberOfQuantities(), self.numberOfQuantities()) spaceTimePredictorRhs = OptionalDimTensor('spaceTimePredictorRhs', self.Q.optName(), self.Q.optSize(), self.Q.optPos(), stpShape, alignStride=True) spaceTimePredictor = OptionalDimTensor('spaceTimePredictor', self.Q.optName(), self.Q.optSize(), self.Q.optPos(), stpShape, alignStride=True) testRhs = OptionalDimTensor('testRhs', self.Q.optName(), self.Q.optSize(), self.Q.optPos(), stpShape, alignStride=True) testLhs = OptionalDimTensor('testLhs', self.Q.optName(), self.Q.optSize(), self.Q.optPos(), stpShape, alignStride=True) timestep = Scalar('timestep') G = {10: Scalar('Gk'), 11: Scalar('Gl'), 12: Scalar('Gm')} ## Compute the index range for basis functions of a certain degree # # The basis functions are ordered with increasing degree, i.e. the first # basis function has degree 0, the next three basis functions have degree # 1, the next six basis functions have degree 2 and so forth. # This method computes the indices Bn_lower, Bn_upper, such that # forall Bn_lower =< i < Bn_upper: degree(phi_i) == n # # @param n The desired polynomial degree def modeRange(n): Bn_lower = choose(n - 1 + 3, 3) Bn_upper = choose(n + 3, 3) return (Bn_lower, Bn_upper) ## Compute a matrix, which filters out all basis function of degree n # # Compute a matrix M, such that for any DOF vector Q, M*Q only contains # the parts of Q, which correspond to basis functions of degree n # # @param n The desired polynomial degree def selectModes(n): Bn_1, Bn = modeRange(n) selectModesSpp = np.zeros(fullShape) selectModesSpp[Bn_1:Bn, Bn_1:Bn] = np.eye(Bn - Bn_1) return Tensor('selectModes({})'.format(n), fullShape, spp=selectModesSpp) ## Compute a matrix, which slices out one quantity # # @param quantityNumber The number of the quantity, which is sliced out def selectQuantity(quantityNumber): selectSpp = np.zeros( (self.numberOfQuantities(), self.numberOfQuantities())) selectSpp[quantityNumber, quantityNumber] = 1 return Tensor('selectQuantity({})'.format(quantityNumber), selectSpp.shape, spp=selectSpp) ## Compute a matrix, which slides out one quantity from the upper triangular # part of the source matrix # # Note: G = E - diag(E) # # @param quantityNumber The number of the quantity, which is sliced out def selectQuantityG(quantityNumber): selectSpp = np.zeros( (self.numberOfQuantities(), self.numberOfQuantities())) #The source matrix G only contains values at (o-4, o) selectSpp[quantityNumber - 4, quantityNumber] = 1 return Tensor('selectQuantityG({})'.format(quantityNumber), selectSpp.shape, spp=selectSpp) ## Zinv(o) = $(Z - E^*_{oo} * I)^{-1}$ # # @param o Index as described above def Zinv(o): return Tensor('Zinv({})'.format(o), (self.order, self.order)) ## Submatrix of the stiffness matrix # # The stiffness matrix for derivatives in direction d, where only the # contribution from basis functions of degree n is considered # # @param d The direction in which derivatives are taken 0 =< d < 3 # @param n The polynomial degree n, which is taken into account def kSub(d, n): Bn_1, Bn = modeRange(n) stiffnessSpp = np.zeros(fullShape) stiffnessSpp[:, Bn_1:Bn] = -stiffnessValues[d][:, Bn_1:Bn] return Tensor('kDivMTSub({},{})'.format(d, n), fullShape, spp=stiffnessSpp) kernels = list() kernels.append( spaceTimePredictorRhs['kpt'] <= self.Q['kp'] * self.db.wHat['t']) for n in range(self.order - 1, -1, -1): for o in range(self.numberOfQuantities() - 1, -1, -1): kernels.append( spaceTimePredictor['kpt'] <= spaceTimePredictor['kpt'] + selectModes(n)['kl'] * selectQuantity(o)['pq'] * spaceTimePredictorRhs['lqu'] * Zinv(o)['ut']) #G only has one relevant non-zero entry in each iteration, so we make it a scalar #G[o] = E[o-4, o] * timestep #In addition E only has non-zero entries, if o > 10 if o >= 10: kernels.append( spaceTimePredictorRhs['kpt'] <= spaceTimePredictorRhs['kpt'] + G[o] * selectQuantityG(o)['pv'] * selectQuantity(o)['vq'] * selectModes(n)['kl'] * spaceTimePredictor['lqt']) if n > 0: derivativeSum = spaceTimePredictorRhs['kpt'] for d in range(3): derivativeSum += kSub(d, n)['kl'] * spaceTimePredictor[ 'lqt'] * self.starMatrix(d)['qp'] kernels.append(spaceTimePredictorRhs['kpt'] <= derivativeSum) kernels.append(self.I['kp'] <= timestep * spaceTimePredictor['kpt'] * self.db.timeInt['t']) generator.add('spaceTimePredictor', kernels) # Test to see if the kernel actually solves the system of equations # This part is not used in the time kernel, but for unit testing deltaSppLarge = np.eye(self.numberOfQuantities()) deltaLarge = Tensor('deltaLarge', deltaSppLarge.shape, spp=deltaSppLarge) deltaSppSmall = np.eye(self.order) deltaSmall = Tensor('deltaSmall', deltaSppSmall.shape, spp=deltaSppSmall) minus = Scalar('minus') lhs = deltaLarge['oq'] * self.db.Z['uk'] * spaceTimePredictor['lqk'] lhs += minus * self.sourceMatrix( )['qo'] * deltaSmall['uk'] * spaceTimePredictor['lqk'] generator.add('stpTestLhs', testLhs['lou'] <= lhs) rhs = self.Q['lo'] * self.db.wHat['u'] for d in range(3): rhs += minus * self.starMatrix( d)['qo'] * self.db.kDivMT[d]['lm'] * spaceTimePredictor['mqu'] generator.add('stpTestRhs', testRhs['lou'] <= rhs)