Ejemplo n.º 1
0
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)
Ejemplo n.º 2
0
    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)
Ejemplo n.º 3
0
 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)
Ejemplo n.º 4
0
  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)
Ejemplo n.º 5
0
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)
Ejemplo n.º 6
0
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)
Ejemplo n.º 7
0
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)
Ejemplo n.º 8
0
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}
Ejemplo n.º 9
0
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)
Ejemplo n.º 10
0
    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)