BOOST_AUTO_TEST_CASE_TEMPLATE(symmetric_matches_nonsymmetric_in_aca_mode, ValueType, result_types) { typedef ValueType RT; typedef typename Fiber::ScalarTraits<ValueType>::RealType RealType; typedef RealType BFT; if (boost::is_same<RT, std::complex<float> >::value) { // The AHMED support for single-precision complex symmetric matrices // is broken BOOST_CHECK(true); return; } GridParameters params; params.topology = GridParameters::TRIANGULAR; shared_ptr<Grid> grid = GridFactory::importGmshGrid( params, "../../examples/meshes/sphere-h-0.4.msh", false /* verbose */); PiecewiseLinearContinuousScalarSpace<BFT> pwiseLinears(grid); PiecewiseConstantScalarSpace<BFT> pwiseConstants(grid); AssemblyOptions assemblyOptions; assemblyOptions.setVerbosityLevel(VerbosityLevel::LOW); AcaOptions acaOptions; acaOptions.minimumBlockSize = 4; assemblyOptions.switchToAcaMode(acaOptions); AccuracyOptions accuracyOptions; accuracyOptions.doubleRegular.setRelativeQuadratureOrder(4); accuracyOptions.doubleSingular.setRelativeQuadratureOrder(2); NumericalQuadratureStrategy<BFT, RT> quadStrategy(accuracyOptions); Context<BFT, RT> context(make_shared_from_ref(quadStrategy), assemblyOptions); const RT waveNumber = initWaveNumber<RT>(); BoundaryOperator<BFT, RT> opNonsymmetric = modifiedHelmholtz3dSingleLayerBoundaryOperator<BFT, RT, RT>( make_shared_from_ref(context), make_shared_from_ref(pwiseLinears), make_shared_from_ref(pwiseConstants), make_shared_from_ref(pwiseLinears), waveNumber, "", NO_SYMMETRY); BoundaryOperator<BFT, RT> opSymmetric = modifiedHelmholtz3dSingleLayerBoundaryOperator<BFT, RT, RT>( make_shared_from_ref(context), make_shared_from_ref(pwiseLinears), make_shared_from_ref(pwiseConstants), make_shared_from_ref(pwiseLinears), waveNumber, "", SYMMETRIC); arma::Mat<RT> matNonsymmetric = opNonsymmetric.weakForm()->asMatrix(); arma::Mat<RT> matSymmetric = opSymmetric.weakForm()->asMatrix(); BOOST_CHECK(check_arrays_are_close<RT>( matNonsymmetric, matSymmetric, 2 * acaOptions.eps)); }
DefaultLocalAssemblerForIntegralOperatorsOnSurfacesManager( bool cacheSingularIntegrals) { // Create a Bempp grid shared_ptr<Grid> grid = createGrid(); // These important thing is that the domain and dualToRange spaces are // different piecewiseConstantSpace = std::auto_ptr<PiecewiseConstantSpace>( new PiecewiseConstantSpace(grid)); piecewiseLinearSpace = std::auto_ptr<PiecewiseLinearSpace>( new PiecewiseLinearSpace(grid)); op = std::auto_ptr<Operator>(new Operator( make_shared_from_ref(*piecewiseConstantSpace), make_shared_from_ref(*piecewiseLinearSpace), make_shared_from_ref(*piecewiseLinearSpace), "SLP")); // Construct local assembler Fiber::AccuracyOptions options; options.doubleRegular.setRelativeQuadratureOrder(1); quadStrategy = std::auto_ptr<QuadratureStrategy>(new QuadratureStrategy); AssemblyOptions assemblyOptions; assemblyOptions.setVerbosityLevel(VerbosityLevel::LOW); assemblyOptions.enableSingularIntegralCaching(cacheSingularIntegrals); assembler = op->makeAssembler(*quadStrategy, assemblyOptions); }
BOOST_AUTO_TEST_CASE_TEMPLATE(L2Norm_works_for_constant_function_and_piecewise_constants, ResultType, result_types) { typedef ResultType RT; typedef typename ScalarTraits<RT>::RealType BFT; typedef typename ScalarTraits<RT>::RealType CT; GridParameters params; params.topology = GridParameters::TRIANGULAR; shared_ptr<Grid> grid = GridFactory::importGmshGrid( params, "../../meshes/sphere-h-0.1.msh", false /* verbose */); shared_ptr<Space<BFT> > space( new PiecewiseConstantScalarSpace<BFT>(grid)); AccuracyOptions accuracyOptions; shared_ptr<NumericalQuadratureStrategy<BFT, RT> > quadStrategy( new NumericalQuadratureStrategy<BFT, RT>(accuracyOptions)); AssemblyOptions assemblyOptions; assemblyOptions.setVerbosityLevel(VerbosityLevel::LOW); shared_ptr<Context<BFT, RT> > context( new Context<BFT, RT>(quadStrategy, assemblyOptions)); Bempp::GridFunction<BFT, RT> fun(context, space, space, surfaceNormalIndependentFunction( ConstantFunction<RT>())); CT norm = fun.L2Norm(); CT expectedNorm = sqrt(2. * 2. * 4. * M_PI); BOOST_CHECK_CLOSE(norm, expectedNorm, 1 /* percent */); }
BOOST_AUTO_TEST_CASE_TEMPLATE(cubic_function_can_be_expanded_in_cubic_space, ResultType, result_types) { typedef ResultType RT; typedef typename ScalarTraits<RT>::RealType BFT; typedef typename ScalarTraits<RT>::RealType CT; GridParameters params; params.topology = GridParameters::TRIANGULAR; shared_ptr<Grid> grid = GridFactory::importGmshGrid( params, "../../examples/meshes/sphere-h-0.2.msh", false /* verbose */); shared_ptr<Space<BFT> > space( new PiecewisePolynomialDiscontinuousScalarSpace<BFT>(grid, 3)); AccuracyOptions accuracyOptions; accuracyOptions.singleRegular.setRelativeQuadratureOrder(2); shared_ptr<NumericalQuadratureStrategy<BFT, RT> > quadStrategy( new NumericalQuadratureStrategy<BFT, RT>(accuracyOptions)); AssemblyOptions assemblyOptions; assemblyOptions.setVerbosityLevel(VerbosityLevel::LOW); shared_ptr<Context<BFT, RT> > context( new Context<BFT, RT>(quadStrategy, assemblyOptions)); GridFunction<BFT, RT> function( context, space, space, surfaceNormalIndependentFunction(CubicFunction<RT>())); CT absoluteError, relativeError; estimateL2Error( function, surfaceNormalIndependentFunction(CubicFunction<RT>()), *quadStrategy, absoluteError, relativeError); BOOST_CHECK_SMALL(relativeError, 1000 * std::numeric_limits<CT>::epsilon() /* percent */); }
inline bool shouldUseBlasInQuadrature(const AssemblyOptions& assemblyOptions, const Space<BasisFunctionType>& domain, const Space<BasisFunctionType>& dualToRange) { return assemblyOptions.isBlasEnabledInQuadrature() == AssemblyOptions::YES || (assemblyOptions.isBlasEnabledInQuadrature() == AssemblyOptions::AUTO && (maximumShapesetOrder(domain) >= 2 || maximumShapesetOrder(dualToRange) >= 2)); }
BOOST_AUTO_TEST_CASE_TEMPLATE(aca_of_synthetic_maxwell_single_layer_operator_agrees_with_dense_assembly_in_asymmetric_case, ValueType, complex_result_types) { typedef ValueType RT; typedef typename ScalarTraits<ValueType>::RealType RealType; typedef RealType BFT; GridParameters params; params.topology = GridParameters::TRIANGULAR; shared_ptr<Grid> grid = GridFactory::importGmshGrid( params, "../../meshes/sphere-h-0.4.msh", false /* verbose */); RT waveNumber = initWaveNumber<RT>(); shared_ptr<Space<BFT> > vectorPwiseLinears( new RaviartThomas0VectorSpace<BFT>(grid)); shared_ptr<Space<BFT> > vectorPwiseLinears2( new RaviartThomas0VectorSpace<BFT>(grid)); AccuracyOptions accuracyOptions; accuracyOptions.doubleRegular.setRelativeQuadratureOrder(2); accuracyOptions.singleRegular.setRelativeQuadratureOrder(2); shared_ptr<NumericalQuadratureStrategy<BFT, RT> > quadStrategy( new NumericalQuadratureStrategy<BFT, RT>(accuracyOptions)); AssemblyOptions assemblyOptionsDense; assemblyOptionsDense.setVerbosityLevel(VerbosityLevel::LOW); shared_ptr<Context<BFT, RT> > contextDense( new Context<BFT, RT>(quadStrategy, assemblyOptionsDense)); BoundaryOperator<BFT, RT> opDense = maxwell3dSingleLayerBoundaryOperator<BFT>( contextDense, vectorPwiseLinears, vectorPwiseLinears, vectorPwiseLinears, waveNumber); arma::Mat<RT> weakFormDense = opDense.weakForm()->asMatrix(); AssemblyOptions assemblyOptionsAca; assemblyOptionsAca.setVerbosityLevel(VerbosityLevel::LOW); AcaOptions acaOptions; acaOptions.mode = AcaOptions::LOCAL_ASSEMBLY; assemblyOptionsAca.switchToAcaMode(acaOptions); shared_ptr<Context<BFT, RT> > contextAca( new Context<BFT, RT>(quadStrategy, assemblyOptionsAca)); // Internal domain different from dualToRange BoundaryOperator<BFT, RT> opAca = maxwell3dSingleLayerBoundaryOperator<BFT>( contextAca, vectorPwiseLinears, vectorPwiseLinears, vectorPwiseLinears2, waveNumber); arma::Mat<RT> weakFormAca = opAca.weakForm()->asMatrix(); BOOST_CHECK(check_arrays_are_close<ValueType>( weakFormDense, weakFormAca, 2. * acaOptions.eps)); }
BOOST_AUTO_TEST_CASE_TEMPLATE(interpolated_matches_noniterpolated, BasisFunctionType, basis_function_types) { typedef BasisFunctionType BFT; typedef typename Fiber::ScalarTraits<BFT>::ComplexType RT; typedef typename Fiber::ScalarTraits<BFT>::ComplexType KT; typedef typename Fiber::ScalarTraits<BFT>::RealType CT; GridParameters params; params.topology = GridParameters::TRIANGULAR; shared_ptr<Grid> grid = GridFactory::importGmshGrid( params, "meshes/two_disjoint_triangles.msh", false /* verbose */); PiecewiseLinearContinuousScalarSpace<BFT> pwiseLinears(grid); PiecewiseConstantScalarSpace<BFT> pwiseConstants(grid); AssemblyOptions assemblyOptions; assemblyOptions.setVerbosityLevel(VerbosityLevel::LOW); AccuracyOptions accuracyOptions; accuracyOptions.doubleRegular.setAbsoluteQuadratureOrder(5); accuracyOptions.doubleSingular.setAbsoluteQuadratureOrder(5); NumericalQuadratureStrategy<BFT, RT> quadStrategy(accuracyOptions); Context<BFT, RT> context(make_shared_from_ref(quadStrategy), assemblyOptions); const KT waveNumber(3.23, 0.31); BoundaryOperator<BFT, RT> opNoninterpolated = modifiedHelmholtz3dSingleLayerBoundaryOperator<BFT, KT, RT>( make_shared_from_ref(context), make_shared_from_ref(pwiseLinears), make_shared_from_ref(pwiseLinears), make_shared_from_ref(pwiseLinears), waveNumber, "", NO_SYMMETRY, false); BoundaryOperator<BFT, RT> opInterpolated = modifiedHelmholtz3dSingleLayerBoundaryOperator<BFT, KT, RT>( make_shared_from_ref(context), make_shared_from_ref(pwiseLinears), make_shared_from_ref(pwiseLinears), make_shared_from_ref(pwiseLinears), waveNumber, "", NO_SYMMETRY, true); arma::Mat<RT> matNoninterpolated = opNoninterpolated.weakForm()->asMatrix(); arma::Mat<RT> matInterpolated = opInterpolated.weakForm()->asMatrix(); const CT eps = std::numeric_limits<CT>::epsilon(); BOOST_CHECK(check_arrays_are_close<RT>( matNoninterpolated, matInterpolated, 100 * eps)); }
inline bool shouldUseBlasInQuadrature(const AssemblyOptions &assemblyOptions, const Space<BasisFunctionType> &domain, const Space<BasisFunctionType> &dualToRange) { // Disabled as BLAS quadrature causes crashes in the hypersingular operator for // unknown reasons. return assemblyOptions.isBlasEnabledInQuadrature() == AssemblyOptions::YES || (assemblyOptions.isBlasEnabledInQuadrature() == AssemblyOptions::AUTO && (maximumShapesetOrder(domain) >= 2 || maximumShapesetOrder(dualToRange) >= 2)); }
BOOST_AUTO_TEST_CASE_TEMPLATE(aca_of_synthetic_modified_helmholtz_hypersingular_operator_agrees_with_dense_assembly_in_symmetric_case, ValueType, result_types) { typedef ValueType RT; typedef typename ScalarTraits<ValueType>::RealType RealType; typedef RealType BFT; GridParameters params; params.topology = GridParameters::TRIANGULAR; shared_ptr<Grid> grid = GridFactory::importGmshGrid( params, "../../meshes/sphere-h-0.4.msh", false /* verbose */); RT waveNumber = initWaveNumber<RT>(); shared_ptr<Space<BFT> > pwiseConstants( new PiecewiseConstantScalarSpace<BFT>(grid)); shared_ptr<Space<BFT> > pwiseLinears( new PiecewiseLinearContinuousScalarSpace<BFT>(grid)); AccuracyOptions accuracyOptions; accuracyOptions.doubleRegular.setRelativeQuadratureOrder(2); accuracyOptions.singleRegular.setRelativeQuadratureOrder(2); shared_ptr<NumericalQuadratureStrategy<BFT, RT> > quadStrategy( new NumericalQuadratureStrategy<BFT, RT>(accuracyOptions)); AssemblyOptions assemblyOptionsDense; assemblyOptionsDense.setVerbosityLevel(VerbosityLevel::LOW); shared_ptr<Context<BFT, RT> > contextDense( new Context<BFT, RT>(quadStrategy, assemblyOptionsDense)); BoundaryOperator<BFT, RT> opDense = modifiedHelmholtz3dHypersingularBoundaryOperator<BFT, RT, RT>( contextDense, pwiseLinears, pwiseConstants, pwiseLinears, waveNumber); arma::Mat<RT> weakFormDense = opDense.weakForm()->asMatrix(); AssemblyOptions assemblyOptionsAca; assemblyOptionsAca.setVerbosityLevel(VerbosityLevel::LOW); AcaOptions acaOptions; acaOptions.mode = AcaOptions::LOCAL_ASSEMBLY; assemblyOptionsAca.switchToAcaMode(acaOptions); shared_ptr<Context<BFT, RT> > contextAca( new Context<BFT, RT>(quadStrategy, assemblyOptionsAca)); BoundaryOperator<BFT, RT> opAca = modifiedHelmholtz3dHypersingularBoundaryOperator<BFT, RT, RT>( contextAca, pwiseLinears, pwiseConstants, pwiseLinears, waveNumber); arma::Mat<RT> weakFormAca = opAca.weakForm()->asMatrix(); BOOST_CHECK(check_arrays_are_close<ValueType>( weakFormDense, weakFormAca, 2. * acaOptions.eps)); }
void AbstractBoundaryOperator<BasisFunctionType, ResultType>:: collectOptionsDependentDataForAssemblerConstruction( const AssemblyOptions& options, const shared_ptr<Fiber::RawGridGeometry<CoordinateType> >& testRawGeometry, const shared_ptr<Fiber::RawGridGeometry<CoordinateType> >& trialRawGeometry, shared_ptr<Fiber::OpenClHandler>& openClHandler, bool& cacheSingularIntegrals) const { typedef LocalAssemblerConstructionHelper Helper; Helper::makeOpenClHandler(options.parallelizationOptions().openClOptions(), testRawGeometry, trialRawGeometry, openClHandler); cacheSingularIntegrals = options.isSingularIntegralCachingEnabled(); }
std::unique_ptr<typename ElementaryLocalOperator<BasisFunctionType, ResultType>::LocalAssembler> ElementaryLocalOperator<BasisFunctionType, ResultType>::makeAssembler( const QuadratureStrategy &quadStrategy, const AssemblyOptions &options) const { typedef Fiber::RawGridGeometry<CoordinateType> RawGridGeometry; typedef std::vector<const Fiber::Shapeset<BasisFunctionType> *> ShapesetPtrVector; const bool verbose = (options.verbosityLevel() >= VerbosityLevel::DEFAULT); shared_ptr<RawGridGeometry> testRawGeometry, trialRawGeometry; shared_ptr<GeometryFactory> testGeometryFactory, trialGeometryFactory; shared_ptr<Fiber::OpenClHandler> openClHandler; shared_ptr<ShapesetPtrVector> testShapesets, trialShapesets; bool cacheSingularIntegrals; if (verbose) std::cout << "Collecting data for assembler construction..." << std::endl; this->collectDataForAssemblerConstruction( options, testRawGeometry, trialRawGeometry, testGeometryFactory, trialGeometryFactory, testShapesets, trialShapesets, openClHandler, cacheSingularIntegrals); if (verbose) std::cout << "Data collection finished." << std::endl; assert(testRawGeometry == trialRawGeometry); assert(testGeometryFactory == trialGeometryFactory); return quadStrategy.makeAssemblerForLocalOperators( testGeometryFactory, testRawGeometry, testShapesets, trialShapesets, make_shared_from_ref(testTransformations()), make_shared_from_ref(trialTransformations()), make_shared_from_ref(integral()), openClHandler); }
std::pair< shared_ptr<typename HypersingularIntegralOperator< BasisFunctionType, KernelType, ResultType>::LocalAssembler>, shared_ptr<typename HypersingularIntegralOperator< BasisFunctionType, KernelType, ResultType>::LocalAssembler> > HypersingularIntegralOperator<BasisFunctionType, KernelType, ResultType>::makeAssemblers( const QuadratureStrategy& quadStrategy, const AssemblyOptions& options) const { typedef Fiber::RawGridGeometry<CoordinateType> RawGridGeometry; typedef std::vector<const Fiber::Shapeset<BasisFunctionType>*> ShapesetPtrVector; const bool verbose = (options.verbosityLevel() >= VerbosityLevel::DEFAULT); shared_ptr<RawGridGeometry> testRawGeometry, trialRawGeometry; shared_ptr<GeometryFactory> testGeometryFactory, trialGeometryFactory; shared_ptr<Fiber::OpenClHandler> openClHandler; shared_ptr<ShapesetPtrVector> testShapesets, trialShapesets; bool cacheSingularIntegrals; if (verbose) std::cout << "Collecting data for assembler construction..." << std::endl; this->collectDataForAssemblerConstruction(options, testRawGeometry, trialRawGeometry, testGeometryFactory, trialGeometryFactory, testShapesets, trialShapesets, openClHandler, cacheSingularIntegrals); if (verbose) std::cout << "Data collection finished." << std::endl; bool makeSeparateOffDiagonalAssembler = options.assemblyMode() == AssemblyOptions::ACA && options.acaOptions().mode == AcaOptions::HYBRID_ASSEMBLY; return reallyMakeAssemblers(quadStrategy, testGeometryFactory, trialGeometryFactory, testRawGeometry, trialRawGeometry, testShapesets, trialShapesets, openClHandler, options.parallelizationOptions(), options.verbosityLevel(), cacheSingularIntegrals, makeSeparateOffDiagonalAssembler); }
DefaultLocalAssemblerForIntegralOperatorsOnSurfacesManager( bool cacheSingularIntegrals) { // Create a Bempp grid shared_ptr<Grid> grid = createGrid(); // Create context Fiber::AccuracyOptions options; options.doubleRegular.setRelativeQuadratureOrder(1); quadStrategy.reset(new QuadratureStrategy); AssemblyOptions assemblyOptions; assemblyOptions.setVerbosityLevel(VerbosityLevel::LOW); assemblyOptions.enableSingularIntegralCaching(cacheSingularIntegrals); Context<BFT, RT> context(quadStrategy, assemblyOptions); // These important thing is that the domain and dualToRange spaces are // different piecewiseConstantSpace.reset(new PiecewiseConstantSpace(grid)); piecewiseLinearSpace.reset(new PiecewiseLinearSpace(grid)); bop = laplace3dSingleLayerBoundaryOperator<BFT, RT>( make_shared_from_ref(context), piecewiseConstantSpace, piecewiseLinearSpace, piecewiseLinearSpace, "SLP"); const Operator& op = static_cast<const Operator&>(*bop.abstractOperator()); // This would be more elegant than the above, but it doesn't // work on Mac because of a problem with RTTI across // shared-library boundaries. // op = boost::dynamic_pointer_cast<const Operator>(bop.abstractOperator()); // Construct local assembler assembler = op.makeAssembler(*quadStrategy, assemblyOptions); }
std::unique_ptr<DiscreteBoundaryOperator<ResultType>> ElementaryLocalOperator<BasisFunctionType, ResultType>:: assembleWeakFormInSparseMode(LocalAssembler &assembler, const AssemblyOptions &options) const { #ifdef WITH_TRILINOS if (boost::is_complex<BasisFunctionType>::value) throw std::runtime_error( "ElementaryLocalOperator::assembleWeakFormInSparseMode(): " "sparse-mode assembly of identity operators for " "complex-valued basis functions is not supported yet"); const Space<BasisFunctionType> &testSpace = *this->dualToRange(); const Space<BasisFunctionType> &trialSpace = *this->domain(); // Fill local submatrices const GridView &view = testSpace.gridView(); const size_t elementCount = view.entityCount(0); std::vector<int> elementIndices(elementCount); for (size_t i = 0; i < elementCount; ++i) elementIndices[i] = i; std::vector<arma::Mat<ResultType>> localResult; assembler.evaluateLocalWeakForms(elementIndices, localResult); // Global DOF indices corresponding to local DOFs on elements std::vector<std::vector<GlobalDofIndex>> testGdofs(elementCount); std::vector<std::vector<GlobalDofIndex>> trialGdofs(elementCount); std::vector<std::vector<BasisFunctionType>> testLdofWeights(elementCount); std::vector<std::vector<BasisFunctionType>> trialLdofWeights(elementCount); gatherGlobalDofs(testSpace, trialSpace, testGdofs, trialGdofs, testLdofWeights, trialLdofWeights); // Multiply matrix entries by DOF weights for (size_t e = 0; e < elementCount; ++e) for (size_t trialDof = 0; trialDof < trialGdofs[e].size(); ++trialDof) for (size_t testDof = 0; testDof < testGdofs[e].size(); ++testDof) localResult[e](testDof, trialDof) *= conj(testLdofWeights[e][testDof]) * trialLdofWeights[e][trialDof]; // Estimate number of entries in each row // This will be useful when we begin to use MPI // // Get global DOF indices for which this process is responsible // const int testGlobalDofCount = testSpace.globalDofCount(); // Epetra_Map rowMap(testGlobalDofCount, 0 /* index-base */, comm); // std::vector<int> myTestGlobalDofs(rowMap.MyGlobalElements(), // rowMap.MyGlobalElements() + // rowMap.NumMyElements()); // const int myTestGlobalDofCount = myTestGlobalDofs.size(); const int testGlobalDofCount = testSpace.globalDofCount(); const int trialGlobalDofCount = trialSpace.globalDofCount(); arma::Col<int> nonzeroEntryCountEstimates(testGlobalDofCount); nonzeroEntryCountEstimates.fill(0); // Upper estimate for the number of global trial DOFs coupled to a given // global test DOF: sum of the local trial DOF counts for each element that // contributes to the global test DOF in question for (size_t e = 0; e < elementCount; ++e) for (size_t testLdof = 0; testLdof < testGdofs[e].size(); ++testLdof) { int testGdof = testGdofs[e][testLdof]; if (testGdof >= 0) nonzeroEntryCountEstimates(testGdof) += trialGdofs[e].size(); } Epetra_SerialComm comm; // To be replaced once we begin to use MPI Epetra_LocalMap rowMap(testGlobalDofCount, 0 /* index_base */, comm); Epetra_LocalMap colMap(trialGlobalDofCount, 0 /* index_base */, comm); shared_ptr<Epetra_FECrsMatrix> result = boost::make_shared<Epetra_FECrsMatrix>( Copy, rowMap, colMap, nonzeroEntryCountEstimates.memptr()); // TODO: make each process responsible for a subset of elements // Find maximum number of local dofs per element size_t maxLdofCount = 0; for (size_t e = 0; e < elementCount; ++e) maxLdofCount = std::max(maxLdofCount, testGdofs[e].size() * trialGdofs[e].size()); // Initialise sparse matrix with zeros at required positions arma::Col<double> zeros(maxLdofCount); zeros.fill(0.); for (size_t e = 0; e < elementCount; ++e) result->InsertGlobalValues(testGdofs[e].size(), &testGdofs[e][0], trialGdofs[e].size(), &trialGdofs[e][0], zeros.memptr()); // Add contributions from individual elements for (size_t e = 0; e < elementCount; ++e) epetraSumIntoGlobalValues(*result, testGdofs[e], trialGdofs[e], localResult[e]); result->GlobalAssemble(); // If assembly mode is equal to ACA and we have AHMED, // construct the block cluster tree. Otherwise leave it uninitialized. typedef ClusterConstructionHelper<BasisFunctionType> CCH; typedef AhmedDofWrapper<CoordinateType> AhmedDofType; typedef ExtendedBemCluster<AhmedDofType> AhmedBemCluster; typedef bbxbemblcluster<AhmedDofType, AhmedDofType> AhmedBemBlcluster; shared_ptr<AhmedBemBlcluster> blockCluster; shared_ptr<IndexPermutation> test_o2pPermutation, test_p2oPermutation; shared_ptr<IndexPermutation> trial_o2pPermutation, trial_p2oPermutation; #ifdef WITH_AHMED if (options.assemblyMode() == AssemblyOptions::ACA) { const AcaOptions &acaOptions = options.acaOptions(); bool indexWithGlobalDofs = acaOptions.mode != AcaOptions::HYBRID_ASSEMBLY; typedef ClusterConstructionHelper<BasisFunctionType> CCH; shared_ptr<AhmedBemCluster> testClusterTree; CCH::constructBemCluster(testSpace, indexWithGlobalDofs, acaOptions, testClusterTree, test_o2pPermutation, test_p2oPermutation); // TODO: construct a hermitian H-matrix if possible shared_ptr<AhmedBemCluster> trialClusterTree; CCH::constructBemCluster(trialSpace, indexWithGlobalDofs, acaOptions, trialClusterTree, trial_o2pPermutation, trial_p2oPermutation); unsigned int blockCount = 0; bool useStrongAdmissibilityCondition = !indexWithGlobalDofs; blockCluster.reset(CCH::constructBemBlockCluster( acaOptions, false /* hermitian */, *testClusterTree, *trialClusterTree, useStrongAdmissibilityCondition, blockCount).release()); } #endif // Create and return a discrete operator represented by the matrix that // has just been calculated return std::unique_ptr<DiscreteBoundaryOperator<ResultType>>( new DiscreteSparseBoundaryOperator<ResultType>( result, this->symmetry(), NO_TRANSPOSE, blockCluster, trial_o2pPermutation, test_o2pPermutation)); #else // WITH_TRILINOS throw std::runtime_error( "ElementaryLocalOperator::assembleWeakFormInSparseMode(): " "To enable assembly in sparse mode, recompile BEM++ " "with the symbol WITH_TRILINOS defined."); #endif }
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"); }
int main(int argc, char* argv[]) { // Load mesh if (argc != 2) { std::cout << "Solve a Dirichlet problem for the Laplace equation.\n" "Usage: " << argv[0] << " <mesh_file>" << std::endl; return 1; } shared_ptr<Grid> grid = loadTriangularMeshFromFile(argv[1]); //std::cout << grid->gridTopology() << std::endl; // Initialize the spaces PiecewiseLinearContinuousScalarSpace<BFT> HplusHalfSpace(grid); PiecewiseConstantScalarSpace<BFT> HminusHalfSpace(grid); // 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(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 BoundaryOperator<BFT, RT> slpOp = laplace3dSingleLayerBoundaryOperator<BFT, RT >( make_shared_from_ref(context), make_shared_from_ref(HminusHalfSpace), make_shared_from_ref(HplusHalfSpace), make_shared_from_ref(HminusHalfSpace), "SLP"); BoundaryOperator<BFT, RT> dlpOp = laplace3dDoubleLayerBoundaryOperator<BFT, RT >( make_shared_from_ref(context), make_shared_from_ref(HplusHalfSpace), make_shared_from_ref(HplusHalfSpace), make_shared_from_ref(HminusHalfSpace), "DLP"); BoundaryOperator<BFT, RT> id = identityOperator<BFT, RT>( make_shared_from_ref(context), make_shared_from_ref(HplusHalfSpace), make_shared_from_ref(HplusHalfSpace), make_shared_from_ref(HminusHalfSpace), "I"); // Form the right-hand side sum BoundaryOperator<BFT, RT> rhsOp = -0.5 * id + dlpOp; // We also want a grid function GridFunction<BFT, RT> u( make_shared_from_ref(context), make_shared_from_ref(HplusHalfSpace), make_shared_from_ref(HplusHalfSpace), // is this the right choice? surfaceNormalIndependentFunction(MyFunctor())); // Assemble the rhs std::cout << "Assemble rhs" << std::endl; GridFunction<BFT, RT> rhs = rhsOp * u; // Initialize the solver std::cout << "Initialize solver" << std::endl; #ifdef WITH_TRILINOS DefaultIterativeSolver<BFT, RT> solver(slpOp,ConvergenceTestMode::TEST_CONVERGENCE_IN_DUAL_TO_RANGE); solver.initializeSolver(defaultGmresParameterList(1e-5)); Solution<BFT, RT> solution = solver.solve(rhs); std::cout << solution.solverMessage() << std::endl; #else DefaultDirectSolver<BFT, RT> solver(slp, rhs); solver.solve(); #endif // Extract the solution const GridFunction<BFT, RT>& solFun = solution.gridFunction(); // Write out as VTK solFun.exportToVtk(VtkWriter::CELL_DATA, "Neumann_data", "calculated_neumann_data_cell"); solFun.exportToVtk(VtkWriter::VERTEX_DATA, "Neumann_data", "calculated_neumann_data_vertex"); // Uncomment the block below if you are solving the problem on a sphere and // you want to compare the numerical and analytical solution. // arma::Col<RT> solutionCoefficients = solFun.coefficients(); // std::cout << solutionCoefficients << std::endl; // arma::Col<RT> deviation = solutionCoefficients - static_cast<RT>(-1.); // // % in Armadillo -> elementwise multiplication // RT stdDev = sqrt(arma::accu(deviation % deviation) / // static_cast<RT>(solutionCoefficients.n_rows)); // std::cout << "Standard deviation: " << stdDev << std::endl; }
BOOST_AUTO_TEST_CASE_TEMPLATE(works, BasisFunctionType, basis_function_types) { typedef BasisFunctionType BFT; typedef typename Fiber::ScalarTraits<BFT>::ComplexType RT; typedef typename Fiber::ScalarTraits<BFT>::RealType CT; GridParameters params; params.topology = GridParameters::TRIANGULAR; shared_ptr<Grid> grid = GridFactory::importGmshGrid( params, "meshes/two_disjoint_triangles.msh", false /* verbose */); PiecewiseLinearContinuousScalarSpace<BFT> pwiseLinears(grid); PiecewiseConstantScalarSpace<BFT> pwiseConstants(grid); AssemblyOptions assemblyOptions; assemblyOptions.setVerbosityLevel(VerbosityLevel::LOW); AccuracyOptions accuracyOptions; accuracyOptions.doubleRegular.setAbsoluteQuadratureOrder(5); accuracyOptions.doubleSingular.setAbsoluteQuadratureOrder(5); NumericalQuadratureStrategy<BFT, RT> quadStrategy(accuracyOptions); Context<BFT, RT> context(make_shared_from_ref(quadStrategy), assemblyOptions); const RT waveNumber(1.23, 0.31); BoundaryOperator<BFT, RT> slpOpConstants = Bempp::helmholtz3dSingleLayerBoundaryOperator<BFT>( make_shared_from_ref(context), make_shared_from_ref(pwiseConstants), make_shared_from_ref(pwiseConstants), make_shared_from_ref(pwiseConstants), waveNumber); BoundaryOperator<BFT, RT> slpOpLinears = helmholtz3dSingleLayerBoundaryOperator<BFT>( make_shared_from_ref(context), make_shared_from_ref(pwiseLinears), make_shared_from_ref(pwiseLinears), make_shared_from_ref(pwiseLinears), waveNumber); BoundaryOperator<BFT, RT> hypOp = helmholtz3dHypersingularBoundaryOperator<BFT>( make_shared_from_ref(context), make_shared_from_ref(pwiseLinears), make_shared_from_ref(pwiseLinears), make_shared_from_ref(pwiseLinears), waveNumber); // Get the matrix repr. of the hypersingular operator arma::Mat<RT> hypMat = hypOp.weakForm()->asMatrix(); // Construct the expected hypersingular operator matrix. For this, we need: // * the surface curls of all basis functions (which are constant) typedef Fiber::SurfaceCurl3dElementaryFunctor<CT> ElementaryFunctor; typedef Fiber::ElementaryBasisTransformationFunctorWrapper<ElementaryFunctor> Functor; Functor functor; size_t basisDeps = 0, geomDeps = 0; functor.addDependencies(basisDeps, geomDeps); arma::Mat<CT> points(2, 1); points.fill(0.); typedef Fiber::PiecewiseLinearContinuousScalarBasis<3, BFT> Basis; Basis basis; Fiber::BasisData<BFT> basisData; basis.evaluate(basisDeps, points, Fiber::ALL_DOFS, basisData); Fiber::GeometricalData<CT> geomData[2]; std::unique_ptr<GridView> view = grid->leafView(); std::unique_ptr<EntityIterator<0> > it = view->entityIterator<0>(); it->entity().geometry().getData(geomDeps, points, geomData[0]); it->next(); it->entity().geometry().getData(geomDeps, points, geomData[1]); Fiber::DefaultCollectionOfBasisTransformations<Functor> transformations(functor); Fiber::CollectionOf3dArrays<BFT> surfaceCurl[2]; transformations.evaluate(basisData, geomData[0], surfaceCurl[0]); transformations.evaluate(basisData, geomData[1], surfaceCurl[1]); // * the single-layer potential matrix for constant basis functions arma::Mat<RT> slpMatConstants = slpOpConstants.weakForm()->asMatrix(); // * the single-layer potential matrix for linear basis functions arma::Mat<RT> slpMatLinears = slpOpLinears.weakForm()->asMatrix(); arma::Mat<RT> expectedHypMat(6, 6); for (size_t testElement = 0; testElement < 2; ++testElement) for (size_t trialElement = 0; trialElement < 2; ++trialElement) for (size_t r = 0; r < 3; ++r) for (size_t c = 0; c < 3; ++c) { RT curlMultiplier = 0.; for (size_t dim = 0; dim < 3; ++dim) curlMultiplier += surfaceCurl[testElement][0](dim, r, 0) * surfaceCurl[trialElement][0](dim, c, 0); RT valueMultiplier = 0.; for (size_t dim = 0; dim < 3; ++dim) valueMultiplier += geomData[testElement].normals(dim, 0) * geomData[trialElement].normals(dim, 0); expectedHypMat(3 * testElement + r, 3 * trialElement + c) = curlMultiplier * slpMatConstants(testElement, trialElement) - waveNumber * waveNumber * valueMultiplier * slpMatLinears(3 * testElement + r, 3 * trialElement + c); } BOOST_CHECK(check_arrays_are_close<RT>(expectedHypMat, hypMat, 1e-6)); }
int main(int argc, char* argv[]) { // Process command-line args if (argc < 7 || argc % 2 != 1) { std::cout << "Solve a Maxwell Dirichlet problem in an exterior domain.\n" << "Usage: " << argv[0] << " <mesh_file> <n_threads> <aca_eps> <solver_tol>" << " <singular_order_increment>" << " [<regular_order_increment_1> <min_relative_distance_1>]" << " [<regular_order_increment_2> <min_relative_distance_2>]" << " [...] <regular_order_increment_n>" << std::endl; return 1; } int maxThreadCount = atoi(argv[2]); double acaEps = atof(argv[3]); double solverTol = atof(argv[4]); int singOrderIncrement = atoi(argv[5]); if (acaEps > 1. || acaEps < 0.) { std::cout << "Invalid aca_eps: " << acaEps << std::endl; return 1; } if (solverTol > 1. || solverTol < 0.) { std::cout << "Invalid solver_tol: " << solverTol << std::endl; return 1; } AccuracyOptionsEx accuracyOptions; std::vector<double> maxNormDists; std::vector<int> orderIncrements; for (int i = 6; i < argc - 1; i += 2) { orderIncrements.push_back(atoi(argv[i])); maxNormDists.push_back(atof(argv[i + 1])); } orderIncrements.push_back(atoi(argv[argc - 1])); accuracyOptions.setDoubleRegular(maxNormDists, orderIncrements); accuracyOptions.setDoubleSingular(singOrderIncrement); accuracyOptions.setSingleRegular(2); // Load mesh GridParameters params; params.topology = GridParameters::TRIANGULAR; shared_ptr<Grid> grid = GridFactory::importGmshGrid(params, argv[1]); // Initialize the space RaviartThomas0VectorSpace<BFT> HdivSpace(grid); // Set assembly mode and options AssemblyOptions assemblyOptions; assemblyOptions.setMaxThreadCount(maxThreadCount); if (acaEps > 0.) { AcaOptions acaOptions; acaOptions.eps = acaEps; assemblyOptions.switchToAcaMode(acaOptions); } NumericalQuadratureStrategy<BFT, RT> quadStrategy(accuracyOptions); Context<BFT, RT> context(make_shared_from_ref(quadStrategy), assemblyOptions); // Construct operators BoundaryOperator<BFT, RT> slpOp = maxwell3dSingleLayerBoundaryOperator<BFT>( make_shared_from_ref(context), make_shared_from_ref(HdivSpace), make_shared_from_ref(HdivSpace), make_shared_from_ref(HdivSpace), k, "SLP"); BoundaryOperator<BFT, RT> dlpOp = maxwell3dDoubleLayerBoundaryOperator<BFT>( make_shared_from_ref(context), make_shared_from_ref(HdivSpace), make_shared_from_ref(HdivSpace), make_shared_from_ref(HdivSpace), k, "DLP"); BoundaryOperator<BFT, RT> idOp = maxwell3dIdentityOperator<BFT, RT>( make_shared_from_ref(context), make_shared_from_ref(HdivSpace), make_shared_from_ref(HdivSpace), make_shared_from_ref(HdivSpace), "Id"); // Construct a grid function representing the Dirichlet data GridFunction<BFT, RT> dirichletData( make_shared_from_ref(context), make_shared_from_ref(HdivSpace), make_shared_from_ref(HdivSpace), surfaceNormalDependentFunction(DirichletData())); dirichletData.exportToVtk(VtkWriter::CELL_DATA, "Dirichlet_data", "input_dirichlet_data_cell"); dirichletData.exportToVtk(VtkWriter::VERTEX_DATA, "Dirichlet_data", "input_dirichlet_data_vertex"); // Construct a grid function representing the right-hand side GridFunction<BFT, RT> rhs = -((0.5 * idOp + dlpOp) * dirichletData); // Solve the equation Solution<BFT, RT> solution; #ifdef WITH_AHMED if (solverTol > 0.) { DefaultIterativeSolver<BFT, RT> solver(slpOp); AcaApproximateLuInverse<RT> slpOpLu( DiscreteAcaBoundaryOperator<RT>::castToAca( *slpOp.weakForm()), /* LU factorisation accuracy */ 0.01); Preconditioner<RT> prec = discreteOperatorToPreconditioner<RT>( make_shared_from_ref(slpOpLu)); solver.initializeSolver(defaultGmresParameterList(solverTol, 10000), prec); solution = solver.solve(rhs); } else { DefaultDirectSolver<BFT, RT> solver(slpOp); solution = solver.solve(rhs); } #else // WITH_AHMED DefaultDirectSolver<BFT, RT> solver(slpOp); solution = solver.solve(rhs); #endif std::cout << solution.solverMessage() << std::endl; // Extract the solution const GridFunction<BFT, RT>& neumannData = solution.gridFunction(); neumannData.exportToVtk(VtkWriter::CELL_DATA, "Neumann_data", "calculated_neumann_data_cell"); neumannData.exportToVtk(VtkWriter::VERTEX_DATA, "Neumann_data", "calculated_neumann_data_vertex"); // Compare the solution against the analytical result GridFunction<BFT, RT> exactNeumannData( make_shared_from_ref(context), make_shared_from_ref(HdivSpace), make_shared_from_ref(HdivSpace), surfaceNormalDependentFunction(ExactNeumannData())); exactNeumannData.exportToVtk(VtkWriter::CELL_DATA, "Neumann_data", "exact_neumann_data_cell"); exactNeumannData.exportToVtk(VtkWriter::VERTEX_DATA, "Neumann_data", "exact_neumann_data_vertex"); EvaluationOptions evaluationOptions; CT absoluteError = L2NormOfDifference( neumannData, surfaceNormalDependentFunction(ExactNeumannData()), quadStrategy, evaluationOptions); CT exactSolNorm = L2NormOfDifference( 0. * neumannData, surfaceNormalDependentFunction(ExactNeumannData()), quadStrategy, evaluationOptions); std::cout << "Relative L^2 error: " << absoluteError / exactSolNorm << "\nAbsolute L^2 error: " << absoluteError << std::endl; // Evaluate the solution at a few points Maxwell3dSingleLayerPotentialOperator<BFT> slpPotOp(k); Maxwell3dDoubleLayerPotentialOperator<BFT> dlpPotOp(k); const int dimWorld = 3; const int pointCount = 3; arma::Mat<CT> points(dimWorld, pointCount * pointCount); for (int i = 0; i < pointCount; ++i) for (int j = 0; j < pointCount; ++j) { points(0, i * pointCount + j) = 3. + i / CT(pointCount - 1); points(1, i * pointCount + j) = 2. + j / CT(pointCount - 1); points(2, i * pointCount + j) = 0.5; } arma::Mat<RT> slpContrib = slpPotOp.evaluateAtPoints( neumannData, points, quadStrategy, evaluationOptions); arma::Mat<RT> dlpContrib = dlpPotOp.evaluateAtPoints( dirichletData, points, quadStrategy, evaluationOptions); arma::Mat<RT> values = -slpContrib - dlpContrib; // Evaluate the analytical solution at the same points ExactSolution exactSolution; arma::Mat<RT> exactValues(dimWorld, pointCount * pointCount); for (int i = 0; i < pointCount * pointCount; ++i) { arma::Col<RT> activeResultColumn = exactValues.unsafe_col(i); exactSolution.evaluate(points.unsafe_col(i), activeResultColumn); } // Compare the numerical and analytical solutions std::cout << "Numerical | analytical\n"; for (int i = 0; i < pointCount * pointCount; ++i) std::cout << values(0, i) << " " << values(1, i) << " " << values(2, i) << " | " << exactValues(0, i) << " " << exactValues(1, i) << " " << exactValues(2, i) << "\n"; }
int main() { // Import symbols from namespace Bempp to the global namespace using namespace Bempp; // Load mesh const char* meshFile = "meshes/sphere-h-0.2.msh"; GridParameters params; params.topology = GridParameters::TRIANGULAR; shared_ptr<Grid> grid = GridFactory::importGmshGrid(params, meshFile); // Initialize the spaces PiecewiseLinearContinuousScalarSpace<BFT> pwiseLinears(grid); PiecewiseConstantScalarSpace<BFT> pwiseConstants(grid); // Define the quadrature strategy AccuracyOptions accuracyOptions; // Increase by 2 the order of quadrature rule used to approximate // integrals of regular functions on pairs on elements accuracyOptions.doubleRegular.setRelativeQuadratureOrder(2); // Increase by 2 the order of quadrature rule used to approximate // integrals of regular functions on single elements accuracyOptions.singleRegular.setRelativeQuadratureOrder(2); NumericalQuadratureStrategy<BFT, RT> quadStrategy(accuracyOptions); // Specify the assembly method. We want to use ACA AssemblyOptions assemblyOptions; AcaOptions acaOptions; // Default parameters for ACA assemblyOptions.switchToAcaMode(acaOptions); // Create the assembly context Context<BFT, RT> context(make_shared_from_ref(quadStrategy), assemblyOptions); // Construct elementary operators BoundaryOperator<BFT, RT> slpOp = laplace3dSingleLayerBoundaryOperator<BFT, RT>( make_shared_from_ref(context), make_shared_from_ref(pwiseConstants), make_shared_from_ref(pwiseLinears), make_shared_from_ref(pwiseConstants)); BoundaryOperator<BFT, RT> dlpOp = laplace3dDoubleLayerBoundaryOperator<BFT, RT>( make_shared_from_ref(context), make_shared_from_ref(pwiseLinears), make_shared_from_ref(pwiseLinears), make_shared_from_ref(pwiseConstants)); BoundaryOperator<BFT, RT> idOp = identityOperator<BFT, RT>( make_shared_from_ref(context), make_shared_from_ref(pwiseLinears), make_shared_from_ref(pwiseLinears), make_shared_from_ref(pwiseConstants)); // Form the right-hand side sum BoundaryOperator<BFT, RT> rhsOp = -0.5 * idOp + dlpOp; // Construct the grid function representing the (input) Dirichlet data GridFunction<BFT, RT> dirichletData( make_shared_from_ref(context), make_shared_from_ref(pwiseLinears), make_shared_from_ref(pwiseLinears), surfaceNormalIndependentFunction(DirichletData())); // Construct the right-hand-side grid function GridFunction<BFT, RT> rhs = rhsOp * dirichletData; // Initialize the solver DefaultIterativeSolver<BFT, RT> solver(slpOp); solver.initializeSolver(defaultGmresParameterList(1e-5)); // Solve the equation Solution<BFT, RT> solution = solver.solve(rhs); std::cout << solution.solverMessage() << std::endl; // Extract the solution in the form of a grid function // and export it in VTK format const GridFunction<BFT, RT>& solFun = solution.gridFunction(); solFun.exportToVtk(VtkWriter::CELL_DATA, "Neumann_data", "solution"); // Compare the numerical and analytical solution on the grid // GridFunction<BFT, RT> exactSolFun( // make_shared_from_ref(context), // make_shared_from_ref(pwiseConstants), // make_shared_from_ref(pwiseConstants), // surfaceNormalIndependentFunction(ExactNeumannData())); CT absoluteError, relativeError; estimateL2Error( solFun, surfaceNormalIndependentFunction(ExactNeumannData()), quadStrategy, absoluteError, relativeError); std::cout << "Relative L^2 error: " << relativeError << std::endl; // GridFunction<BFT, RT> diff = solFun - exactSolFun; // double relativeError = diff.L2Norm() / exactSolFun.L2Norm(); // std::cout << "Relative L^2 error: " << relativeError << std::endl; // Prepare to evaluate the solution on an annulus outside the sphere // Create potential operators Laplace3dSingleLayerPotentialOperator<BFT, RT> slPotOp; Laplace3dDoubleLayerPotentialOperator<BFT, RT> dlPotOp; // Construct the array 'evaluationPoints' containing the coordinates // of points where the solution should be evaluated const int rCount = 51; const int thetaCount = 361; const CT minTheta = 0., maxTheta = 2. * M_PI; const CT minR = 1., maxR = 2.; const int dimWorld = 3; arma::Mat<CT> evaluationPoints(dimWorld, rCount * thetaCount); for (int iTheta = 0; iTheta < thetaCount; ++iTheta) { CT theta = minTheta + (maxTheta - minTheta) * iTheta / (thetaCount - 1); for (int iR = 0; iR < rCount; ++iR) { CT r = minR + (maxR - minR) * iR / (rCount - 1); evaluationPoints(0, iR + iTheta * rCount) = r * cos(theta); // x evaluationPoints(1, iR + iTheta * rCount) = r * sin(theta); // y evaluationPoints(2, iR + iTheta * rCount) = 0.; // z } } // Use the Green's representation formula to evaluate the solution EvaluationOptions evaluationOptions; arma::Mat<RT> field = -slPotOp.evaluateAtPoints(solFun, evaluationPoints, quadStrategy, evaluationOptions) + dlPotOp.evaluateAtPoints(dirichletData, evaluationPoints, quadStrategy, evaluationOptions); // Export the solution into text file std::ofstream out("solution.txt"); out << "# x y z u\n"; for (int i = 0; i < rCount * thetaCount; ++i) out << evaluationPoints(0, i) << ' ' << evaluationPoints(1, i) << ' ' << evaluationPoints(2, i) << ' ' << field(0, i) << '\n'; }
std::auto_ptr<DiscreteBoundaryOperator<ResultType> > AcaGlobalAssembler<BasisFunctionType, ResultType>::assembleDetachedWeakForm( const Space<BasisFunctionType>& testSpace, const Space<BasisFunctionType>& trialSpace, const std::vector<LocalAssembler*>& localAssemblers, const std::vector<const DiscreteBndOp*>& sparseTermsToAdd, const std::vector<ResultType>& denseTermsMultipliers, const std::vector<ResultType>& sparseTermsMultipliers, const AssemblyOptions& options, int symmetry) { #ifdef WITH_AHMED typedef AhmedDofWrapper<CoordinateType> AhmedDofType; typedef ExtendedBemCluster<AhmedDofType> AhmedBemCluster; typedef bemblcluster<AhmedDofType, AhmedDofType> AhmedBemBlcluster; typedef DiscreteAcaBoundaryOperator<ResultType> DiscreteAcaLinOp; const AcaOptions& acaOptions = options.acaOptions(); const bool indexWithGlobalDofs = acaOptions.globalAssemblyBeforeCompression; const bool verbosityAtLeastDefault = (options.verbosityLevel() >= VerbosityLevel::DEFAULT); const bool verbosityAtLeastHigh = (options.verbosityLevel() >= VerbosityLevel::HIGH); // Currently we don't support Hermitian ACA operators. This is because we // don't have the means to really test them -- we would need complex-valued // basis functions for that. (Assembly of such a matrix would be very easy // -- just change complex_sym from true to false in the call to apprx_sym() // in AcaWeakFormAssemblerLoopBody::operator() -- but operations on // symmetric/Hermitian matrices are not always trivial and we do need to be // able to test them properly.) bool symmetric = symmetry & SYMMETRIC; if (symmetry & HERMITIAN && !(symmetry & SYMMETRIC) && verbosityAtLeastDefault) std::cout << "Warning: assembly of non-symmetric Hermitian H-matrices " "is not supported yet. A general H-matrix will be assembled" << std::endl; #ifndef WITH_TRILINOS if (!indexWithGlobalDofs) throw std::runtime_error("AcaGlobalAssembler::assembleDetachedWeakForm(): " "ACA assembly with globalAssemblyBeforeCompression " "set to false requires BEM++ to be linked with " "Trilinos"); #endif // WITH_TRILINOS const size_t testDofCount = indexWithGlobalDofs ? testSpace.globalDofCount() : testSpace.flatLocalDofCount(); const size_t trialDofCount = indexWithGlobalDofs ? trialSpace.globalDofCount() : trialSpace.flatLocalDofCount(); if (symmetric && testDofCount != trialDofCount) throw std::invalid_argument("AcaGlobalAssembler::assembleDetachedWeakForm(): " "you cannot generate a symmetric weak form " "using test and trial spaces with different " "numbers of DOFs"); // o2p: map of original indices to permuted indices // p2o: map of permuted indices to original indices typedef ClusterConstructionHelper<BasisFunctionType> CCH; shared_ptr<AhmedBemCluster> testClusterTree; shared_ptr<IndexPermutation> test_o2pPermutation, test_p2oPermutation; CCH::constructBemCluster(testSpace, indexWithGlobalDofs, acaOptions, testClusterTree, test_o2pPermutation, test_p2oPermutation); shared_ptr<AhmedBemCluster> trialClusterTree; shared_ptr<IndexPermutation> trial_o2pPermutation, trial_p2oPermutation; if (symmetric || &testSpace == &trialSpace) { trialClusterTree = testClusterTree; trial_o2pPermutation = test_o2pPermutation; trial_p2oPermutation = test_p2oPermutation; } else CCH::constructBemCluster(trialSpace, indexWithGlobalDofs, acaOptions, trialClusterTree, trial_o2pPermutation, trial_p2oPermutation); // // Export VTK plots showing the disctribution of leaf cluster ids // std::vector<unsigned int> testClusterIds; // getClusterIds(*testClusterTree, test_p2oPermutation->permutedIndices(), testClusterIds); // testSpace.dumpClusterIds("testClusterIds", testClusterIds, // indexWithGlobalDofs ? GLOBAL_DOFS : FLAT_LOCAL_DOFS); // std::vector<unsigned int> trialClusterIds; // getClusterIds(*trialClusterTree, trial_p2oPermutation->permutedIndices(), trialClusterIds); // trialSpace.dumpClusterIds("trialClusterIds", trialClusterIds, // indexWithGlobalDofs ? GLOBAL_DOFS : FLAT_LOCAL_DOFS); if (verbosityAtLeastHigh) std::cout << "Test cluster count: " << testClusterTree->getncl() << "\nTrial cluster count: " << trialClusterTree->getncl() << std::endl; unsigned int blockCount = 0; shared_ptr<AhmedBemBlcluster> bemBlclusterTree( CCH::constructBemBlockCluster(acaOptions, symmetric, *testClusterTree, *trialClusterTree, blockCount).release()); if (verbosityAtLeastHigh) std::cout << "Mblock count: " << blockCount << std::endl; std::vector<unsigned int> p2oTestDofs = test_p2oPermutation->permutedIndices(); std::vector<unsigned int> p2oTrialDofs = trial_p2oPermutation->permutedIndices(); WeakFormAcaAssemblyHelper<BasisFunctionType, ResultType> helper(testSpace, trialSpace, p2oTestDofs, p2oTrialDofs, localAssemblers, sparseTermsToAdd, denseTermsMultipliers, sparseTermsMultipliers, options); typedef mblock<typename AhmedTypeTraits<ResultType>::Type> AhmedMblock; boost::shared_array<AhmedMblock*> blocks = allocateAhmedMblockArray<ResultType>(bemBlclusterTree.get()); // matgen_sqntl(helper, AhmedBemBlclusterTree.get(), AhmedBemBlclusterTree.get(), // acaOptions.recompress, acaOptions.eps, // acaOptions.maximumRank, blocks.get()); // matgen_omp(helper, blockCount, AhmedBemBlclusterTree.get(), // acaOptions.eps, acaOptions.maximumRank, blocks.get()); // // Dump mblocks // const int mblockCount = AhmedBemBlclusterTree->nleaves(); // for (int i = 0; i < mblockCount; ++i) // if (blocks[i]->isdns()) // { // char buffer[1024]; // sprintf(buffer, "mblock-dns-%d-%d.txt", // blocks[i]->getn1(), blocks[i]->getn2()); // arma::Col<ResultType> block((ResultType*)blocks[i]->getdata(), // blocks[i]->nvals()); // arma::diskio::save_raw_ascii(block, buffer); // } // else // { // char buffer[1024]; // sprintf(buffer, "mblock-lwr-%d-%d.txt", // blocks[i]->getn1(), blocks[i]->getn2()); // arma::Col<ResultType> block((ResultType*)blocks[i]->getdata(), // blocks[i]->nvals()); // arma::diskio::save_raw_ascii(block, buffer); // } AhmedLeafClusterArray leafClusters(bemBlclusterTree.get()); leafClusters.sortAccordingToClusterSize(); const size_t leafClusterCount = leafClusters.size(); const ParallelizationOptions& parallelOptions = options.parallelizationOptions(); int maxThreadCount = 1; if (!parallelOptions.isOpenClEnabled()) { if (parallelOptions.maxThreadCount() == ParallelizationOptions::AUTO) maxThreadCount = tbb::task_scheduler_init::automatic; else maxThreadCount = parallelOptions.maxThreadCount(); } tbb::task_scheduler_init scheduler(maxThreadCount); tbb::atomic<size_t> done; done = 0; std::vector<ChunkStatistics> chunkStats(leafClusterCount); // typedef AcaWeakFormAssemblerLoopBody<BasisFunctionType, ResultType> Body; // // std::cout << "Loop start" << std::endl; // tbb::tick_count loopStart = tbb::tick_count::now(); // // tbb::parallel_for(tbb::blocked_range<size_t>(0, leafClusterCount), // // Body(helper, leafClusters, blocks, acaOptions, done // // , chunkStats)); // tbb::parallel_for(ScatteredRange(0, leafClusterCount), // Body(helper, leafClusters, blocks, acaOptions, done // , chunkStats)); // tbb::tick_count loopEnd = tbb::tick_count::now(); // // std::cout << "Loop end" << std::endl; typedef AcaWeakFormAssemblerLoopBody<BasisFunctionType, ResultType> Body; typename Body::LeafClusterIndexQueue leafClusterIndexQueue; for (size_t i = 0; i < leafClusterCount; ++i) leafClusterIndexQueue.push(i); if (verbosityAtLeastDefault) std::cout << "About to start the ACA assembly loop" << std::endl; tbb::tick_count loopStart = tbb::tick_count::now(); { Fiber::SerialBlasRegion region; // if possible, ensure that BLAS is single-threaded tbb::parallel_for(tbb::blocked_range<size_t>(0, leafClusterCount), Body(helper, leafClusters, blocks, acaOptions, done, verbosityAtLeastDefault, leafClusterIndexQueue, symmetric, chunkStats)); } tbb::tick_count loopEnd = tbb::tick_count::now(); if (verbosityAtLeastDefault) { std::cout << "\n"; // the progress bar doesn't print the final \n std::cout << "ACA loop took " << (loopEnd - loopStart).seconds() << " s" << std::endl; } // TODO: parallelise! if (acaOptions.recompress) { if (verbosityAtLeastDefault) std::cout << "About to start ACA agglomeration" << std::endl; agglH(bemBlclusterTree.get(), blocks.get(), acaOptions.eps, acaOptions.maximumRank); if (verbosityAtLeastDefault) std::cout << "Agglomeration finished" << std::endl; } // // Dump timing data of individual chunks // std::cout << "\nChunks:\n"; // for (int i = 0; i < leafClusterCount; ++i) // if (chunkStats[i].valid) { // int blockIndex = leafClusters[i]->getidx(); // std::cout << chunkStats[i].chunkStart << "\t" // << chunkStats[i].chunkSize << "\t" // << (chunkStats[i].startTime - loopStart).seconds() << "\t" // << (chunkStats[i].endTime - loopStart).seconds() << "\t" // << (chunkStats[i].endTime - chunkStats[i].startTime).seconds() << "\t" // << blocks[blockIndex]->getn1() << "\t" // << blocks[blockIndex]->getn2() << "\t" // << blocks[blockIndex]->islwr() << "\t" // << (blocks[blockIndex]->islwr() ? blocks[blockIndex]->rank() : 0) << "\n"; // } { size_t origMemory = sizeof(ResultType) * testDofCount * trialDofCount; size_t ahmedMemory = sizeH(bemBlclusterTree.get(), blocks.get()); int maximumRank = Hmax_rank(bemBlclusterTree.get(), blocks.get()); if (verbosityAtLeastDefault) std::cout << "\nNeeded storage: " << ahmedMemory / 1024. / 1024. << " MB.\n" << "Without approximation: " << origMemory / 1024. / 1024. << " MB.\n" << "Compressed to " << (100. * ahmedMemory) / origMemory << "%.\n" << "Maximum rank: " << maximumRank << ".\n" << std::endl; if (acaOptions.outputPostscript) { if (verbosityAtLeastDefault) std::cout << "Writing matrix partition ..." << std::flush; std::ofstream os(acaOptions.outputFname.c_str()); if (symmetric) // seems valid also for Hermitian matrices psoutputHeH(os, bemBlclusterTree.get(), testDofCount, blocks.get()); else psoutputGeH(os, bemBlclusterTree.get(), testDofCount, blocks.get()); os.close(); if (verbosityAtLeastDefault) std::cout << " done." << std::endl; } } int outSymmetry = NO_SYMMETRY; if (symmetric) { outSymmetry = SYMMETRIC; if (!boost::is_complex<ResultType>()) outSymmetry |= HERMITIAN; } std::auto_ptr<DiscreteAcaLinOp> acaOp( new DiscreteAcaLinOp(testDofCount, trialDofCount, acaOptions.eps, acaOptions.maximumRank, outSymmetry, bemBlclusterTree, blocks, *trial_o2pPermutation, *test_o2pPermutation, parallelOptions)); std::auto_ptr<DiscreteBndOp> result; if (indexWithGlobalDofs) result = acaOp; else { #ifdef WITH_TRILINOS // without Trilinos, this code will never be reached -- an exception // will be thrown earlier in this function typedef DiscreteBoundaryOperatorComposition<ResultType> DiscreteBndOpComp; shared_ptr<DiscreteBndOp> acaOpShared(acaOp.release()); shared_ptr<DiscreteBndOp> trialGlobalToLocal = constructOperatorMappingGlobalToFlatLocalDofs< BasisFunctionType, ResultType>(trialSpace); shared_ptr<DiscreteBndOp> testLocalToGlobal = constructOperatorMappingFlatLocalToGlobalDofs< BasisFunctionType, ResultType>(testSpace); shared_ptr<DiscreteBndOp> tmp( new DiscreteBndOpComp(acaOpShared, trialGlobalToLocal)); result.reset(new DiscreteBndOpComp(testLocalToGlobal, tmp)); #endif // WITH_TRILINOS } return result; #else // without Ahmed throw std::runtime_error("AcaGlobalAssembler::assembleDetachedWeakForm(): " "To enable assembly in ACA mode, recompile BEM++ " "with the symbol WITH_AHMED defined."); #endif // WITH_AHMED }