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);
    }
Пример #3
0
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 */);
}
Пример #5
0
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));
}
Пример #8
0
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));
}
Пример #10
0
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();
}
Пример #11
0
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);
    }
Пример #14
0
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
}
Пример #15
0
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");
}
Пример #16
0
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));
}
Пример #18
0
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";
}
Пример #19
0
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';
}
Пример #20
0
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
}