diff --git a/src/bem/bem_fft_base.cpp b/src/bem/bem_fft_base.cpp index d385590..de00540 100644 --- a/src/bem/bem_fft_base.cpp +++ b/src/bem/bem_fft_base.cpp @@ -1,193 +1,171 @@ /** * @file * @section LICENSE * * Copyright (©) 2016-2020 EPFL (École Polytechnique Fédérale de Lausanne), * Laboratory (LSMS - Laboratoire de Simulation en Mécanique des Solides) * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published * by the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . * */ /* -------------------------------------------------------------------------- */ #include "bem_fft_base.hh" /* -------------------------------------------------------------------------- */ #include "bem_meta_functional.hh" #include "elastic_energy_functional.hh" -#include "fft_plan_manager.hh" -#include "surface_statistics.hh" #include "legacy_types.hh" +#include "surface_statistics.hh" /* -------------------------------------------------------------------------- */ namespace tamaas { /* -------------------------------------------------------------------------- */ BemFFTBase::BemFFTBase(Surface& p) : BemInterface(p), functional(new MetaFunctional(*this)), surface_tractions(p.size(), p.getL()), surface_displacements(p.size(), p.getL()), surface_spectral_input(p.size(), p.getL()), surface_spectral_output(p.size(), p.getL()), surface_spectral_influence_disp(p.size(), p.getL()), true_displacements(p.size(), p.getL()), old_displacements(p.size(), p.getL()), gap(p.size(), p.getL()), e_star(std::nan("")), tmp_coeff(1. / M_PI) { // this->setNumberOfThreads(this->nthreads); max_iterations = 5000; dump_freq = 100; this->functional->addFunctionalTerm(new ElasticEnergyFunctional(*this)); std::cerr << this << " is setting up the surfaces"; std::cerr << &p << " has size " << p.size() << std::endl; } /* -------------------------------------------------------------------------- */ -BemFFTBase::~BemFFTBase() { FFTPlanManager::get().clean(); } - -/* -------------------------------------------------------------------------- */ - const Surface& BemFFTBase::getTractions() const { return surface_tractions; } /* -------------------------------------------------------------------------- */ const Surface& BemFFTBase::getDisplacements() const { return surface_displacements; } /* -------------------------------------------------------------------------- */ const Surface& BemFFTBase::getSpectralInfluenceOverDisplacement() { return surface_spectral_influence_disp; } /* -------------------------------------------------------------------------- */ void BemFFTBase::setEffectiveModulus(Real e_star) { this->e_star = e_star; this->computeSpectralInfluenceOverDisplacement(); } /* -------------------------------------------------------------------------- */ Real BemFFTBase::getEffectiveModulus() { return this->e_star; } /* -------------------------------------------------------------------------- */ void BemFFTBase::computeSpectralInfluenceOverDisplacement() { // if (std::isnan(tmp_coeff)) SURFACE_FATAL("constant tmp_coeff is nan"); // if (std::isnan(e_star)) SURFACE_FATAL("constant e_star is nan"); const Real L = surface.getL(); - auto wavevectors = FFTransform::computeFrequencies( + auto wavevectors = FFTEngine::template computeFrequencies( surface_spectral_influence_disp.sizes()); auto inf_it = surface_spectral_influence_disp.begin(), inf_begin = surface_spectral_influence_disp.begin(); auto end = wavevectors.end(2); //#pragma omp parallel for firstprivate(inf_it) for (auto it = wavevectors.begin(2); it < end; ++it) { inf_it = inf_begin; inf_it += it - wavevectors.begin(2); legacy::VectorProxy q_vec(&(*it), 2); q_vec *= 2 * M_PI / L; Real q = q_vec.l2norm(); // Influence function *inf_it = 2. / (q * e_star); } surface_spectral_influence_disp(0) = 0.; } /* -------------------------------------------------------------------------- */ void BemFFTBase::computeDisplacementsFromTractions() { this->applyInfluenceFunctions(surface_tractions, surface_displacements); } /* -------------------------------------------------------------------------- */ void BemFFTBase::computeTractionsFromDisplacements() { this->applyInverseInfluenceFunctions(surface_displacements, surface_tractions); } /* -------------------------------------------------------------------------- */ void BemFFTBase::applyInfluenceFunctions(Surface& input, Surface& output) { - FFTransform& plan1 = - FFTPlanManager::get().createPlan(input, surface_spectral_output); - - plan1.forwardTransform(); - + this->engine.forward(input, surface_spectral_output); surface_spectral_output *= surface_spectral_influence_disp; - - FFTransform& plan2 = - FFTPlanManager::get().createPlan(output, surface_spectral_output); - - plan2.backwardTransform(); + this->engine.backward(output, surface_spectral_output); } /* -------------------------------------------------------------------------- */ void BemFFTBase::applyInverseInfluenceFunctions(Surface& input, Surface& output) { - - FFTransform& plan1 = - FFTPlanManager::get().createPlan(input, surface_spectral_output); - - plan1.forwardTransform(); - + this->engine.forward(input, surface_spectral_output); surface_spectral_output /= surface_spectral_influence_disp; surface_spectral_output(0) = 0; - - FFTransform& plan2 = - FFTPlanManager::get().createPlan(output, surface_spectral_output); - - plan2.backwardTransform(); + this->engine.backward(output, surface_spectral_output); } /* -------------------------------------------------------------------------- */ void BemFFTBase::computeGaps() { UInt size = surface.size(); #pragma omp parallel for for (UInt i = 0; i < size * size; i++) { gap(i) = true_displacements(i) - surface(i); } } /* -------------------------------------------------------------------------- */ void BemFFTBase::computeTrueDisplacements() { this->true_displacements = this->surface_displacements; UInt n = surface.size(); UInt size = n * n; Real shift = 1e300; for (UInt i = 0; i < size; ++i) { if (surface_displacements(i) - shift - surface(i) < 0.) { shift = surface_displacements(i) - surface(i); } } for (UInt i = 0; i < size; ++i) { true_displacements(i) = surface_displacements(i) - shift; } } /* -------------------------------------------------------------------------- */ void BemFFTBase::addFunctional(Functional* new_funct) { this->functional->addFunctionalTerm(new_funct); } /* -------------------------------------------------------------------------- */ } // namespace tamaas diff --git a/src/bem/bem_fft_base.hh b/src/bem/bem_fft_base.hh index 2ae7a40..f489bb1 100644 --- a/src/bem/bem_fft_base.hh +++ b/src/bem/bem_fft_base.hh @@ -1,150 +1,148 @@ /** * @file * @section LICENSE * * Copyright (©) 2016-2020 EPFL (École Polytechnique Fédérale de Lausanne), * Laboratory (LSMS - Laboratoire de Simulation en Mécanique des Solides) * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published * by the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . * */ /* -------------------------------------------------------------------------- */ #ifndef BEM_FFT_BASE_H #define BEM_FFT_BASE_H /* -------------------------------------------------------------------------- */ #include "bem_functional.hh" #include "bem_interface.hh" #include "bem_meta_functional.hh" -#include "fftransform.hh" #include "surface_statistics.hh" /* -------------------------------------------------------------------------- */ namespace tamaas { class BemFFTBase : public BemInterface { /* ------------------------------------------------------------------------ */ /* Constructors/Destructors */ /* ------------------------------------------------------------------------ */ public: BemFFTBase(Surface& p); - virtual ~BemFFTBase(); /* ------------------------------------------------------------------------ */ /* Methods */ /* ------------------------------------------------------------------------ */ public: //! compute the displacement from traction input virtual void computeDisplacementsFromTractions(); //! compute the tractions from displacement input virtual void computeTractionsFromDisplacements(); //! apply influence funtions virtual void applyInfluenceFunctions(Surface& input, Surface& output); //! apply inverse influence funtions virtual void applyInverseInfluenceFunctions(Surface& input, Surface& output); //! compute the influcence coefficients of pressure over displacement virtual void computeSpectralInfluenceOverDisplacement(); //! compute the displacements virtual void computeTrueDisplacements(); //! compute the gap virtual void computeGaps(); //! get the number of iterations virtual UInt getNbIterations() const = 0; //! get the convergence steps of iterations virtual const std::vector& getConvergenceIterations() const = 0; /* ------------------------------------------------------------------------ */ /* Accessors */ /* ------------------------------------------------------------------------ */ public: //! return the influcence coefficients virtual const Surface& getSpectralInfluenceOverDisplacement(); //! return the actual tractions virtual const Surface& getTractions() const; //! return the actual displacements virtual const Surface& getDisplacements() const; //! get the computed true displacement const Surface& getTrueDisplacements() { return true_displacements; }; //! get the computed gap const Surface& getGap() { return gap; }; //! set the elastic effective modulus void setEffectiveModulus(Real e_star); //! set the elastic effective modulus Real getEffectiveModulus(); //! set the maximal number of iterations void setMaxIterations(UInt max_iter) { this->max_iterations = max_iter; }; //! get the maximal number of iterations UInt getMaxIterations() { return this->max_iterations; }; //! set dump freq void setDumpFreq(UInt dump_freq) { this->dump_freq = dump_freq; }; //! get dump freq UInt getDumpFreq() { return this->dump_freq; }; //! set functional void addFunctional(::tamaas::Functional* new_funct); /* ------------------------------------------------------------------------ */ /* Class Members */ /* ------------------------------------------------------------------------ */ protected: //! Functional for optimization MetaFunctional* functional; //! surface tractions Surface surface_tractions; //! surface displacement Surface surface_displacements; //! surface input in spectral SurfaceComplex surface_spectral_input; //! surface output in spectral SurfaceComplex surface_spectral_output; //! surface influence coefficient over displacement Surface surface_spectral_influence_disp; //! shifted displacement in order to get the true displacement Surface true_displacements; //! old displacements (for stopping criterion) Surface old_displacements; //! computing of the gap Surface gap; //! Effective young modulus Real e_star; //! maximum of iterations UInt max_iterations; //! number of iterations UInt nb_iterations; //! number of iterations std::vector convergence_iterations; //! frequency at which to dump iteration info UInt dump_freq; /* ------------------------------------------------------------------------ */ /* temporary trick about some obscure scaling factor */ /* ------------------------------------------------------------------------ */ /* 0 2 = 2*1 1 4 = 2*2 2 10 = 2*5 3 20 = 2*10 4 34 = 2*12 */ //#define tmp_coeff (2.*n/M_PI) //#define tmp_coeff (n/M_PI) const Real tmp_coeff; }; } // namespace tamaas #endif /* BEM_FFT_BASE_H */ diff --git a/src/bem/bem_grid.cpp b/src/bem/bem_grid.cpp index 6c3963c..8bcc155 100644 --- a/src/bem/bem_grid.cpp +++ b/src/bem/bem_grid.cpp @@ -1,442 +1,429 @@ /** * * @author Lucas Frérot * * @section LICENSE * * Copyright (©) 2016 EPFL (Ecole Polytechnique Fédérale de * Lausanne) Laboratory (LSMS - Laboratoire de Simulation en Mécanique des * Solides) * * Tamaas is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free * Software Foundation, either version 3 of the License, or (at your option) any * later version. * * Tamaas is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR * A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with Tamaas. If not, see . * */ /* -------------------------------------------------------------------------- */ #include "bem_grid.hh" -#include "fft_plan_manager.hh" /* -------------------------------------------------------------------------- */ namespace tamaas { BemGrid::BemGrid(Surface& surface) : E(1.), nu(0.3), surface(), surface_normals(surface.sizes(), 3), tractions(surface.sizes(), 3), displacements(surface.sizes(), 3), gaps(surface.sizes(), 3), true_tractions(nullptr), true_displacements(nullptr), dump_freq(100) { this->surface.wrap(surface); auto hermitian_sizes = GridHermitian::hermitianDimensions(surface.sizes()); influence.setNbComponents(9); spectral_tractions.setNbComponents(3); spectral_displacements.setNbComponents(3); influence.resize(hermitian_sizes); spectral_tractions.resize(hermitian_sizes); spectral_displacements.resize(hermitian_sizes); // computeSurfaceNormals(); } /* -------------------------------------------------------------------------- */ BemGrid::~BemGrid() { - FFTPlanManager::get().clean(); delete true_tractions; delete true_displacements; } /* -------------------------------------------------------------------------- */ const Grid& BemGrid::getTrueTractions() const { if (true_tractions == nullptr) TAMAAS_EXCEPTION("True tractions were not allocated"); return *true_tractions; } const Grid& BemGrid::getTrueDisplacements() const { if (true_displacements == nullptr) TAMAAS_EXCEPTION("True displacements were not allocated"); return *true_displacements; } /* -------------------------------------------------------------------------- */ /// This function computes surface slopes in fourier space and fills the normals /// grid void BemGrid::computeSurfaceNormals() { SurfaceComplex spectral_surface(surface.size(), surface.getL()); GridHermitian spectral_slopes(spectral_surface.sizes(), 3); - FFTransform& plan1 = - FFTPlanManager::get().createPlan(surface, spectral_surface); - FFTransform& plan2 = - FFTPlanManager::get().createPlan(surface_normals, spectral_slopes); - - plan1.forwardTransform(); + engine.forward(surface, spectral_surface); // Loops should be with signed integers std::array n; std::copy(spectral_surface.sizes().begin(), spectral_surface.sizes().end(), n.begin()); const Complex I(0, 1); #pragma omp parallel { #pragma omp for collapse(2) for (Int i = 0; i <= n[0] / 2; i++) { for (Int j = 0; j < n[1]; j++) { Real q1 = 2 * M_PI * j; Real q2 = 2 * M_PI * i; spectral_slopes(i, j, 0) = -q1 * I * spectral_surface(i, j); spectral_slopes(i, j, 1) = -q2 * I * spectral_surface(i, j); spectral_slopes(i, j, 2) = 0; } } #pragma omp for collapse(2) for (Int i = n[0] / 2 + 1; i < n[0]; i++) { for (Int j = 0; j < n[1]; j++) { Real q1 = 2 * M_PI * j; Real q2 = 2 * M_PI * (i - n[0]); spectral_slopes(i, j, 0) = -q1 * I * spectral_surface(i, j); spectral_slopes(i, j, 1) = -q2 * I * spectral_surface(i, j); spectral_slopes(i, j, 2) = 0; } } } // end #pragma omp parallel // Dirac spectral_slopes(0, 0, 2) = n[0] * n[0]; - plan2.backwardTransform(); - FFTPlanManager::get().destroyPlan(surface, spectral_surface); - FFTPlanManager::get().destroyPlan(surface_normals, spectral_slopes); + engine.backward(surface_normals, spectral_slopes); // Make surface normal legacy::VectorProxy sn(nullptr, 3); const UInt N = surface_normals.getNbPoints(); #pragma omp parallel for firstprivate(sn) for (UInt i = 0; i < N; i++) { sn.setPointer(surface_normals.getInternalData() + i * sn.dataSize()); sn /= sn.l2norm(); } } /* -------------------------------------------------------------------------- */ void BemGrid::computeDisplacementsFromTractions() { applyInfluenceFunctions(tractions, displacements); } void BemGrid::computeTractionsFromDisplacements() { applyInverseInfluenceFunctions(tractions, displacements); } /* -------------------------------------------------------------------------- */ void BemGrid::computeGaps(Grid& disp) { auto n = surface.sizes(); #pragma omp parallel for collapse(2) for (UInt i = 0; i < n[0]; i++) { for (UInt j = 0; j < n[1]; j++) { gaps(i, j, 0) = disp(i, j, 0); gaps(i, j, 1) = disp(i, j, 1); gaps(i, j, 2) = disp(i, j, 2) - surface(i, j); } } } /* -------------------------------------------------------------------------- */ void BemGrid::applyInfluenceFunctions(Grid& input, Grid& output) { TAMAAS_ASSERT(input.dataSize() == output.dataSize(), "input and output have different size"); - FFTransform& plan_1 = - FFTPlanManager::get().createPlan(input, spectral_tractions); - FFTransform& plan_2 = - FFTPlanManager::get().createPlan(output, spectral_displacements); - plan_1.forwardTransform(); + engine.forward(input, spectral_tractions); Complex *F = influence.getInternalData(), *p = spectral_tractions.getInternalData(), *u = spectral_displacements.getInternalData(); legacy::VectorProxy spectral_p(nullptr, 3), spectral_u(nullptr, 3); legacy::MatrixProxy spectral_F(nullptr, 3, 3); spectral_displacements = 0; const UInt N = influence.getNbPoints(); // OpenMP not very cool with this: best to have a POD iteration variable #pragma omp parallel for firstprivate(spectral_p, spectral_u, spectral_F) for (UInt i = 0; i < N; ++i) { spectral_p.setPointer(p + i * spectral_p.dataSize()); spectral_u.setPointer(u + i * spectral_u.dataSize()); spectral_F.setPointer(F + i * spectral_F.dataSize()); spectral_u.mul(spectral_F, spectral_p); } - plan_2.backwardTransform(); + engine.backward(output, spectral_displacements); } /* -------------------------------------------------------------------------- */ void BemGrid::applyInverseInfluenceFunctions( Grid& input[[gnu::unused]], Grid& output[[gnu::unused]]) { TAMAAS_EXCEPTION("applyInverseInfluenceFunctions not yet implemented"); } /* -------------------------------------------------------------------------- */ // warning: this function will suck your soul out // loops must be written with signed integers void BemGrid::computeInfluence() { // Iterator over points of surface legacy::MatrixProxy it(influence.getInternalData(), 3, 3); // Imaginary unit const Complex I(0, 1); const Int size = this->surface.size(); const Real L_1 = 1.; // this->surface.lengths()[0]; const Real L_2 = 1.; // this->surface.lengths()[1]; this->influence = 0; // Influence functions matrix, ask Lucas #define INFLUENCE(q_1, q_2, q, E, nu) \ { \ 2. / (E * q * q * q) * (-nu * nu * q_1 * q_1 - nu * q_2 * q_2 + q * q), \ -q_1 *q_2 / (E * q * q * q) * (1 + nu) * 2 * nu, \ I *q_1 / (E * q * q) * (1 + nu) * (1 - 2 * nu), \ -q_1 *q_2 / (E * q * q * q) * (1 + nu) * 2 * nu, \ 2. / (E * q * q * q) * \ (-nu * nu * q_2 * q_2 - nu * q_1 * q_1 + q * q), \ I *q_2 / (E * q * q) * (1 + nu) * (1 - 2 * nu), \ -I *q_1 / (E * q * q) * (1 + nu) * (1 - 2 * nu), \ -I *q_2 / (E * q * q) * (1 + nu) * (1 - 2 * nu), \ 2. / (E * q) * (1 - nu * nu) \ } // Fill influence for single point #define FILL_INFLUENCE(k, l) \ do { \ Real q_1 = 2 * M_PI * (l) / L_1; \ Real q_2 = 2 * M_PI * (k) / L_2; \ Real q = std::sqrt(q_1 * q_1 + q_2 * q_2); \ Complex influence_1[] = INFLUENCE(q_1, q_2, q, E, nu); \ legacy::MatrixProxy inf_mat(influence_1, 3, 3); \ it.setPointer(&influence(i, j, 0)); \ it = inf_mat; \ } while (0) // Fill influence points for "symetric" areas #define FILL_SYM_INFLUENCE(k, l) \ do { \ Real q_1 = 2 * M_PI * (l) / L_1; \ Real q_2 = 2 * M_PI * (k) / L_2; \ Real q = std::sqrt(q_1 * q_1 + q_2 * q_2); \ Complex influence_1[] = INFLUENCE(q_1, q_2, q, E, nu); \ Complex influence_2[] = INFLUENCE(q_2, q_1, q, E, nu); \ legacy::MatrixProxy inf_mat(influence_1, 3, 3); \ it.setPointer(&influence(i, j, 0)); \ it = inf_mat; \ inf_mat.setPointer(influence_2); \ it.setPointer(&influence(j, i, 0)); \ it = inf_mat; \ } while (0) #pragma omp parallel firstprivate( \ it) // all the following loops are independent { // Loops over parts of surface /* [ ][ ][ ][ ] [ ][x][ ][ ] [ ][ ][ ][ ] [ ][ ][ ][o] */ #pragma omp for schedule(dynamic) // can't collapse triangluar loops for (Int i = 1; i < size / 2; i++) { for (Int j = i; j < size / 2; j++) { FILL_SYM_INFLUENCE(i, j); } } /* [ ][ ][ ][ ] [ ][ ][ ][o] [ ][ ][ ][ ] [ ][x][ ][ ] */ #pragma omp for collapse(2) for (Int i = size / 2 + 1; i < size; i++) { for (Int j = 1; j < size / 2; j++) { FILL_INFLUENCE(i - size, j); } } /* [ ][x][x][o] [x][ ][x][ ] [x][x][x][ ] [ ][ ][ ][ ] */ #pragma omp for for (Int i = 1; i <= size / 2; i++) { { Int j = size / 2; FILL_SYM_INFLUENCE(i, j); } { Int j = 0; FILL_SYM_INFLUENCE(i, j); } } /* [ ][ ][ ][ ] [ ][ ][ ][ ] [ ][ ][ ][o] [x][ ][x][ ] */ #pragma omp for for (Int i = size / 2 + 1; i < size; i++) { { Int j = size / 2; FILL_INFLUENCE(i - size, j); } { Int j = 0; FILL_INFLUENCE(i - size, j); } } } // End #pragma omp parallel /* State of surface now: [0][x][x][o] [x][x][x][o] [x][x][x][o] [x][x][x][o] Legend: x => explicitely filled o => hermitian symetry 0 => actually 0 */ computeInfluenceLipschitz(); #undef INFLUENCE #undef FILL_INFLUENCE #undef FILL_SYM_INFLUENCE } /* -------------------------------------------------------------------------- */ /// Computing L_2 norm of influence functions void BemGrid::computeInfluenceLipschitz() { const UInt N = influence.getNbPoints(); legacy::VectorProxy m(nullptr, 9); Real _norm = 0.; #pragma omp parallel for firstprivate(m) reduction(+ : _norm) for (UInt i = 0; i < N; i++) { m.setPointer(influence.getInternalData() + i * m.dataSize()); _norm += pow(norm(m.l2norm()), 2); } lipschitz = sqrt(_norm); } /* -------------------------------------------------------------------------- */ /* Friction related computations */ /* -------------------------------------------------------------------------- */ void BemGrid::projectOnFrictionCone(Grid& field, Real mu) { legacy::VectorProxy p_proxy(nullptr, 3); const UInt N = field.getNbPoints(); #pragma omp parallel for firstprivate(p_proxy) for (UInt i = 0; i < N; i++) { p_proxy.setPointer(field.getInternalData() + i * p_proxy.dataSize()); projectOnFrictionCone(p_proxy, mu); } } /* -------------------------------------------------------------------------- */ void BemGrid::enforceMeanValue(Grid& field, const Grid& target) { Real red_0 = 0, red_1 = 0, red_2 = 0; auto n = field.sizes(); #pragma omp parallel for collapse(2) reduction(+ : red_0, red_1, red_2) for (UInt i = 0; i < n[0]; i++) { for (UInt j = 0; j < n[1]; j++) { red_0 += field(i, j, 0); red_1 += field(i, j, 1); red_2 += field(i, j, 2); } } red_0 /= n[0] * n[1]; red_1 /= n[0] * n[1]; red_2 /= n[0] * n[1]; Real p1_data[3] = {red_0, red_1, red_2}; // Loop for pressure shifting legacy::VectorProxy p1(p1_data, 3); legacy::VectorProxy p0((Real*)target.getInternalData(), 3); legacy::VectorProxy p(nullptr, 3); const UInt N = field.getNbPoints(); #pragma omp parallel for firstprivate(p0, p1) for (UInt i = 0; i < N; i++) { p.setPointer(field.getInternalData() + i * p.dataSize()); p -= p1; p += p0; } } /* -------------------------------------------------------------------------- */ void BemGrid::computeMeanValueError(Grid& field, const Grid& target, Grid& error_vec) { Real p0 = 0, p1 = 0, p2 = 0; auto n = field.sizes(); #pragma omp parallel for collapse(2) reduction(+ : p0, p1, p2) for (UInt i = 0; i < n[0]; i++) { for (UInt j = 0; j < n[1]; j++) { p0 += field(i, j, 0); p1 += field(i, j, 1); p2 += field(i, j, 2); } } p0 /= n[0] * n[1]; p1 /= n[0] * n[1]; p2 /= n[0] * n[1]; Real error[3] = {p0 - target(0), p1 - target(1), p2 - target(2)}; legacy::VectorProxy e(error, 3); error_vec = e; } /* -------------------------------------------------------------------------- */ Real BemGrid::computeMeanValueError(Grid& field, const Grid& target) { Real e[3] = {0.}; legacy::VectorProxy error(e, 3); computeMeanValueError(field, target, error); Real norm = target(0) * target(0) + target(1) * target(1) + target(2) * target(2); return error.l2norm() / std::sqrt(norm); } /* -------------------------------------------------------------------------- */ } // namespace tamaas diff --git a/src/bem/bem_grid.hh b/src/bem/bem_grid.hh index 844cd72..b8896e5 100644 --- a/src/bem/bem_grid.hh +++ b/src/bem/bem_grid.hh @@ -1,184 +1,187 @@ /** * * @author Lucas Frérot * * @section LICENSE * * Copyright (©) 2016 EPFL (Ecole Polytechnique Fédérale de * Lausanne) Laboratory (LSMS - Laboratoire de Simulation en Mécanique des * Solides) * * Tamaas is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free * Software Foundation, either version 3 of the License, or (at your option) any * later version. * * Tamaas is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR * A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with Tamaas. If not, see . * */ /* -------------------------------------------------------------------------- */ -#ifndef __BEM_GRID_HH__ -#define __BEM_GRID_HH__ +#ifndef BEM_GRID_HH +#define BEM_GRID_HH /* -------------------------------------------------------------------------- */ +#include "fft_engine.hh" #include "grid.hh" #include "surface.hh" #include "legacy_types.hh" /* -------------------------------------------------------------------------- */ namespace tamaas { /** * @brief abstract class for BEMs that use multi component fields */ class BemGrid { public: /// Constructor BemGrid(Surface& surface); /// Destructor virtual ~BemGrid(); public: /* ------------------------------------------------------------------------ */ /* Actual computations */ /* ------------------------------------------------------------------------ */ /// Compute normal vectors to surface void computeSurfaceNormals(); /// Compute influence coefficients void computeInfluence(); /// Compute a Lipschitz constant of gradient void computeInfluenceLipschitz(); /// Apply influence fonctions (p -> u) void applyInfluenceFunctions(Grid& input, Grid& output); /// Apply inverse influence fonctions (u -> p) void applyInverseInfluenceFunctions(Grid& input, Grid& output); /// Compute true tractions virtual void computeTrueTractions() = 0; /// Compute true displacements virtual void computeTrueDisplacements() = 0; /// Compute equilibrium virtual void computeEquilibrium(Real tol, const Grid& p_target) = 0; /// Compute displacements void computeDisplacementsFromTractions(); /// Compute tractions void computeTractionsFromDisplacements(); /// Compute gaps void computeGaps(Grid& disp); /* ------------------------------------------------------------------------ */ /* Field related computations */ /* ------------------------------------------------------------------------ */ /// Project point friction cone static inline void projectOnFrictionCone(legacy::VectorProxy& p, Real mu); /// Project field friction cone static void projectOnFrictionCone(Grid& field, Real mu); /// Project on mean value space static void enforceMeanValue(Grid& field, const Grid& target); /// Compute error relative to target mean value static Real computeMeanValueError(Grid& field, const Grid& target); /// Compute error vector static void computeMeanValueError(Grid& field, const Grid& target, Grid& error_vec); /* ------------------------------------------------------------------------ */ /* Getters */ /* ------------------------------------------------------------------------ */ /// Get influence const GridHermitian& getInfluence() const { return influence; } /// Get normals const Grid& getSurfaceNormals() const { return surface_normals; } /// Get tractions const Grid& getTractions() const { return tractions; } /// Get displacements const Grid& getDisplacements() const { return displacements; } /// Get true tractions const Grid& getTrueTractions() const; /// Get true displacements const Grid& getTrueDisplacements() const; /// Get gaps const Grid& getGaps() const { return gaps; } /* ------------------------------------------------------------------------ */ /* Setters */ /* ------------------------------------------------------------------------ */ /// Elasticity parameters void setElasticity(Real E_, Real nu_) { E = E_; nu = nu_; } /// Friction coefficient void setMu(Real mu_) { mu = mu_; } /// Max iterations void setMaxIterations(UInt n) { max_iter = n; } /// Dump frequency void setDumpFrequency(UInt n) { dump_freq = n; } protected: /// Elasticity constants Real E, nu; /// Friction coefficient Real mu; /// Lipschitz constant of functional gradient Real lipschitz; /// Max iterations UInt max_iter; /// Surface Surface surface; /// Normal vectors Grid surface_normals; /// Influcence matrices GridHermitian influence; /// Tractions Grid tractions; /// Displacements Grid displacements; /// Gap Grid gaps; /// True tractions Grid* true_tractions; /// True displacements Grid* true_displacements; /// Spectral tractions GridHermitian spectral_tractions; /// Spectral displacements GridHermitian spectral_displacements; /// Dump frequency UInt dump_freq; + /// FFT Engine + FFTEngine engine; }; /* -------------------------------------------------------------------------- */ /* Inline definitions */ /* -------------------------------------------------------------------------- */ inline void BemGrid::projectOnFrictionCone(legacy::VectorProxy& p, Real mu) { Real pt = std::sqrt(p(0) * p(0) + p(1) * p(1)); Real pn = p(2); Real x = (pn + mu * pt) / (1 + mu * mu); if (pt <= mu * pn && pn >= 0) // inside friction cone return; else if (mu * pt <= std::abs(pn) && pn < 0) // inside "project to origin" cone (undefined normal) // else if (pn < 0) // project to origin all points with pn < 0 p = 0; else { // projectable on cone p(0) *= mu / pt * x; p(1) *= mu / pt * x; p(2) = x; } } /* -------------------------------------------------------------------------- */ } // namespace tamaas #endif // __BEM_GRID_HH__ diff --git a/src/bem/bem_interface.hh b/src/bem/bem_interface.hh index 86f3877..2cc620b 100644 --- a/src/bem/bem_interface.hh +++ b/src/bem/bem_interface.hh @@ -1,100 +1,100 @@ /** * * @author Guillaume Anciaux * * @section LICENSE * * Copyright (©) 2016 EPFL (Ecole Polytechnique Fédérale de * Lausanne) Laboratory (LSMS - Laboratoire de Simulation en Mécanique des * Solides) * * Tamaas is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free * Software Foundation, either version 3 of the License, or (at your option) any * later version. * * Tamaas is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR * A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with Tamaas. If not, see . * */ /* -------------------------------------------------------------------------- */ #ifndef BEM_INTERFACE_H #define BEM_INTERFACE_H -#include "fft_plan_manager.hh" +#include "fft_engine.hh" #include "fftransform.hh" #include "surface.hh" /* -------------------------------------------------------------------------- */ namespace tamaas { class BemInterface { /* ------------------------------------------------------------------------ */ /* Constructors/Destructors */ /* ------------------------------------------------------------------------ */ public: BemInterface(Surface& surface) : surface(), spectral_surface(surface.size(), surface.getL()) { this->surface.wrap(surface); this->computeSpectralSurface(); }; virtual ~BemInterface(){}; /* ------------------------------------------------------------------------ */ /* Methods */ /* ------------------------------------------------------------------------ */ protected: //! compute the displacements from the tractions virtual void computeDisplacementsFromTractions(){ SURFACE_FATAL("TO BE IMPLEMENTED")}; //! compute the tractions from the displacements virtual void computeTractionsFromDisplacements(){ SURFACE_FATAL("TO BE IMPLEMENTED")}; public: //! compute the spectral surface virtual void computeSpectralSurface() { - FFTransform& plan = - FFTPlanManager::get().createPlan(surface, spectral_surface); - plan.forwardTransform(); + engine.forward(surface, spectral_surface); }; /* ------------------------------------------------------------------------ */ /* Accessors */ /* ------------------------------------------------------------------------ */ public: //! return the actual tractions virtual const Surface& getTractions() const = 0; //! return the actual displacements virtual const Surface& getDisplacements() const = 0; //! return the manipulated surface const Surface& getSurface() const { return this->surface; }; //! return the manipulated surface in fourier space const SurfaceComplex& getSpectralSurface() const { return this->spectral_surface; }; //! return the manipulated surface void setSurface(Surface& surface) { this->surface = surface; }; /* ------------------------------------------------------------------------ */ /* Class Members */ /* ------------------------------------------------------------------------ */ protected: //! the surface profile we work with Surface surface; //! the surface profile in fourier space SurfaceComplex spectral_surface; + //! fft engine + FFTEngine engine; }; } // namespace tamaas #endif /* BEM_INTERFACE_H */ diff --git a/src/core/surface_statistics.cpp b/src/core/surface_statistics.cpp index 9cc2178..efdcf15 100644 --- a/src/core/surface_statistics.cpp +++ b/src/core/surface_statistics.cpp @@ -1,535 +1,530 @@ /** * @file * @section LICENSE * * Copyright (©) 2016-2020 EPFL (École Polytechnique Fédérale de Lausanne), * Laboratory (LSMS - Laboratoire de Simulation en Mécanique des Solides) * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published * by the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . * */ /* -------------------------------------------------------------------------- */ #include "surface_statistics.hh" -#include "fft_plan_manager.hh" -#include "fftransform.hh" +#include "fft_engine.hh" /* -------------------------------------------------------------------------- */ namespace tamaas { +namespace { +FFTEngine engine; ///< local to this translation unit +} + /* -------------------------------------------------------------------------- */ std::vector SurfaceStatistics::computeSpectralDistribution( const Surface& surface, unsigned int n_bins, UInt cutoff) { if (n_bins == 0) SURFACE_FATAL("badly set number of bins"); const int n = surface.size(); Real min_value = SurfaceStatistics::computeMinimum(surface); Real max_value = SurfaceStatistics::computeMaximum(surface); std::vector distrib_fft(n_bins); const int N = n_bins; Real L = max_value - min_value; #pragma omp parallel for for (int k = 0; k < N; ++k) distrib_fft[k] = 0.; #pragma omp parallel for for (int k = 1; k < N / 2; ++k) { for (int i = 0; i < n * n; ++i) { Real value = surface(i); Real angle = -2. * M_PI * k * (value - min_value) / L; Complex phase = std::polar(1., angle); distrib_fft[k] += phase; distrib_fft[N - k] += phase; } } for (int i = 0; i < n * n; ++i) { Real value = surface(i); Real angle = -2. * M_PI * N / 2 * (value - min_value) / L; Complex phase = std::polar(1., angle); distrib_fft[N / 2] += phase; } #pragma omp parallel for for (int k = 1; k < N; ++k) distrib_fft[k] /= n * n; distrib_fft[0] = std::polar(1., 0.); #pragma omp parallel for for (int k = 1; k < N / 2; ++k) { if (k >= (int)cutoff) { distrib_fft[k] = 0.; distrib_fft[N - k] = 0.; } } - // proceed to inverse fourier transform + // proceed to inverse fourier transform #pragma omp parallel for for (int k = 0; k < N; ++k) { distrib_fft[k] = conj(distrib_fft[k]); } auto* in = (fftw_complex*)&distrib_fft[0]; fftw_plan p = fftw_plan_dft_1d(N, in, in, FFTW_FORWARD, FFTW_ESTIMATE); fftw_execute(p); fftw_destroy_plan(p); std::vector distrib; distrib.resize(N); #pragma omp parallel for for (int k = 0; k < N; ++k) { distrib[k] = conj(distrib_fft[k]); } return distrib; } /* -------------------------------------------------------------------------- */ Real SurfaceStatistics::computeAverageAmplitude(const Surface& surface) { return surface.mean(); } /* -------------------------------------------------------------------------- */ Complex SurfaceStatistics::computeAverageAmplitude( const SurfaceComplex& surface) { return surface.mean(); } /* -------------------------------------------------------------------------- */ Real SurfaceStatistics::computeContactArea(const Surface& surface) { Real L = surface.getL(); return SurfaceStatistics::computeContactAreaRatio(surface) * L * L; } /* -------------------------------------------------------------------------- */ Real SurfaceStatistics::computeContactAreaRatio(const Surface& surface) { UInt area = 0; UInt n = surface.size(); #pragma omp parallel for collapse(2), reduction(+ : area) for (UInt i = 0; i < n; i++) { for (UInt j = 0; j < n; j++) { if (surface(i, j) > 1e-10) area++; } } return area / Real(n * n); } /* -------------------------------------------------------------------------- */ Surface SurfaceStatistics::computeAutocorrelation(Surface& surface) { SurfaceComplex correlation_heights = computePowerSpectrum(surface); Surface acf(correlation_heights.size(), correlation_heights.getL()); - FFTransform& plan = - FFTPlanManager::get().createPlan(acf, correlation_heights); - plan.backwardTransform(); - FFTPlanManager::get().destroyPlan(acf, correlation_heights); + engine.backward(acf, correlation_heights); return acf; } /* -------------------------------------------------------------------------- */ SurfaceComplex SurfaceStatistics::computePowerSpectrum(Surface& surface) { SurfaceComplex power_spectrum(surface.size(), surface.getL()); - FFTransform& plan = - FFTPlanManager::get().createPlan(surface, power_spectrum); - plan.forwardTransform(); - + engine.forward(surface, power_spectrum); UInt n = power_spectrum.dataSize(); UInt factor = power_spectrum.size() * power_spectrum.size(); #pragma omp parallel for for (UInt i = 0; i < n; ++i) power_spectrum(i) /= factor; power_spectrum.makeItRealBySquare(); - FFTPlanManager::get().destroyPlan(surface, power_spectrum); return power_spectrum; } /* -------------------------------------------------------------------------- */ Real SurfaceStatistics::computeSkewness(const Surface& surface, Real avg, Real std) { Real skw = 0.0; UInt n = surface.size(); #pragma omp parallel for reduction(+ : skw) for (UInt i = 0; i < n * n; i++) { Real tmp = surface(i) - avg; skw += (tmp * tmp * tmp); } skw /= (n * std * std * std); return skw; } -/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ Real SurfaceStatistics::computeKurtosis(const Surface& surface, Real avg, Real std) { - Real kur = 0.0; UInt n = surface.size(); #pragma omp parallel for reduction(+ : kur) for (UInt i = 0; i < n * n; i++) { Real tmp = surface.size() - avg; kur += (tmp * tmp * tmp * tmp); } kur /= (n * std * std * std * std); return kur - 3; } -/* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ Real SurfaceStatistics::computeMaximum(const Surface& surface) { Real _max = -1. * std::numeric_limits::max(); UInt n = surface.size(); #pragma omp parallel for reduction(max : _max) for (UInt i = 0; i < n * n; i++) { _max = std::max(surface(i), _max); } return _max; } /* -------------------------------------------------------------------------- */ Real SurfaceStatistics::computeMinimum(const Surface& surface) { Real _min = std::numeric_limits::max(); UInt n = surface.size(); #pragma omp parallel for reduction(min : _min) for (UInt i = 0; i < n * n; i++) { _min = std::min(surface(i), _min); } return _min; } /* -------------------------------------------------------------------------- */ Real SurfaceStatistics::computeRMSSlope(const Surface& surface) { UInt n = surface.size(); Surface grad(n, 1.); for (UInt i = 0, m = 0; i < n; i++) { for (UInt j = 0; j < n; j++) { UInt k = i + n * j; // xl = ((i==0)?(k+n-1):(k-1)); UInt xh = ((i == (n - 1)) ? (k - n + 1) : (k + 1)); // yl = ((j==0)?(k+n*n-n):(k-n)); UInt yh = ((j == (n - 1)) ? (k - n * n + n) : (k + n)); Real gx = surface(xh) - surface(k); Real gy = surface(yh) - surface(k); grad(m++) = gx * gx + gy * gy; } } Real avg = SurfaceStatistics::computeAverage(grad); Real slp = sqrt(avg); /* According to paper Hyun PRE 70:026117 (2004) */ Real scale_factor = surface.getL() / n; return slp / scale_factor; } /* -------------------------------------------------------------------------- */ Real SurfaceStatistics::computeSpectralRMSSlope(Surface& surface) { SurfaceComplex power = computePowerSpectrum(surface); UInt n = surface.size(); Real rms_slopes = 0; // /!\ commented code was causing race conditions with dummy variable in // GridHermitian #pragma omp parallel for collapse(2), reduction(+ : rms_slopes) for (UInt i = 1; i < n / 2; ++i) for (UInt j = 1; j < n / 2; ++j) { rms_slopes += 2. * ((i * i) + (j * j)) * power(i, j).real(); // rms_slopes += 1.*((i*i)+(j*j))*power(n-i,n-j).real(); rms_slopes += 2. * ((i * i) + (j * j)) * power(n - i, j).real(); // rms_slopes += 1.*((i*i)+(j*j))*power(i,n-j).real(); } - // external line (i or j == 0) + // external line (i or j == 0) #pragma omp parallel for reduction(+ : rms_slopes) for (UInt i = 1; i < n / 2; ++i) { rms_slopes += 1. * (i * i) * power(i, 0).real(); rms_slopes += 1. * (i * i) * power(n - i, 0).real(); rms_slopes += 2. * (i * i) * power(0, i).real(); // rms_slopes += 1.*(i*i)*power(0,n-i).real(); } - // internal line (i or j == n/2) + // internal line (i or j == n/2) #pragma omp parallel for reduction(+ : rms_slopes) for (UInt i = 1; i < n / 2; ++i) { rms_slopes += 0.5 * (i * i + n * n / 4) * power(i, n / 2).real(); rms_slopes += 0.5 * (i * i + n * n / 4) * power(n - i, n / 2).real(); rms_slopes += 1. * (i * i + n * n / 4) * power(n / 2, i).real(); // rms_slopes += 0.5*(i*i+n*n/4)*power(n/2,n-i).real(); } { // i == n/2 j == n/2 rms_slopes += 0.25 * (n * n / 2) * power(n / 2, n / 2).real(); // i == 0 j == n/2 rms_slopes += 1. * (n * n / 4) * power(0, n / 2).real(); // i == n/2 j == 0 rms_slopes += 1. * (n * n / 4) * power(n / 2, 0).real(); } rms_slopes = 2. * M_PI / n * sqrt(rms_slopes); Real scale_factor = surface.getL() / n; return rms_slopes / scale_factor; } /* -------------------------------------------------------------------------- */ Real SurfaceStatistics::computeStdev(const Grid& surface, Real epsilon) { UInt n = surface.dataSize(); UInt cpt = 0; Real std = 0.0; Real avg = 0.0; #pragma omp parallel for reduction(+ : avg, std, cpt) for (UInt i = 0; i < n; i++) { Real val = surface(i); if (val > epsilon) { avg += val; std += val * val; ++cpt; } } avg /= cpt; return sqrt(std / cpt - avg * avg); } /* -------------------------------------------------------------------------- */ Real SurfaceStatistics::computeSpectralStdev(Surface& surface) { SurfaceComplex power = computePowerSpectrum(surface); Real rms_heights = 0; UInt n = surface.size(); #pragma omp parallel for collapse(2), reduction(+ : rms_heights) for (UInt i = 0; i < n; ++i) for (UInt j = 0; j < n; ++j) { rms_heights += power(i, j).real(); } rms_heights = sqrt(rms_heights); return rms_heights; } /* -------------------------------------------------------------------------- */ Real SurfaceStatistics::computeSpectralMeanCurvature(Surface& surface) { SurfaceComplex power = computePowerSpectrum(surface); UInt n = surface.size(); Real rms_curvatures = 0; #pragma omp parallel for collapse(2), reduction(+ : rms_curvatures) for (UInt i = 1; i < n / 2; ++i) for (UInt j = 1; j < n / 2; ++j) { Real coeff = ((i * i) + (j * j)); coeff *= coeff; rms_curvatures += 1. * coeff * power(i, j).real(); rms_curvatures += 1. * coeff * power(n - i, n - j).real(); rms_curvatures += 1. * coeff * power(n - i, j).real(); rms_curvatures += 1. * coeff * power(i, n - j).real(); } - // external line (i or j == 0) + // external line (i or j == 0) #pragma omp parallel for reduction(+ : rms_curvatures) for (UInt i = 1; i < n / 2; ++i) { Real coeff = i * i; coeff *= coeff; rms_curvatures += 4. / 3. * coeff * power(i, 0).real(); rms_curvatures += 4. / 3. * coeff * power(n - i, 0).real(); rms_curvatures += 4. / 3. * coeff * power(0, i).real(); rms_curvatures += 4. / 3. * coeff * power(0, n - i).real(); } - // internal line (i or j == n/2) + // internal line (i or j == n/2) #pragma omp parallel for reduction(+ : rms_curvatures) for (UInt i = 1; i < n / 2; ++i) { Real coeff = (i * i + n * n / 4); coeff *= coeff; rms_curvatures += 0.5 * coeff * power(i, n / 2).real(); rms_curvatures += 0.5 * coeff * power(n - i, n / 2).real(); rms_curvatures += 0.5 * coeff * power(n / 2, i).real(); rms_curvatures += 0.5 * coeff * power(n / 2, n - i).real(); } { // i == n/2 j == n/2 rms_curvatures += 0.25 * (n * n / 2) * (n * n / 2) * power(n / 2, n / 2).real(); // i == 0 j == n/2 rms_curvatures += 2. / 3. * (n * n / 4) * (n * n / 4) * power(0, n / 2).real(); // i == n/2 j == 0 rms_curvatures += 2. / 3. * (n * n / 4) * (n * n / 4) * power(n / 2, 0).real(); } rms_curvatures *= 9. / 4.; rms_curvatures = M_PI / n / n * sqrt(rms_curvatures); return rms_curvatures; } /* -------------------------------------------------------------------------- */ Real SurfaceStatistics::computePerimeter(const Surface& surface, Real eps) { int perimeter = 0; UInt n = surface.size(); #pragma omp parallel for collapse(2), reduction(+ : perimeter) for (UInt i = 0; i < n; ++i) { for (UInt j = 0; j < n; ++j) { if (j > 0 && ((surface(i, (j - 1)) < eps && surface(i, j) > eps) || (surface(i, (j - 1)) > eps && surface(i, j) < eps))) perimeter++; if (i > 0 && ((surface(j, (i - 1)) < eps && surface(j, i) > eps) || (surface(j, (i - 1)) > eps && surface(j, i) < eps))) perimeter++; } } return perimeter / Real(n); } /* -------------------------------------------------------------------------- */ Real SurfaceStatistics::computeSum(const Surface& surface) { Real sum = 0.; UInt n = surface.size(); #pragma omp parallel for reduction(+ : sum) for (UInt i = 0; i < n * n; ++i) sum += surface(i); return sum; } /* -------------------------------------------------------------------------- */ std::vector SurfaceStatistics::computeMoments(Surface& surface) { SurfaceComplex power = computePowerSpectrum(surface); Real L = surface.getL(); UInt n = surface.size(); Real k_factor = 2. * M_PI / L; std::vector> moment; moment.resize(6); for (int i = 0; i < 5; i++) moment[i].resize(5); for (UInt i = 0; i < n / 2; i++) { Real i0 = pow(Real(i) * k_factor, 0); Real i2 = pow(Real(i) * k_factor, 2); Real i4 = pow(Real(i) * k_factor, 4); Real im0 = pow(-Real(i) * k_factor, 0); Real im2 = pow(-Real(i) * k_factor, 2); Real im4 = pow(-Real(i) * k_factor, 4); for (UInt j = 0; j < n / 2; j++) { Real Phi = power(i, j).real(); Real Phi_10; Real Phi_01; Real Phi_11; if (i == 0 && j == 0) { Phi_10 = 0; Phi_11 = 0; Phi_01 = 0; } else if (i == 0) { Phi_10 = 0; Phi_11 = 0; Phi_01 = power(i, (n - j)).real(); } else if (j == 0) { Phi_10 = power(n - i, j).real(); Phi_11 = 0; Phi_01 = 0; } else { Phi_10 = power(n - i, j).real(); Phi_11 = power(n - i, (n - j)).real(); Phi_01 = power(i, (n - j)).real(); } Real j0 = pow(Real(j) * k_factor, 0); Real j2 = pow(Real(j) * k_factor, 2); Real j4 = pow(Real(j) * k_factor, 4); Real jm0 = pow(-Real(j) * k_factor, 0); Real jm2 = pow(-Real(j) * k_factor, 2); Real jm4 = pow(-Real(j) * k_factor, 4); moment[0][0] += i0 * j0 * Phi + im0 * j0 * Phi_10 + i0 * jm0 * Phi_01 + im0 * jm0 * Phi_11; moment[0][2] += i0 * j2 * Phi + im0 * j2 * Phi_10 + i0 * jm2 * Phi_01 + im0 * jm2 * Phi_11; moment[2][0] += i2 * j0 * Phi + im2 * j0 * Phi_10 + i2 * jm0 * Phi_01 + im2 * jm0 * Phi_11; moment[2][2] += i2 * j2 * Phi + im2 * j2 * Phi_10 + i2 * jm2 * Phi_01 + im2 * jm2 * Phi_11; moment[0][4] += i0 * j4 * Phi + im0 * j4 * Phi_10 + i0 * jm4 * Phi_01 + im0 * jm4 * Phi_11; moment[4][0] += i4 * j0 * Phi + im4 * j0 * Phi_10 + i4 * jm0 * Phi_01 + im4 * jm0 * Phi_11; } } std::vector mean_moments; // m2 Real mean_m2 = 0.5 * (moment[0][2] + moment[2][0]); mean_moments.push_back(mean_m2); // m4 Real mean_m4 = (3 * moment[2][2] + moment[0][4] + moment[4][0]) / 3.; mean_moments.push_back(mean_m4); return mean_moments; } /* -------------------------------------------------------------------------- */ Real SurfaceStatistics::computeAnalyticFractalMoment(UInt order, UInt k1, UInt k2, Real hurst, Real A, Real L) { Real xi = k2 / k1; Real _k1 = 2. * M_PI / L * k1; Real Cq; switch (order) { case 0: Cq = 2. * M_PI; break; case 2: Cq = M_PI; break; case 4: Cq = 3. / 4. * M_PI; break; default: SURFACE_FATAL("invalid order"); } Real exp = order - 2. * hurst; Real res = A * Cq * pow(_k1, exp) / exp * (pow(xi, exp) - 1); return res; } /* -------------------------------------------------------------------------- */ -} +} // namespace tamaas diff --git a/src/core/tamaas.cpp b/src/core/tamaas.cpp index 04200e1..8ff2c25 100644 --- a/src/core/tamaas.cpp +++ b/src/core/tamaas.cpp @@ -1,56 +1,55 @@ /** * @file * @section LICENSE * * Copyright (©) 2016-2020 EPFL (École Polytechnique Fédérale de Lausanne), * Laboratory (LSMS - Laboratoire de Simulation en Mécanique des Solides) * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published * by the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . * */ /* -------------------------------------------------------------------------- */ #include "tamaas.hh" #include "fft_plan_manager.hh" #include "loop.hh" #include #include /* -------------------------------------------------------------------------- */ namespace tamaas { void initialize(UInt num_threads) { if (num_threads) omp_set_num_threads(num_threads); // set user-defined number of threads // fftw_import_wisdom_from_filename("fftransform_fftw_wisdom"); #ifndef USE_CUDA if (!fftw_init_threads()) TAMAAS_EXCEPTION("FFTW could not initialize threads!"); fftw_plan_with_nthreads(omp_get_max_threads()); #endif } /* -------------------------------------------------------------------------- */ void finalize() { - FFTPlanManager::get().clean(); // fftw_export_wisdom_to_filename("fftransform_fftw_wisdom"); fftw_cleanup_threads(); } } // namespace tamaas diff --git a/src/model/volume_potential.cpp b/src/model/volume_potential.cpp index d30fb84..5cad2d7 100644 --- a/src/model/volume_potential.cpp +++ b/src/model/volume_potential.cpp @@ -1,115 +1,113 @@ /** * @file * @section LICENSE * * Copyright (©) 2016-2020 EPFL (École Polytechnique Fédérale de Lausanne), * Laboratory (LSMS - Laboratoire de Simulation en Mécanique des Solides) * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published * by the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . * */ /* -------------------------------------------------------------------------- */ #include "volume_potential.hh" #include "model.hh" #include /* -------------------------------------------------------------------------- */ namespace tamaas { template VolumePotential::VolumePotential(Model* model) : IntegralOperator(model) { // Copy horizontal sizes std::array sizes; std::copy(model->getDiscretization().begin() + 1, model->getDiscretization().end(), sizes.begin()); auto hermitian_sizes = GridHermitian::hermitianDimensions( sizes); wavevectors = - FFTransform::template computeFrequencies< - true>(hermitian_sizes); + FFTEngine::template computeFrequencies(hermitian_sizes); // Normalize wavevectors auto system_size = model->getBoundarySystemSize(); VectorProxy boundary_domain{ system_size[0]}; wavevectors *= 2 * M_PI; wavevectors /= boundary_domain; wavevectors *= -1.; // < this is important for the convolution computation } /* -------------------------------------------------------------------------- */ template model_type VolumePotential::getType() const { return this->model->getType(); } /* -------------------------------------------------------------------------- */ template void VolumePotential::initialize(UInt source_components, UInt out_components, UInt out_buffer_size) { auto hermitian_sizes = GridHermitian::hermitianDimensions( this->model->getBoundaryDiscretization()); // Initializing buffers this->source_buffer.resize(this->model->getDiscretization()[0]); this->out_buffer.resize(out_buffer_size); // Resizing source buffer std::for_each(this->source_buffer.begin(), this->source_buffer.end(), [&](auto& buffer) { buffer.setNbComponents(source_components); buffer.resize(hermitian_sizes); }); // Resizing output buffer std::for_each(this->out_buffer.begin(), this->out_buffer.end(), [&](auto& buffer) { buffer.setNbComponents(out_components); buffer.resize(hermitian_sizes); }); } /* -------------------------------------------------------------------------- */ template void VolumePotential::transformSource(GridBase& in, filter_t pred) const { constexpr UInt dim = trait::dimension; auto& i = dynamic_cast&>(in); // Transforming source for (UInt layer : Loop::range(i.sizes().front())) { if (not pred(layer)) continue; auto in_layer = make_view(i, layer); - FFTPlanManager::get() - .createPlan(in_layer, source_buffer[layer]) - .forwardTransform(); + engine.forward(in_layer, source_buffer[layer]); } - for (auto && layer : out_buffer) + for (auto&& layer : out_buffer) layer = 0; } /* -------------------------------------------------------------------------- */ /* Template instanciation */ /* -------------------------------------------------------------------------- */ template class VolumePotential; template class VolumePotential; } // namespace tamaas diff --git a/src/model/volume_potential.hh b/src/model/volume_potential.hh index f0d15f9..d2da1fd 100644 --- a/src/model/volume_potential.hh +++ b/src/model/volume_potential.hh @@ -1,142 +1,141 @@ /** * @file * @section LICENSE * * Copyright (©) 2016-2020 EPFL (École Polytechnique Fédérale de Lausanne), * Laboratory (LSMS - Laboratoire de Simulation en Mécanique des Solides) * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published * by the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . * */ /* -------------------------------------------------------------------------- */ #ifndef VOLUME_POTENTIAL_HH #define VOLUME_POTENTIAL_HH /* -------------------------------------------------------------------------- */ -#include "fft_plan_manager.hh" +#include "fft_engine.hh" #include "grid_hermitian.hh" #include "grid_view.hh" #include "integral_operator.hh" #include "logger.hh" #include "model_type.hh" /* -------------------------------------------------------------------------- */ #include /* -------------------------------------------------------------------------- */ namespace tamaas { /// Trait type for component management template struct derivative_traits; template <> struct derivative_traits<0> { template static constexpr auto source_components = model_type_traits::components; template static constexpr auto out_components = model_type_traits::components; }; template <> struct derivative_traits<1> { template static constexpr auto source_components = model_type_traits::voigt; template static constexpr auto out_components = model_type_traits::components; }; template <> struct derivative_traits<2> { template static constexpr auto source_components = model_type_traits::voigt; template static constexpr auto out_components = model_type_traits::voigt; }; /** * @brief Volume potential operator class. Applies the operators for computation * of displacements and strains due to residual/eigen strains */ template class VolumePotential : public IntegralOperator { using trait = model_type_traits; protected: using filter_t = std::function; public: VolumePotential(Model* model); /// Update from model (does nothing) void updateFromModel() override {} /// Kind IntegralOperator::kind getKind() const override { return IntegralOperator::neumann; } /// Type model_type getType() const override; /// Apply to all of the source layers void apply(GridBase& input, GridBase& output) const override { applyIf(input, output, [](UInt) { return true; }); } protected: /// Transform source layer-by-layer void transformSource(GridBase& in, filter_t pred) const; /// Transform all source void transformSource(GridBase& in) const { transformSource(in, [](auto) { return true; }); } /// Transform output layer-by-layer template void transformOutput(Func func, GridBase& out) const; /// Initialize fourier buffers void initialize(UInt source_components, UInt out_components, UInt source_buffer_size); protected: Grid wavevectors; using BufferType = GridHermitian; mutable std::vector source_buffer; mutable std::vector out_buffer; + mutable FFTEngine engine; }; /* -------------------------------------------------------------------------- */ /* Template implementation */ /* -------------------------------------------------------------------------- */ template template void VolumePotential::transformOutput(Func func, GridBase& out) const { constexpr UInt dim = trait::dimension; auto& o = dynamic_cast&>(out); // Transforming output for (UInt layer : Loop::range(o.sizes().front())) { Logger().get(LogLevel::debug) << "[VolumePotential] Computing layer " << layer << '\n'; auto out_layer = make_view(o, layer); auto& fourier_out_layer = func(out_buffer, layer); - FFTPlanManager::get() - .createPlan(out_layer, fourier_out_layer) - .backwardTransform(); + engine.backward(out_layer, fourier_out_layer); } } } // namespace tamaas #endif // VOLUME_POTENTIAL_HH diff --git a/src/solvers/contact_solver.cpp b/src/solvers/contact_solver.cpp index 79c2317..4812cf8 100644 --- a/src/solvers/contact_solver.cpp +++ b/src/solvers/contact_solver.cpp @@ -1,71 +1,65 @@ /** * @file * @section LICENSE * * Copyright (©) 2016-2020 EPFL (École Polytechnique Fédérale de Lausanne), * Laboratory (LSMS - Laboratoire de Simulation en Mécanique des Solides) * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published * by the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . * */ /* -------------------------------------------------------------------------- */ #include "contact_solver.hh" #include "fft_plan_manager.hh" #include "logger.hh" #include #include /* -------------------------------------------------------------------------- */ namespace tamaas { ContactSolver::ContactSolver(Model& model, const GridBase& surface, Real tolerance) : model(model), surface(), functional(), tolerance(tolerance), surface_stddev(std::sqrt(surface.var())) { auto discretization = model.getBoundaryDiscretization(); auto n_points = model.getTraction().getNbPoints(); if (n_points != surface.dataSize()) TAMAAS_EXCEPTION("Model size and surface size do not match!"); this->surface.wrap(surface); _gap = allocateGrid(model.getType(), discretization); model.registerField("gap", _gap); } /* -------------------------------------------------------------------------- */ -ContactSolver::~ContactSolver() { - FFTPlanManager::get().clean(); /// TODO find a smarter way to clean up -} - -/* -------------------------------------------------------------------------- */ - void ContactSolver::printState(UInt iter, Real cost_f, Real error) { // UInt precision = std::cout.precision(); if (iter % dump_frequency) return; Logger().get(LogLevel::info) << std::setw(5) << iter << " " << std::setw(15) << std::scientific << cost_f << " " << std::setw(15) << error << std::endl << std::fixed; } /* -------------------------------------------------------------------------- */ void ContactSolver::addFunctionalTerm(functional::Functional* func) { functional.addFunctionalTerm(func, false); } } // namespace tamaas /* -------------------------------------------------------------------------- */ diff --git a/src/solvers/contact_solver.hh b/src/solvers/contact_solver.hh index 45c2fa5..51b28f8 100644 --- a/src/solvers/contact_solver.hh +++ b/src/solvers/contact_solver.hh @@ -1,77 +1,77 @@ /** * @file * @section LICENSE * * Copyright (©) 2016-2020 EPFL (École Polytechnique Fédérale de Lausanne), * Laboratory (LSMS - Laboratoire de Simulation en Mécanique des Solides) * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published * by the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . * */ /* -------------------------------------------------------------------------- */ #ifndef __CONTACT_SOLVER_HH__ #define __CONTACT_SOLVER_HH__ /* -------------------------------------------------------------------------- */ #include "model.hh" #include "tamaas.hh" #include "meta_functional.hh" /* -------------------------------------------------------------------------- */ namespace tamaas { class ContactSolver { public: /// Constructor ContactSolver(Model& model, const GridBase& surface, Real tolerance); /// Destructor - virtual ~ContactSolver(); + virtual ~ContactSolver() = default; public: /// Print state of solve virtual void printState(UInt iter, Real cost_f, Real error); /// Set maximum number of iterations void setMaxIterations(UInt n) { max_iterations = n; } /// Set dump_frequency void setDumpFrequency(UInt n) { dump_frequency = n; } /// Add term to functional void addFunctionalTerm(functional::Functional* func); // Virtual functions public: virtual Real solve(std::vector /*load*/) { TAMAAS_EXCEPTION("solve() not implemented"); } virtual Real solve(Real load) { return solve(std::vector{load}); } public: GridBase& getSurface() { return surface; } Model& getModel() { return model; } protected: Model& model; GridBase surface; std::shared_ptr> _gap = nullptr; functional::MetaFunctional functional; Real tolerance; UInt max_iterations = 1000; Real surface_stddev; UInt dump_frequency = 100; }; } // namespace tamaas /* -------------------------------------------------------------------------- */ #endif // __CONTACT_SOLVER_HH__ diff --git a/src/surface/filter.hh b/src/surface/filter.hh index e4f9af1..aedff77 100644 --- a/src/surface/filter.hh +++ b/src/surface/filter.hh @@ -1,65 +1,65 @@ /** * @file * @section LICENSE * * Copyright (©) 2016-2020 EPFL (École Polytechnique Fédérale de Lausanne), * Laboratory (LSMS - Laboratoire de Simulation en Mécanique des Solides) * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published * by the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . * */ /* -------------------------------------------------------------------------- */ #ifndef FILTER_H #define FILTER_H /* -------------------------------------------------------------------------- */ -#include "fftransform.hh" +#include "fft_engine.hh" #include "grid.hh" #include "grid_hermitian.hh" /* -------------------------------------------------------------------------- */ namespace tamaas { template class Filter { public: /// Default constructor Filter() = default; /// Destructor virtual ~Filter() = default; public: /// Compute Filter coefficients virtual void computeFilter(GridHermitian& filter_coefficients) const = 0; protected: /// Compute filter coefficients using lambda template void computeFilter(T&& f, GridHermitian& filter) const; }; template template void Filter::computeFilter(T&& f, GridHermitian& filter) const { auto wavevectors = - FFTransform::template computeFrequencies(filter.sizes()); + FFTEngine::template computeFrequencies(filter.sizes()); Loop::loop(std::forward(f), filter, range>(wavevectors)); } } // namespace tamaas #endif // FILTER_H diff --git a/tests/test_integration.cpp b/tests/test_integration.cpp index 253df49..ff4726e 100644 --- a/tests/test_integration.cpp +++ b/tests/test_integration.cpp @@ -1,164 +1,164 @@ /** * @file * @section LICENSE * * Copyright (©) 2016-2020 EPFL (École Polytechnique Fédérale de Lausanne), * Laboratory (LSMS - Laboratoire de Simulation en Mécanique des Solides) * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published * by the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . * */ /* -------------------------------------------------------------------------- */ -#include "fftransform.hh" +#include "fft_engine.hh" #include "integration/accumulator.hh" #include "test.hh" #include #include /* -------------------------------------------------------------------------- */ using namespace tamaas; namespace ex = expolit; static Real tol = 1e-13; class AccumulatorTest : public ::testing::Test { protected: void SetUp() override { nodal_values.resize(n); for (auto&& grid : nodal_values) { grid.setNbComponents(6); grid.resize({nh, nh}); grid = 1; } accumulator.makeUniformMesh(nodal_values.size(), size); - wavevectors = FFTransform::computeFrequencies({nh, nh}); + wavevectors = FFTEngine::computeFrequencies({nh, nh}); } UInt n = 10, nh = 10; Real size = 3; std::vector> nodal_values; Grid wavevectors; Accumulator> accumulator; }; TEST_F(AccumulatorTest, uniformMesh) { auto& pos = this->accumulator.nodePositions(); std::vector sol(pos.size()); std::iota(sol.begin(), sol.end(), 0); std::transform(sol.begin(), sol.end(), sol.begin(), [&](auto& x) { return size * x / (sol.size() - 1); }); ASSERT_TRUE(compare(sol, pos, AreFloatEqual())) << "Uniform mesh fail"; } // Litteral type using Q = ex::Litteral; TEST_F(AccumulatorTest, forward) { // Setup for layer checking std::vector layers_seen, layers_to_see(n); std::iota(layers_to_see.begin(), layers_to_see.end(), 0); // Setup for integral checking constexpr Q q; constexpr auto z = ex::Polynomial({0, 1}); constexpr auto g0 = ex::exp(q * z); constexpr auto g1 = q * z * ex::exp(q * z); for (auto&& tuple : accumulator.forward(this->nodal_values, this->wavevectors)) { auto&& l = std::get<0>(tuple); auto&& xl_ = std::get<1>(tuple); auto&& acc_g0 = std::get<2>(tuple); auto&& acc_g1 = std::get<3>(tuple); layers_seen.push_back(l); // fundamental mode auto testing = acc_g0(0, 0, 0); ASSERT_NEAR(testing.real(), xl_, tol) << "Fundamental mode G0 fail"; testing = acc_g1(0, 0, 0); ASSERT_NEAR(testing.real(), 0, tol) << "Fundamental mode G1 fail"; // other modes testing = acc_g0(1, 0, 0); ASSERT_NEAR(testing.real(), acc_g0(0, 1, 0).real(), tol) << "Symmetry fail"; ASSERT_NEAR(testing.imag(), acc_g0(0, 1, 0).imag(), tol) << "Symmetry fail"; testing = acc_g0(1, 1, 0); Real sqrt_two = std::sqrt(Real(2)); auto ref_value = ex::definite_integral( std::pair(0, xl_), ex::substitute(ex::Constant({sqrt_two}), g0)); ASSERT_NEAR(testing.real(), ref_value, tol) << "Integration of exp(qy)*ϕ(y) fail"; testing = acc_g1(1, 1, 0); ref_value = ex::definite_integral( std::pair(0, xl_), ex::substitute(ex::Constant({sqrt_two}), g1)); ASSERT_NEAR(testing.real(), ref_value, tol) << "Integration of qy*exp(qy)*ϕ(y) fail"; } ASSERT_TRUE(compare(layers_seen, layers_to_see)) << "Did not see correct layers"; } TEST_F(AccumulatorTest, backward) { // Setup for layer checking std::vector layers_seen, layers_to_see(n); std::iota(layers_to_see.rbegin(), layers_to_see.rend(), 0); // Setup for integral checking constexpr Q q; constexpr auto z = ex::Polynomial({0, 1}); constexpr auto g0 = ex::exp(q * (z * (-1))); constexpr auto g1 = q * z * ex::exp(q * ((-1) * z)); for (auto&& tuple : accumulator.backward(nodal_values, wavevectors)) { auto&& l = std::get<0>(tuple); auto&& xl_ = std::get<1>(tuple); auto&& acc_g0 = std::get<2>(tuple); auto&& acc_g1 = std::get<3>(tuple); layers_seen.push_back(l); // fundamental mode auto testing = acc_g0(0, 0, 0); ASSERT_NEAR(testing.real(), size - xl_, tol) << "Fundamental mode G0 fail"; testing = acc_g1(0, 0, 0); ASSERT_NEAR(testing.real(), 0, tol) << "Fundamental mode G1 fail"; // other modes testing = acc_g0(1, 0, 0); ASSERT_NEAR(testing.real(), acc_g0(0, 1, 0).real(), tol) << "Symmetry fail"; ASSERT_NEAR(testing.imag(), acc_g0(0, 1, 0).imag(), tol) << "Symmetry fail"; testing = acc_g0(1, 1, 0); auto ref_value = ex::definite_integral( std::make_pair(xl_, size), ex::substitute(ex::Constant({std::sqrt(2)}), g0)); ASSERT_NEAR(testing.real(), ref_value, tol) << "Integration of exp(-qy)*ϕ(y) fail"; testing = acc_g1(1, 1, 0); ref_value = ex::definite_integral( std::make_pair(xl_, size), ex::substitute(ex::Constant({std::sqrt(2)}), g1)); ASSERT_NEAR(testing.real(), ref_value, tol) << "Integration of qy*exp(-qy)*ϕ(y) fail"; } ASSERT_TRUE(compare(layers_seen, layers_to_see)) << "Did not see correct layers"; }