diff --git a/dune/mmdg/mmdg.hh b/dune/mmdg/mmdg.hh index 044da9845ae966c3801c3f9f6d71545ccd1ae126..5892fa85f95ce721a010292df2ff7a1f2f01be1f 100644 --- a/dune/mmdg/mmdg.hh +++ b/dune/mmdg/mmdg.hh @@ -11,7 +11,7 @@ public: static constexpr int dim = GridView::dimension; using Base = DG<Grid, GridView, Mapper, Problem>; - using Scalar = typename Base::Scalar; + using Scalar = typename Base::Scalar; //NOTE using Base::Scalar using Matrix = typename Base::Matrix; using Vector = typename Base::Vector; using Coordinate = Dune::FieldVector<Scalar, dim>; @@ -105,60 +105,6 @@ private: //basis functions (0, phi_iElem,i) const int iElemIdxSLE = (dim + 1)*Base::gridView_.size(0) + dim*iElemIdx; - // === interface entries === - - //fill load vector b with interface entries using a quadrature rule - for (const auto& ip : rule_qInterface) - { - //quadrature point in local and global coordinates - const auto& qpLocal = ip.position(); - const auto& qpGlobal = iGeo.global(qpLocal); - - const Scalar quadratureFactor = - ip.weight() * iGeo.integrationElement(qpLocal); - const Scalar qInterfaceEvaluation(Base::problem_.qInterface(qpGlobal)); - - //quadrature for int_elem q*phi_elem,0 dV - Base::b[iElemIdxSLE] += quadratureFactor * qInterfaceEvaluation; - - //quadrature for int_elem q*phi_elem,i dV - for (int i = 0; i < dim - 1; i++) - { - Base::b[iElemIdxSLE + i + 1] += quadratureFactor * - (qpGlobal * iFrame[i]) * qInterfaceEvaluation; - } - } - - //TODO: fill stiffness matrix A with interface entries - - //evaluate - // int_iElem d * (K_par*grad_par(phi_iElem,i)) * grad_par(phi_iElem,j) ds - // = int_iElem d * (K_par * tau_i) * tau_j ds - //using a quadrature rule for i,j = 1,...,dim-1 - for (const auto& ip : rule_Kpar) - { - const auto& qpLocal = ip.position(); - const auto& qpGlobal = iGeo.global(qpLocal); - - const Scalar quadratureFactor = - ip.weight() * iGeo.integrationElement(qpLocal); - - for (int i = 0; i < dim-1 ; i++) - { - Coordinate K_parDotTau_i; - Base::problem_.Kparallel(qpGlobal).mv(iFrame[i], K_parDotTau_i); - - for (int j = 0; j <= i; j++) //TODO: symmetrize - { - Base::A->entry(iElemIdxSLE + i + 1, iElemIdxSLE + j + 1) += - quadratureFactor * Base::problem_.aperture(qpGlobal) - * ( K_parDotTau_i * iFrame[j] ); - } - } - } - - //TODO: check interface bilinear form - // === coupling entries === //evaluate the coupling term @@ -373,9 +319,239 @@ private: Base::A->entry(elemOutIdxSLE + dim + 1, iElemIdx) -= couplingUpdate4; Base::A->entry(iElemIdx, elemOutIdxSLE + dim + 1) -= couplingUpdate4; } + + // === interface entries === + + //fill load vector b with interface entries using a quadrature rule + for (const auto& ip : rule_qInterface) + { + //quadrature point in local and global coordinates + const auto& qpLocal = ip.position(); + const auto& qpGlobal = iGeo.global(qpLocal); + + const Scalar quadratureFactor = + ip.weight() * iGeo.integrationElement(qpLocal); + const Scalar qInterfaceEvaluation(Base::problem_.qInterface(qpGlobal)); + + //quadrature for int_elem q*phi_elem,0 dV + Base::b[iElemIdxSLE] += quadratureFactor * qInterfaceEvaluation; + + //quadrature for int_elem q*phi_elem,i dV + for (int i = 0; i < dim - 1; i++) + { + Base::b[iElemIdxSLE + i + 1] += quadratureFactor * + (qpGlobal * iFrame[i]) * qInterfaceEvaluation; + } + } + + //evaluate + // int_iElem (Kparallel * grad(phi_iElem,i)) * grad(phi_iElem,j) ds + // = int_iElem (Kpar * tau_i) * tau_j ds + //using a quadrature rule for i,j = 1,...,dim-1 + for (const auto& ip : rule_Kpar) + { + const auto& qpLocal = ip.position(); + const auto& qpGlobal = iGeo.global(qpLocal); + + const Scalar quadratureFactor = + ip.weight() * iGeo.integrationElement(qpLocal); + + for (int i = 0; i < dim-1 ; i++) + { + Coordinate KparDotTau_i; + Base::problem_.Kparallel(qpGlobal).mv(iFrame[i], KparDotTau_i); + + for (int j = 0; j <= i; j++) + { + Base::A->entry(iElemIdxSLE + i + 1, iElemIdxSLE + j + 1) += + quadratureFactor * ( KparDotTau_i * iFrame[j] ); + + if (i != j) + { + Base::A->entry(iElemIdxSLE + j + 1, iElemIdxSLE + i + 1) += + quadratureFactor * ( KparDotTau_i * iFrame[j] ); + } + } + } + } + + //iterate over all intersection with the boundary of iElem + //NOTE: only works for 2d!, otherwise quadrature required + for (const auto& intersct : intersections(iGridView_, iElem)) + { + const auto& center = intersct.geometry().center(); + const auto& normal = intersct.centerUnitOuterNormal(); + Coordinate KparDotTau_i; + + //evaluate the interface term + // int_intersct mu^Gamma * jump(phi_iElem,0) * jump(phi_iElem,0) dr + Base::A->entry(iElemIdxSLE, iElemIdxSLE) += mu; + + if (intersct.neighbor()) //inner interface edge + { + //evaluate the interface terms + // int_intersct mu^Gamma * jump(phi_iElem,i) * jump(phi_iElem,j) dr + //and + // -int_intersct avg(Kparallel * grad(phi_iElem,i)) + // * jump(phi_iElem,j) dr + //for i,j = 0,...,dim-1 and (i != 0 or j != 0) + for (int i = 0; i < dim - 1; i++) + { + Base::problem_.Kparallel(center).mv(iFrame[i], KparDotTau_i); + const Scalar centerI = center * iFrame[i]; + const Scalar interfaceUpdate1 = mu * centerI; + const Scalar interfaceUpdate2 = KparDotTau_i * normal; + const Scalar interfaceUpdate3 = + interfaceUpdate1 - 0.5 * interfaceUpdate2; + + Base::A->entry(iElemIdxSLE + i + 1, iElemIdxSLE) += + interfaceUpdate3; + Base::A->entry(iElemIdxSLE, iElemIdxSLE + i + 1) += + interfaceUpdate3; + Base::A->entry(iElemIdxSLE + i + 1, iElemIdxSLE + i + 1) += + centerI * (interfaceUpdate1 - interfaceUpdate2); + + for (int j = 0; j < i; i++) + { //NOTE: only relevant for n=3, requires quadrature rule for n=3 + Coordinate KparDotTau_j; + Base::problem_.Kparallel(center).mv(iFrame[j], KparDotTau_j); + const Scalar interfaceUpdate4 = + (center * iFrame[j]) * interfaceUpdate1 + - 0.5 * (interfaceUpdate2 * (center * iFrame[j]) + + (KparDotTau_j * normal) * centerI); + + Base::A->entry(iElemIdxSLE + i + 1, iElemIdxSLE + j + 1) += + interfaceUpdate4; + Base::A->entry(iElemIdxSLE + j + 1, iElemIdxSLE + i + 1) += + interfaceUpdate4; + } + } + + //index of the neighboring element + const int neighborIdx = iMapper_.index(intersct.outside()); + + if (neighborIdx > iElemIdx) + { //make sure that each interface edge is considered only once + continue; + } + + const int neighborIdxSLE = + (dim + 1) * Base::gridView_.size(0) + dim * neighborIdx; + + //evaluate the interface terms + // int_intersct mu^Gamma * jump(phi_iElem,i) * jump(phi_neighbor,j) dr + //and + // -int_intersct avg(Kparallel * grad(phi_iElem,i)) + // * jump(phi_neighbor,j) dr + //resp. + // -int_intersct avg(Kparallel * grad(phi_neighbor,i)) + // * jump(phi_iElem,j) dr + //for i,j = 0,...,dim-1 + Base::A->entry(iElemIdxSLE, neighborIdxSLE) += -mu; + Base::A->entry(neighborIdxSLE, iElemIdxSLE) += -mu; + + for (int i = 0; i < dim - 1; i++) + { + Base::problem_.Kparallel(center).mv(iFrame[i], KparDotTau_i); + const Scalar centerI = center * iFrame[i]; + const Scalar interfaceUpdate1 = mu * centerI; + const Scalar interfaceUpdate2 = KparDotTau_i * normal; + const Scalar interfaceUpdate3 = + -interfaceUpdate1 + 0.5 * interfaceUpdate2; + const Scalar interfaceUpdate4 = + -interfaceUpdate1 - 0.5 * interfaceUpdate2; + + Base::A->entry(iElemIdxSLE + i + 1, neighborIdxSLE) += + interfaceUpdate3; + Base::A->entry(neighborIdxSLE, iElemIdxSLE + i + 1) += + interfaceUpdate3; + Base::A->entry(neighborIdxSLE + i + 1, iElemIdxSLE) += + -interfaceUpdate4; + Base::A->entry(iElemIdxSLE, neighborIdxSLE + i + 1) += + -interfaceUpdate4; + + Base::A->entry(iElemIdxSLE + i + 1, neighborIdxSLE + i + 1) += + -centerI * interfaceUpdate1; + + for (int j = 0; j < i; j++) + { //NOTE: only relevant for n=3, requires quadrature rule + Coordinate KparDotTau_j; + Base::problem_.Kparallel(center).mv(iFrame[j], KparDotTau_j); + const Scalar interfaceUpdate5 = + (center * iFrame[j]) * interfaceUpdate1; + const Scalar interfaceUpdate6 = interfaceUpdate2 * + (center * iFrame[j]) - (KparDotTau_j * normal) * centerI; + const Scalar interfaceUpdate7 = + -interfaceUpdate5 - 0.5 * interfaceUpdate6; + const Scalar interfaceUpdate8 = + -interfaceUpdate5 + 0.5 * interfaceUpdate6; + + Base::A->entry(iElemIdxSLE + i + 1, neighborIdxSLE + j + 1) += + interfaceUpdate7; + Base::A->entry(neighborIdxSLE + j + 1, iElemIdxSLE + i + 1) += + interfaceUpdate7; + Base::A->entry(iElemIdxSLE + j + 1, neighborIdxSLE + i + 1) += + interfaceUpdate8; + Base::A->entry(neighborIdxSLE + i + 1, iElemIdxSLE + j + 1) += + interfaceUpdate8; + } + } + } + else //boundary interface edge + { + //evaluate the interface terms + // int_intersct mu^Gamma * phi_iElem,i * phi_iElem,j dr + //and + // -int_intersct phi_iElem,j * (Kparallel * grad(phi_iElem,i)) + // * normal dr + //for i,j = 0,...,dim-1 and (i != 0 or j != 0) + //and add interface boundary terms to the load vector b: + // int_intersct d * g * (mu * phi_iElem,i + + // (Kparallel * grad(phi_i,iElem)) * normal) dr + //for i = 0,...,dim-1 + const Scalar dgGamma = Base::problem_.aperture(center) + * Base::problem_.boundary(center); + Base::b[iElemIdxSLE] += mu * dgGamma; + + for (int i = 0; i < dim - 1; i++) + { + Base::problem_.Kparallel(center).mv(iFrame[i], KparDotTau_i); + const Scalar centerI = center * iFrame[i]; + const Scalar interfaceUpdate1 = mu * centerI; + const Scalar interfaceUpdate2 = KparDotTau_i * normal; + const Scalar interfaceUpdate3 = + interfaceUpdate1 - interfaceUpdate2; + + Base::b[iElemIdxSLE] += dgGamma * (mu * centerI - interfaceUpdate2); + + Base::A->entry(iElemIdxSLE + i + 1, iElemIdxSLE) += + interfaceUpdate3; + Base::A->entry(iElemIdxSLE, iElemIdxSLE + i + 1) += + interfaceUpdate3; + Base::A->entry(iElemIdxSLE + i + 1, iElemIdxSLE + i + 1) += + centerI * (interfaceUpdate1 - 2.0 * interfaceUpdate2); + + for (int j = 0; j < i; i++) + { //NOTE: only relevant for n=3, requires quadrature rule for n=3 + Coordinate KparDotTau_j; + Base::problem_.Kparallel(center).mv(iFrame[j], KparDotTau_j); + const Scalar interfaceUpdate4 = + (center * iFrame[j]) * interfaceUpdate1 + - (interfaceUpdate2 * (center * iFrame[j]) + + (KparDotTau_j * normal) * centerI); + + Base::A->entry(iElemIdxSLE + i + 1, iElemIdxSLE + j + 1) += + interfaceUpdate4; + Base::A->entry(iElemIdxSLE + j + 1, iElemIdxSLE + i + 1) += + interfaceUpdate4; + } + } + } + } } } +//NOTE: MMesh ensures unique intersection, i.e. unique normal, add assert index(inside) < index(outside) //returns a basis of the interface coordinate system //2d: tau = [ normal[1], // -normal[0] ] @@ -399,39 +575,16 @@ private: tau[i] += normal[j]; tau[j] = -normal[i]; } - tau /= tau.two_norm(); + + if constexpr (dim != 2) + { + tau /= tau.two_norm(); + } iBasis[i] = tau; } return iBasis; - -/* //NOTE: which version is better? - if constexpr (dim == 2) - { - Coordinate tau; - tau[0] = normal[1]; - tau[1] = -normal[0]; - - interfaceFrame = {tau}; - } - else if constexpr (dim == 3) - { - Coordinate tau1; - tau1[0] = normal[1] + normal[2]; - tau1[1] = -normal[0]; - tau1[2] = -normal[0]; - Coordinate tau2; - tau2[0] = -normal[1]; - tau2[1] = normal[0] + normal[2]; - tau2[2] = -normal[1]; - interfaceFrame = {tau1, tau2}; - } - else - { - DUNE_THROW(Dune::Exception, - "Invalid dimension: dim = " + std::to_string(dim)); - }*/ } }; diff --git a/dune/mmdg/problems/coupleddgproblem.hh b/dune/mmdg/problems/coupleddgproblem.hh index ad26400cc62d663d9948d01156fa6f963c736c30..6e23c898c3e04ae11b07ccb1e64f5ef8ce770a3a 100644 --- a/dune/mmdg/problems/coupleddgproblem.hh +++ b/dune/mmdg/problems/coupleddgproblem.hh @@ -61,7 +61,7 @@ class CoupledDGProblem : public DGProblem<Vector, Scalar> } //returns the recommended quadrature order to compute an integral - //over d(x) * Kparallel(x) + //over Kparallel(x) virtual int quadratureOrder_Kparallel () const { return 1;