BOOST_AUTO_TEST_CASE_TEMPLATE(boundary_operator_agrees_with_2x2_blocked_boundary_operator, ValueType, result_types) { typedef ValueType RT; typedef typename ScalarTraits<ValueType>::RealType RealType; typedef RealType BFT; typedef Bempp::DefaultDirectSolver<BFT, RT> DirectSolver; const RealType solverTol = 1e-5; Laplace3dDirichletFixture<BFT, RT> fixture; arma::Col<RT> solutionVectorNonblocked; // Solve using nonblocked operator { DirectSolver solver(fixture.lhsOp); Solution<BFT, RT> solution = solver.solve(fixture.rhs); solutionVectorNonblocked = solution.gridFunction().coefficients(); } // Solve using 2x2 ([A, 0 * A; 0, A]) blocked operator { BlockedOperatorStructure<BFT, RT> structure; structure.setBlock(0, 0, fixture.lhsOp); structure.setBlock(0, 1, 0. * fixture.lhsOp); structure.setBlock(1, 1, fixture.lhsOp); BlockedBoundaryOperator<BFT, RT> lhsBlockedOp(structure); std::vector<GridFunction<BFT, RT> > blockedRhs(2); blockedRhs[0] = fixture.rhs; blockedRhs[1] = 2. * fixture.rhs; DirectSolver solver(lhsBlockedOp); BlockedSolution<BFT, RT> solution = solver.solve(blockedRhs); arma::Col<RT> solutionVectorBlock0 = solution.gridFunction(0).coefficients(); arma::Col<RT> solutionVectorBlock1 = solution.gridFunction(1).coefficients() / 2.; BOOST_CHECK(check_arrays_are_close<ValueType>( solutionVectorNonblocked, solutionVectorBlock0, solverTol * 10)); BOOST_CHECK(check_arrays_are_close<ValueType>( solutionVectorNonblocked, solutionVectorBlock1, solverTol * 10)); } }
BlockedBoundaryOperator<BasisFunctionType, ResultType> modifiedHelmholtz3dInteriorCalderonProjector( const shared_ptr<const Context<BasisFunctionType,ResultType> >& context, const shared_ptr<const Space<BasisFunctionType> >& hminusSpace, const shared_ptr<const Space<BasisFunctionType> >& hplusSpace, KernelType waveNumber, const std::string& label, bool useInterpolation, int interpPtsPerWavelength) { typedef BoundaryOperator<BasisFunctionType,ResultType> BdOp; shared_ptr<const Space<BasisFunctionType> > internalHplusSpace = hplusSpace->discontinuousSpace(hplusSpace); BdOp internalSlp = modifiedHelmholtz3dSingleLayerBoundaryOperator(context,internalHplusSpace,internalHplusSpace,internalHplusSpace,waveNumber,label+"_slp",SYMMETRIC, useInterpolation,interpPtsPerWavelength); BdOp dlp = modifiedHelmholtz3dDoubleLayerBoundaryOperator(context,hplusSpace,hplusSpace,hminusSpace,waveNumber,label+"_dlp",NO_SYMMETRY, useInterpolation,interpPtsPerWavelength); BdOp adjDlp = adjoint(dlp); BdOp hyp = modifiedHelmholtz3dHypersingularBoundaryOperator(context,hplusSpace,hminusSpace,hplusSpace,waveNumber,label+"_hyp",SYMMETRIC, useInterpolation,interpPtsPerWavelength,internalSlp); BdOp idSpaceTransformation1 = identityOperator(context,hminusSpace,internalHplusSpace,internalHplusSpace); BdOp idSpaceTransformation2 = identityOperator(context,internalHplusSpace,hplusSpace,hminusSpace); BdOp idDouble = identityOperator(context,hplusSpace,hplusSpace,hminusSpace); BdOp idAdjDouble = identityOperator(context,hminusSpace,hminusSpace,hplusSpace); // Now Assemble the entries of the Calderon Projector BlockedOperatorStructure<BasisFunctionType,ResultType> structure; structure.setBlock(0,0,.5*idDouble-dlp); structure.setBlock(0,1,idSpaceTransformation2*internalSlp*idSpaceTransformation1); structure.setBlock(1,0,hyp); structure.setBlock(1,1,.5*idAdjDouble+adjDlp); return BlockedBoundaryOperator<BasisFunctionType,ResultType>(structure); }
int main(int argc, char* argv[]) { // Physical parameters, general const BFT c0 = 0.3; // speed of light in vacuum [mm/ps] BFT refind = 1.4; // refractive index BFT alpha = A_Keijzer(refind); // boundary term BFT c = c0/refind; // speed of light in medium [mm/ps] BFT freq = 100*1e6; // modulation frequency [Hz] BFT omega = 2.0*M_PI * freq*1e-12; // modulation frequency [cycles/ps] // Physical parameters, outer region BFT mua1 = 0.01; // absorption coefficient BFT mus1 = 1.0; // scattering coefficient BFT kappa1 = 1.0/(3.0*(mua1+mus1)); // diffusion coefficient RT waveNumber1 = sqrt (RT(mua1/kappa1, omega/(c*kappa1))); // outer region // Physical parameters, inner region BFT mua2 = 0.02; // absorption coefficient BFT mus2 = 0.5; // scattering coefficient BFT kappa2 = 1.0/(3.0*(mua2+mus2)); // diffusion coefficient RT waveNumber2 = sqrt (RT(mua2/kappa2, omega/(c*kappa2))); // outer region // Create sphere meshes on the fly shared_ptr<Grid> grid1 = CreateSphere(25.0, 1.0); shared_ptr<Grid> grid2 = CreateSphere(15.0, 1.0); // Initialize the spaces PiecewiseLinearContinuousScalarSpace<BFT> HplusHalfSpace1(grid1); PiecewiseLinearContinuousScalarSpace<BFT> HplusHalfSpace2(grid2); // Define some default options. AssemblyOptions assemblyOptions; // We want to use ACA AcaOptions acaOptions; // Default parameters for ACA acaOptions.eps = 1e-5; assemblyOptions.switchToAcaMode(acaOptions); // Define the standard integration factory AccuracyOptions accuracyOptions; accuracyOptions.doubleRegular.setRelativeQuadratureOrder(2); accuracyOptions.singleRegular.setRelativeQuadratureOrder(1); NumericalQuadratureStrategy<BFT, RT> quadStrategy(accuracyOptions); Context<BFT, RT> context(make_shared_from_ref(quadStrategy), assemblyOptions); // We need the single layer, double layer, and the identity operator // mesh1 x mesh1 BoundaryOperator<BFT, RT> slp11 = modifiedHelmholtz3dSingleLayerBoundaryOperator<BFT, RT, RT>( SHARED(context), SHARED(HplusHalfSpace1), SHARED(HplusHalfSpace1), SHARED(HplusHalfSpace1), waveNumber1); BoundaryOperator<BFT, RT> dlp11 = modifiedHelmholtz3dDoubleLayerBoundaryOperator<BFT, RT, RT>( SHARED(context), SHARED(HplusHalfSpace1), SHARED(HplusHalfSpace1), SHARED(HplusHalfSpace1), waveNumber1); BoundaryOperator<BFT, RT> id11 = identityOperator<BFT, RT>( SHARED(context), SHARED(HplusHalfSpace1), SHARED(HplusHalfSpace1), SHARED(HplusHalfSpace1)); // mesh2 x mesh2, wavenumber 1 BoundaryOperator<BFT, RT> slp22_w1 = modifiedHelmholtz3dSingleLayerBoundaryOperator<BFT, RT, RT>( SHARED(context), SHARED(HplusHalfSpace2), SHARED(HplusHalfSpace2), SHARED(HplusHalfSpace2), waveNumber1); BoundaryOperator<BFT, RT> dlp22_w1 = modifiedHelmholtz3dDoubleLayerBoundaryOperator<BFT, RT, RT>( SHARED(context), SHARED(HplusHalfSpace2), SHARED(HplusHalfSpace2), SHARED(HplusHalfSpace2), waveNumber1); BoundaryOperator<BFT, RT> id22 = identityOperator<BFT, RT>( SHARED(context), SHARED(HplusHalfSpace2), SHARED(HplusHalfSpace2), SHARED(HplusHalfSpace2)); // mesh2 x mesh2, wavenumber 2 BoundaryOperator<BFT, RT> slp22_w2 = modifiedHelmholtz3dSingleLayerBoundaryOperator<BFT, RT, RT>( SHARED(context), SHARED(HplusHalfSpace2), SHARED(HplusHalfSpace2), SHARED(HplusHalfSpace2), waveNumber2); BoundaryOperator<BFT, RT> dlp22_w2 = modifiedHelmholtz3dDoubleLayerBoundaryOperator<BFT, RT, RT>( SHARED(context), SHARED(HplusHalfSpace2), SHARED(HplusHalfSpace2), SHARED(HplusHalfSpace2), waveNumber2); // mesh1 x mesh2 BoundaryOperator<BFT, RT> slp12 = modifiedHelmholtz3dSingleLayerBoundaryOperator<BFT, RT, RT>( SHARED(context), SHARED(HplusHalfSpace2), SHARED(HplusHalfSpace1), SHARED(HplusHalfSpace1), waveNumber1); BoundaryOperator<BFT, RT> dlp12 = modifiedHelmholtz3dDoubleLayerBoundaryOperator<BFT, RT, RT>( SHARED(context), SHARED(HplusHalfSpace2), SHARED(HplusHalfSpace1), SHARED(HplusHalfSpace1), waveNumber1); // mesh2 x mesh1 BoundaryOperator<BFT, RT> slp21 = modifiedHelmholtz3dSingleLayerBoundaryOperator<BFT, RT, RT>( SHARED(context), SHARED(HplusHalfSpace1), SHARED(HplusHalfSpace2), SHARED(HplusHalfSpace2), waveNumber1); BoundaryOperator<BFT, RT> dlp21 = modifiedHelmholtz3dDoubleLayerBoundaryOperator<BFT, RT, RT>( SHARED(context), SHARED(HplusHalfSpace1), SHARED(HplusHalfSpace2), SHARED(HplusHalfSpace2), waveNumber1); BFT scale = 1.0/(2.0*alpha*kappa1); BoundaryOperator<BFT, RT> lhs_k11 = 0.5*id11 + dlp11 + scale*slp11; BoundaryOperator<BFT, RT> lhs_k12 = -1.0*dlp12; // sign flipped to accommodate normal direction BoundaryOperator<BFT, RT> lhs_k13 = -(1.0/kappa1)*slp12; BoundaryOperator<BFT, RT> lhs_k21 = dlp21 + scale*slp21; BoundaryOperator<BFT, RT> lhs_k22 = 0.5*id22 - dlp22_w1; // sign flipped to accommodate normal direction BoundaryOperator<BFT, RT> lhs_k23 = -(1.0/kappa1)*slp22_w1; // BoundaryOperator<BFT, RT> lhs_k31 -- empty BoundaryOperator<BFT, RT> lhs_k32 = 0.5*id22 + dlp22_w2; BoundaryOperator<BFT, RT> lhs_k33 = (1.0/kappa2) * slp22_w2; BlockedOperatorStructure<BFT, RT> structure; structure.setBlock(0, 0, lhs_k11); structure.setBlock(0, 1, lhs_k12); structure.setBlock(0, 2, lhs_k13); structure.setBlock(1, 0, lhs_k21); structure.setBlock(1, 1, lhs_k22); structure.setBlock(1, 2, lhs_k23); // structure.setBlock(2, 0, ...); -- empty structure.setBlock(2, 1, lhs_k32); structure.setBlock(2, 2, lhs_k33); BlockedBoundaryOperator<BFT, RT> blockedOp(structure); // Grid functions for the RHS // TODO: remove the necessity of creating "dummy" functions // corresponding to zero blocks. BoundaryOperator<BFT, RT> rhs1 = scale*slp11; BoundaryOperator<BFT, RT> rhs2 = scale*slp21; std::vector<GridFunction<BFT, RT> > blockedRhs(3); blockedRhs[0] = rhs1 * GridFunction<BFT, RT>( SHARED(context), SHARED(HplusHalfSpace1), SHARED(HplusHalfSpace1), //surfaceNormalIndependentFunction(NullFunctor())); surfaceNormalIndependentFunction(MyFunctor(waveNumber1))); blockedRhs[1] = rhs2 * GridFunction<BFT, RT>( SHARED(context), SHARED(HplusHalfSpace1), SHARED(HplusHalfSpace1), //surfaceNormalIndependentFunction(NullFunctor())); surfaceNormalIndependentFunction(MyFunctor(waveNumber1))); blockedRhs[2] = GridFunction<BFT, RT>( SHARED(context), SHARED(HplusHalfSpace2), SHARED(HplusHalfSpace2), //surfaceNormalIndependentFunction(MyFunctor(waveNumber2))); surfaceNormalIndependentFunction(NullFunctor())); // Initialize the solver const double solverTol = 1e-10; DefaultIterativeSolver<BFT, RT> solver(blockedOp); solver.initializeSolver(defaultGmresParameterList(solverTol)); // Solve BlockedSolution<BFT, RT> solution = solver.solve(blockedRhs); std::cout << solution.solverMessage() << std::endl; arma::Col<RT> solutionVectorBlock1 = solution.gridFunction(0).coefficients(); arma::Col<RT> solutionVectorBlock2 = solution.gridFunction(1).coefficients(); arma::Col<RT> solutionVectorBlock3 = solution.gridFunction(2).coefficients(); arma::diskio::save_raw_ascii(solutionVectorBlock1, "sol1.txt"); arma::diskio::save_raw_ascii(solutionVectorBlock2, "sol2.txt"); arma::diskio::save_raw_ascii(solutionVectorBlock3, "sol3.txt"); solution.gridFunction(0).exportToVtk(VtkWriter::VERTEX_DATA, "gf1", "gf1"); solution.gridFunction(1).exportToVtk(VtkWriter::VERTEX_DATA, "gf2", "gf2"); solution.gridFunction(2).exportToVtk(VtkWriter::VERTEX_DATA, "gf3", "gf3"); }
// Constructor for blocked operators Impl(const BlockedBoundaryOperator<BasisFunctionType, ResultType>& op_, ConvergenceTestMode::Mode mode_) : op(op_), mode(mode_) { typedef BlockedBoundaryOperator<BasisFunctionType, ResultType> BoundaryOp; typedef Solver<BasisFunctionType, ResultType> Solver_; const BoundaryOp& boundaryOp = boost::get<BoundaryOp>(op); if (mode == ConvergenceTestMode::TEST_CONVERGENCE_IN_DUAL_TO_RANGE) { if (boundaryOp.totalGlobalDofCountInDomains() != boundaryOp.totalGlobalDofCountInDualsToRanges()) throw std::invalid_argument("DefaultIterativeSolver::Impl::Impl(): " "non-square system provided"); solverWrapper.reset( new BelosSolverWrapper<ResultType>( Teuchos::rcp<const Thyra::LinearOpBase<ResultType> >( boundaryOp.weakForm()))); } else if (mode == ConvergenceTestMode::TEST_CONVERGENCE_IN_RANGE) { if (boundaryOp.totalGlobalDofCountInDomains() != boundaryOp.totalGlobalDofCountInRanges()) throw std::invalid_argument("DefaultIterativeSolver::Impl::Impl(): " "non-square system provided"); // Construct a block-diagonal operator composed of pseudoinverses // for appropriate spaces BlockedOperatorStructure<BasisFunctionType, ResultType> pinvIdStructure; size_t rowCount = boundaryOp.rowCount(); size_t columnCount = boundaryOp.columnCount(); for (size_t row = 0; row < rowCount; ++row) { // Find the first non-zero block in row #row and retrieve its context shared_ptr<const Context<BasisFunctionType, ResultType> > context; for (int col = 0; col < columnCount; ++col) if (boundaryOp.block(row, col).context()) { context = boundaryOp.block(row, col).context(); break; } assert(context); BoundaryOperator<BasisFunctionType, ResultType> id = identityOperator( context, boundaryOp.range(row), boundaryOp.range(row), boundaryOp.dualToRange(row)); pinvIdStructure.setBlock(row, row, pseudoinverse(id)); } pinvId = BlockedBoundaryOperator<BasisFunctionType, ResultType>( pinvIdStructure); shared_ptr<DiscreteBoundaryOperator<ResultType> > totalBoundaryOp = boost::make_shared<DiscreteBoundaryOperatorComposition<ResultType> >( boost::get<BoundaryOp>(pinvId).weakForm(), boundaryOp.weakForm()); solverWrapper.reset( new BelosSolverWrapper<ResultType>( Teuchos::rcp<const Thyra::LinearOpBase<ResultType> >( totalBoundaryOp))); } else throw std::invalid_argument( "DefaultIterativeSolver::DefaultIterativeSolver(): " "invalid convergence test mode"); }