diff --git a/examples/rough_contact.py b/examples/rough_contact.py index 1374ec4d..10f145ba 100644 --- a/examples/rough_contact.py +++ b/examples/rough_contact.py @@ -1,74 +1,74 @@ #!/usr/bin/env python3 # @file # @section LICENSE # # Copyright (©) 2016-19 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 <https://www.gnu.org/licenses/>. import tamaas as tm -import sys import matplotlib.pyplot as plt # Initialize threads and fftw tm.initialize() +tm.set_log_level(tm.LogLevel.info) # Show progression of solver # Surface size n = 512 # Surface generator sg = tm.SurfaceGeneratorFilter2D() sg.setSizes([n, n]) sg.random_seed = 0 # Spectrum spectrum = tm.Isopowerlaw2D() sg.setFilter(spectrum) # Parameters spectrum.q0 = 16 spectrum.q1 = 16 spectrum.q2 = 64 spectrum.hurst = 0.8 # Generating surface surface = sg.buildSurface() #surface /= tm.SurfaceStatistics.computeSpectralRMSSlope(surface) surface /= n #print(spectrum.rmsSlopes()) #print(tm.SurfaceStatistics.computeRMSSlope(surface)) plt.imshow(surface) # Creating model model = tm.ModelFactory.createModel(tm.model_type_basic_2d, [1., 1.], [n, n]) # Solver solver = tm.PolonskyKeerRey(model, surface, 1e-12, tm.PolonskyKeerRey.pressure, tm.PolonskyKeerRey.pressure) # Solve for target pressure p_target = 0.1 solver.solve(p_target) plt.figure() plt.imshow(model.getTraction()) print(model.getTraction().mean()) # Cleanup threads tm.finalize() plt.show() diff --git a/python/wrap/core.cpp b/python/wrap/core.cpp index 57b0930d..4f344c64 100644 --- a/python/wrap/core.cpp +++ b/python/wrap/core.cpp @@ -1,103 +1,112 @@ /** * @file * @section LICENSE * * Copyright (©) 2016-19 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 <https://www.gnu.org/licenses/>. * */ /* -------------------------------------------------------------------------- */ +#include "logger.hh" #include "statistics.hh" #include "surface_statistics.hh" #include "wrap.hh" #include <pybind11/stl.h> /* -------------------------------------------------------------------------- */ __BEGIN_TAMAAS__ /* -------------------------------------------------------------------------- */ namespace wrap { /* -------------------------------------------------------------------------- */ template <UInt dim> void wrapStatistics(py::module& mod) { auto name = makeDimensionName("Statistics", dim); py::class_<Statistics<dim>>(mod, name.c_str()) .def_static("computePowerSpectrum", &Statistics<dim>::computePowerSpectrum, py::return_value_policy::move) .def_static("computeAutocorrelation", &Statistics<dim>::computeAutocorrelation, py::return_value_policy::move) .def_static("computeMoments", &Statistics<dim>::computeMoments) .def_static("computeSpectralRMSSlope", &Statistics<dim>::computeSpectralRMSSlope) .def_static("computeRMSHeights", &Statistics<dim>::computeRMSHeights); } /* -------------------------------------------------------------------------- */ void wrapCore(py::module& mod) { + // Exposing logging facility + py::enum_<LogLevel>(mod, "LogLevel") + .value("debug", LogLevel::debug) + .value("info", LogLevel::info) + .value("warning", LogLevel::warning) + .value("error", LogLevel::error); + mod.def("set_log_level", [](LogLevel level) { Logger::setLevel(level); }); + // Exposing SurfaceStatistics (legacy) py::class_<SurfaceStatistics>(mod, "SurfaceStatistics") .def_static("computeMaximum", &SurfaceStatistics::computeMaximum) .def_static("computeMinimum", &SurfaceStatistics::computeMinimum) .def_static("computeSpectralRMSSlope", &SurfaceStatistics::computeSpectralRMSSlope) .def_static("computeRMSSlope", &SurfaceStatistics::computeRMSSlope) .def_static("computeMoments", &SurfaceStatistics::computeMoments) .def_static("computeSkewness", &SurfaceStatistics::computeSkewness) .def_static("computeKurtosis", &SurfaceStatistics::computeKurtosis) .def_static("computeSpectralMeanCurvature", &SurfaceStatistics::computeSpectralMeanCurvature) .def_static("computeSpectralStdev", &SurfaceStatistics::computeSpectralStdev) .def_static("computeAnalyticFractalMoment", &SurfaceStatistics::computeAnalyticFractalMoment) .def_static("computePerimeter", &SurfaceStatistics::computePerimeter) .def_static("computeContactArea", &SurfaceStatistics::computeContactArea) .def_static("computeContactAreaRatio", &SurfaceStatistics::computeContactAreaRatio) .def_static("computeSpectralDistribution", &SurfaceStatistics::computeSpectralDistribution) .def_static("computeSum", &SurfaceStatistics::computeSum) .def_static("computeAutocorrelation", &SurfaceStatistics::computeAutocorrelation, py::return_value_policy::copy) .def_static("computePowerSpectrum", &SurfaceStatistics::computePowerSpectrum, py::return_value_policy::copy); wrapStatistics<1>(mod); wrapStatistics<2>(mod); mod.def("to_voigt", [](const Grid<Real, 3>& field) { if (field.getNbComponents() == 9) { Grid<Real, 3> voigt(field.sizes(), 6); Loop::loop([](auto in, auto out) { out.symmetrize(in); }, range<MatrixProxy<const Real, 3, 3>>(field), range<SymMatrixProxy<Real, 3>>(voigt)); return voigt; } else TAMAAS_EXCEPTION("Wrong number of components to symmetrize"); }, "Convert a 3D tensor field to voigt notation", py::return_value_policy::copy); } } // namespace wrap /* -------------------------------------------------------------------------- */ __END_TAMAAS__ diff --git a/src/SConscript b/src/SConscript index 5b784cf1..570e3625 100644 --- a/src/SConscript +++ b/src/SConscript @@ -1,160 +1,161 @@ # -*- mode:python; coding: utf-8 -*- # vim: set ft=python: # @file # @section LICENSE # # Copyright (©) 2016-19 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 <https://www.gnu.org/licenses/>. import os Import('main_env') def prepend(path, list): return [os.path.join(path, x) for x in list] env = main_env.Clone() # Core core_list = """ fft_plan_manager.cpp fftransform.cpp fftransform_fftw.cpp grid.cpp grid_hermitian.cpp statistics.cpp surface.cpp tamaas.cpp legacy_types.cpp loop.cpp eigenvalues.cpp +logger.cpp """.split() core_list = prepend('core', core_list) if not main_env['strip_info']: core_list.append('tamaas_info.cpp') # Lib roughcontact generator_list = """ surface_generator.cpp surface_generator_filter.cpp surface_generator_filter_fft.cpp surface_generator_random_phase.cpp isopowerlaw.cpp regularized_powerlaw.cpp """.split() generator_list = prepend('surface', generator_list) # Lib PERCOLATION percolation_list = """ flood_fill.cpp """.split() percolation_list = prepend('percolation', percolation_list) # BEM PERCOLATION bem_list = """ bem_kato.cpp bem_polonski.cpp bem_gigi.cpp bem_gigipol.cpp bem_penalty.cpp bem_uzawa.cpp bem_fft_base.cpp bem_functional.cpp bem_meta_functional.cpp elastic_energy_functional.cpp exponential_adhesion_functional.cpp squared_exponential_adhesion_functional.cpp maugis_adhesion_functional.cpp complimentary_term_functional.cpp bem_grid.cpp bem_grid_polonski.cpp bem_grid_kato.cpp bem_grid_teboulle.cpp bem_grid_condat.cpp """.split() bem_list = prepend('bem', bem_list) # Model model_list = """ model.cpp model_factory.cpp model_type.cpp model_template.cpp be_engine.cpp westergaard.cpp elastic_functional.cpp meta_functional.cpp adhesion_functional.cpp volume_potential.cpp kelvin.cpp mindlin.cpp boussinesq.cpp elasto_plastic/isotropic_hardening.cpp elasto_plastic/elasto_plastic_model.cpp elasto_plastic/residual.cpp integration/element.cpp """.split() model_list = prepend('model', model_list) # Solvers solvers_list = """ contact_solver.cpp polonsky_keer_rey.cpp kato.cpp beck_teboulle.cpp condat.cpp polonsky_keer_tan.cpp ep_solver.cpp epic.cpp """.split() solvers_list = prepend('solvers', solvers_list) # GPU API gpu_list = """ fftransform_cufft.cpp """.split() gpu_list = prepend('gpu', gpu_list) # Assembling total list rough_contact_list = \ core_list + generator_list + percolation_list + model_list + solvers_list # For some reason collapse OMP loops don't compile on intel if env['CXX'] != 'icpc': rough_contact_list += bem_list # Adding GPU if needed if env['backend'] == 'cuda': rough_contact_list += gpu_list # Generating dependency files # env.AppendUnique(CXXFLAGS=['-MMD']) # for file_name in rough_contact_list: # obj = file_name.replace('.cpp', '.os') # dep_file_name = file_name.replace('.cpp', '.d') # env.SideEffect(dep_file_name, obj) # env.ParseDepends(dep_file_name) # Adding extra warnings for Tamaas base lib env.AppendUnique(CXXFLAGS=['-Wextra']) libTamaas = env.SharedLibrary('Tamaas', rough_contact_list) Export('libTamaas') diff --git a/src/surface/surface_generator_filter_fft.cpp b/src/core/logger.cpp similarity index 50% copy from src/surface/surface_generator_filter_fft.cpp copy to src/core/logger.cpp index 4cc35974..3b7fb32d 100644 --- a/src/surface/surface_generator_filter_fft.cpp +++ b/src/core/logger.cpp @@ -1,55 +1,62 @@ /** * @file * @section LICENSE * * Copyright (©) 2016-19 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 <https://www.gnu.org/licenses/>. * */ /* -------------------------------------------------------------------------- */ - +#include "logger.hh" +#include <boost/preprocessor/seq.hpp> +#include <boost/preprocessor/stringize.hpp> +#include <cstdlib> /* -------------------------------------------------------------------------- */ -#include "surface_generator_filter_fft.hh" -#include "isopowerlaw.hh" -#include "surface_statistics.hh" +namespace tamaas { /* -------------------------------------------------------------------------- */ - -__BEGIN_TAMAAS__ - -extern template class Grid<Real, 2>; - -SurfaceGeneratorFilterFFT::SurfaceGeneratorFilterFFT() { - pl_filter = std::make_shared<Isopowerlaw<2>>(); - generator.setFilter(pl_filter); +LogLevel Logger::current_level = LogLevel::info; + +std::ostream& operator<<(std::ostream& o, const LogLevel& val) { + switch (val) { +#define PRINT_LOGLEVEL(r, data, type) \ + case data::type: \ + o << BOOST_PP_STRINGIZE(type); \ + break; + BOOST_PP_SEQ_FOR_EACH(PRINT_LOGLEVEL, LogLevel, + (debug)(info)(warning)(error)); +#undef PRINT_LOGLEVEL + } + return o; } -SurfaceGeneratorFilterFFT::~SurfaceGeneratorFilterFFT() {} - -Real SurfaceGeneratorFilterFFT::constrainRMS() { - std::cerr << "Waring /!\\ :: Statistical bias in using the generated RMS as " - "normalizing factor" - << std::endl; - Real hrms_grid = SurfaceStatistics::computeStdev(generator.grid); - generator.grid *= rms / hrms_grid; - return rms; +Logger::~Logger() { + if (wish_level >= current_level) { + fprintf(stderr, "%s", stream.str().c_str()); + fflush(stderr); + } } -Grid<Real, 2>& SurfaceGeneratorFilterFFT::buildSurface() { - generator.setSizes({{grid_size, grid_size}}); - return generator.buildSurface(); +std::ostream& Logger::get(LogLevel level) { + wish_level = level; + // std::string name; + // stream << level << ": "; + return stream; } -__END_TAMAAS__ +void Logger::setLevel(LogLevel level) { current_level = level; } + +/* -------------------------------------------------------------------------- */ +} // namespace tamaas diff --git a/src/surface/surface_generator_filter_fft.cpp b/src/core/logger.hh similarity index 57% copy from src/surface/surface_generator_filter_fft.cpp copy to src/core/logger.hh index 4cc35974..2594d98e 100644 --- a/src/surface/surface_generator_filter_fft.cpp +++ b/src/core/logger.hh @@ -1,55 +1,56 @@ /** * @file * @section LICENSE * * Copyright (©) 2016-19 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 <https://www.gnu.org/licenses/>. * */ /* -------------------------------------------------------------------------- */ - +#ifndef LOGGER_HH +#define LOGGER_HH +/* -------------------------------------------------------------------------- */ +#include "tamaas.hh" +#include <sstream> /* -------------------------------------------------------------------------- */ -#include "surface_generator_filter_fft.hh" -#include "isopowerlaw.hh" -#include "surface_statistics.hh" +namespace tamaas { /* -------------------------------------------------------------------------- */ -__BEGIN_TAMAAS__ +/// Log levels enumeration +enum class LogLevel { debug = 0, info = 1, warning = 2, error = 3 }; -extern template class Grid<Real, 2>; +/// Logging class +class Logger { +public: + /// Writing to stderr + ~Logger(); -SurfaceGeneratorFilterFFT::SurfaceGeneratorFilterFFT() { - pl_filter = std::make_shared<Isopowerlaw<2>>(); - generator.setFilter(pl_filter); -} + /// Get stream + std::ostream& get(LogLevel level = LogLevel::debug); -SurfaceGeneratorFilterFFT::~SurfaceGeneratorFilterFFT() {} + /// Set acceptable logging level + static void setLevel(LogLevel level); -Real SurfaceGeneratorFilterFFT::constrainRMS() { - std::cerr << "Waring /!\\ :: Statistical bias in using the generated RMS as " - "normalizing factor" - << std::endl; - Real hrms_grid = SurfaceStatistics::computeStdev(generator.grid); - generator.grid *= rms / hrms_grid; - return rms; -} +private: + static LogLevel current_level; -Grid<Real, 2>& SurfaceGeneratorFilterFFT::buildSurface() { - generator.setSizes({{grid_size, grid_size}}); - return generator.buildSurface(); -} + std::ostringstream stream; + LogLevel wish_level = LogLevel::debug; +}; +/* -------------------------------------------------------------------------- */ +} // namespace tamaas -__END_TAMAAS__ +#endif // LOGGER_HH diff --git a/src/core/tamaas.hh b/src/core/tamaas.hh index 2e1652d5..4c191288 100644 --- a/src/core/tamaas.hh +++ b/src/core/tamaas.hh @@ -1,175 +1,174 @@ /** * @mainpage Welcome to Tamaas ! * * @section Introduction * Tamaas is a spectral-integral-equation based contact library. It is made * with love to be fast and friendly! * * @author Guillaume Anciaux <guillaume.anciaux@epfl.ch> * @author Lucas Frérot <lucas.frerot@epfl.ch> * @author Valentine Rey <valentine.rey@epfl.ch> * @author Son Pham-Ba <son.phamba@epfl.ch> * * @section LICENSE * * Copyright (©) 2016-19 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 <https://www.gnu.org/licenses/>. * */ /* -------------------------------------------------------------------------- */ #ifndef __TAMAAS_HH__ #define __TAMAAS_HH__ /* -------------------------------------------------------------------------- */ // Standard includes #include <exception> #include <iostream> #include <memory> #include <string> /* -------------------------------------------------------------------------- */ // Special thrust includes #include <thrust/complex.h> #include <thrust/random.h> #ifdef USE_CUDA #include "unified_allocator.hh" #else #include "fftw_allocator.hh" #endif /// @section Namespace macros #define __BEGIN_TAMAAS__ namespace tamaas { #define __END_TAMAAS__ } /// @section Cuda specific definitions #define CUDA_LAMBDA __device__ __host__ #ifdef USE_CUDA #define DEFAULT_ALLOCATOR UnifiedAllocator<T> #else #define DEFAULT_ALLOCATOR FFTWAllocator<T> #endif /* -------------------------------------------------------------------------- */ /// @section Standard definitions pulled from C++14 namespace std { #if __cplusplus < 201402L /// exchange template <class T, class U = T> T exchange(T& obj, U&& new_value) { T old_value = move(obj); obj = forward<U>(new_value); return old_value; } /// make_unique template <typename T, typename... Args> unique_ptr<T> make_unique(Args&&... args) { return unique_ptr<T>(new T(forward<Args>(args)...)); } #endif } // namespace std /* -------------------------------------------------------------------------- */ __BEGIN_TAMAAS__ /* -------------------------------------------------------------------------- */ /// @section Common types definitions using Real = double; ///< default floating point type using UInt = unsigned int; ///< default unsigned integer type using Int = int; ///< default signed integer type template <typename T> using complex = thrust::complex<T>; ///< template complex wrapper using Complex = complex<Real>; ///< default floating point complex type static constexpr Real zero_threshold = 1e-14; /// @section Defining random toolbox using ::thrust::random::normal_distribution; using ::thrust::random::uniform_real_distribution; using random_engine = ::thrust::random::default_random_engine; namespace detail { template <bool acc, template <typename> class Trait, typename Head, typename... Tail> struct fold_trait_tail_rec : std::integral_constant<bool, fold_trait_tail_rec<acc and Trait<Head>::value, Trait, Tail...>::value> {}; template <bool acc, template <typename> class Trait, typename Head> struct fold_trait_tail_rec<acc, Trait, Head> : std::integral_constant<bool, acc and Trait<Head>::value> {}; } // namespace detail template <template <typename> class Trait, typename... T> struct fold_trait : detail::fold_trait_tail_rec<true, Trait, T...> {}; /* -------------------------------------------------------------------------- */ /// initialize tamaas (0 threads => let OMP_NUM_THREADS decide) void initialize(UInt num_threads = 0); /// cleanup tamaas void finalize(); /* -------------------------------------------------------------------------- */ /// Generic exception class class Exception : public std::exception { public: /// Constructor Exception(const std::string& mesg) : msg(mesg) {} virtual const char* what() const noexcept { return msg.c_str(); } virtual ~Exception() = default; private: std::string msg; ///< message of exception }; /* -------------------------------------------------------------------------- */ /// Enumeration of reduction operations enum class operation { plus, times, min, max }; /* -------------------------------------------------------------------------- */ __END_TAMAAS__ /* -------------------------------------------------------------------------- */ /// @section Convenience macros #define TAMAAS_EXCEPTION(mesg) \ { \ std::stringstream sstr; \ - sstr << __FILE__ << ":" << __LINE__ << ":FATAL: " << mesg << std::endl; \ - std::cerr.flush(); \ + sstr << __FILE__ << ":" << __LINE__ << ":FATAL: " << mesg << '\n'; \ throw ::tamaas::Exception(sstr.str()); \ } #define SURFACE_FATAL(mesg) TAMAAS_EXCEPTION(mesg) #if defined(TAMAAS_DEBUG) #define TAMAAS_ASSERT(cond, reason) \ do { \ if (!(cond)) { \ TAMAAS_EXCEPTION(#cond " assert failed: " << reason); \ } \ } while (0) #define TAMAAS_DEBUG_EXCEPTION(reason) TAMAAS_EXCEPTION(reason) #else #define TAMAAS_ASSERT(cond, reason) #define TAMAAS_DEBUG_EXCEPTION(reason) #endif #define TAMAAS_ACCESSOR(var, type, name) \ type& get##name() { return var; } \ void set##name(const type& new_var) { var = new_var; } /* -------------------------------------------------------------------------- */ #endif // TAMAAS_HH diff --git a/src/model/be_engine.cpp b/src/model/be_engine.cpp index b0f3f1e0..a77d89cf 100644 --- a/src/model/be_engine.cpp +++ b/src/model/be_engine.cpp @@ -1,82 +1,85 @@ /** * @file * @section LICENSE * * Copyright (©) 2016-19 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 <https://www.gnu.org/licenses/>. * */ /* -------------------------------------------------------------------------- */ #include "be_engine.hh" +#include "logger.hh" #include "model.hh" /* -------------------------------------------------------------------------- */ namespace tamaas { /* -------------------------------------------------------------------------- */ void solve(IntegralOperator::kind kind, const std::map<IntegralOperator::kind, IntegralOperator*>& operators, GridBase<Real>& input, GridBase<Real>& output) try { operators.at(kind)->apply(input, output); } catch (std::out_of_range& e) { - std::cerr << "Operator (" << "Westergaard::" << kind << ") not registered\n"; + Logger().get(LogLevel::warning) + << "Operator (" + << "Westergaard::" << kind << ") not registered\n"; throw e; } template <model_type mtype, IntegralOperator::kind otype> void registerWestergaardOperator( std::map<IntegralOperator::kind, IntegralOperator*>& operators, Model& model) { std::stringstream sstr; sstr << "Westergaard::" << otype; if (operators.find(otype) == operators.end()) operators[otype] = model.template registerIntegralOperator<Westergaard<mtype, otype>>( sstr.str()); } template <model_type type> void BEEngineTmpl<type>::solveNeumann(GridBase<Real>& neumann, GridBase<Real>& dirichlet) const { solve(IntegralOperator::neumann, this->operators, neumann, dirichlet); } template <model_type type> void BEEngineTmpl<type>::solveDirichlet(GridBase<Real>& dirichlet, GridBase<Real>& neumann) const { solve(IntegralOperator::dirichlet, this->operators, dirichlet, neumann); } template <model_type type> void BEEngineTmpl<type>::registerNeumann() { registerWestergaardOperator<type, IntegralOperator::neumann>(this->operators, *this->model); } /// Register dirichlet operator template <model_type type> void BEEngineTmpl<type>::registerDirichlet() { registerWestergaardOperator<type, IntegralOperator::dirichlet>( this->operators, *this->model); } template class BEEngineTmpl<model_type::basic_1d>; template class BEEngineTmpl<model_type::basic_2d>; template class BEEngineTmpl<model_type::surface_1d>; template class BEEngineTmpl<model_type::surface_2d>; template class BEEngineTmpl<model_type::volume_1d>; template class BEEngineTmpl<model_type::volume_2d>; /* -------------------------------------------------------------------------- */ } // namespace tamaas diff --git a/src/model/model.cpp b/src/model/model.cpp index 49fd58bd..68110286 100644 --- a/src/model/model.cpp +++ b/src/model/model.cpp @@ -1,151 +1,154 @@ /** * @file * @section LICENSE * * Copyright (©) 2016-19 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 <https://www.gnu.org/licenses/>. * */ /* -------------------------------------------------------------------------- */ #include "model.hh" #include "be_engine.hh" +#include "logger.hh" /* -------------------------------------------------------------------------- */ __BEGIN_TAMAAS__ /* -------------------------------------------------------------------------- */ Model::Model(const std::vector<Real>& system_size, const std::vector<UInt>& discretization) : system_size(system_size), discretization(discretization) {} /* -------------------------------------------------------------------------- */ Model::~Model() = default; /* -------------------------------------------------------------------------- */ void Model::setElasticity(Real E, Real nu) { this->E = E; this->nu = nu; updateOperators(); } /* -------------------------------------------------------------------------- */ Real Model::getHertzModulus() const { return E / (1 - nu * nu); } /* -------------------------------------------------------------------------- */ GridBase<Real>& Model::getTraction() { return getField("traction"); } const GridBase<Real>& Model::getTraction() const { return getField("traction"); } /* -------------------------------------------------------------------------- */ GridBase<Real>& Model::getDisplacement() { return getField("displacement"); } const GridBase<Real>& Model::getDisplacement() const { return getField("displacement"); } /* -------------------------------------------------------------------------- */ const std::vector<Real>& Model::getSystemSize() const { return system_size; } /* -------------------------------------------------------------------------- */ const std::vector<UInt>& Model::getDiscretization() const { return discretization; } /* -------------------------------------------------------------------------- */ void Model::solveNeumann() { engine->registerNeumann(); engine->solveNeumann(getField("traction"), getField("displacement")); } void Model::solveDirichlet() { engine->registerDirichlet(); engine->solveDirichlet(getField("displacement"), getField("traction")); } /* -------------------------------------------------------------------------- */ IntegralOperator* Model::getIntegralOperator(const std::string& name) { return operators[name].get(); } /* -------------------------------------------------------------------------- */ void Model::updateOperators() { for (auto& op : operators) op.second->updateFromModel(); } /* -------------------------------------------------------------------------- */ void Model::addDumper(std::shared_ptr<ModelDumper> dumper) { this->dumpers.push_back(dumper); } void Model::dump() const { for (auto& dumper : dumpers) if (dumper) dumper->dump(*this); } /* -------------------------------------------------------------------------- */ void Model::registerField(const std::string& name, std::shared_ptr<GridBase<Real>> field) { fields[name] = std::move(field); } const GridBase<Real>& Model::getField(const std::string& name) const try { return *fields.at(name); } catch (std::out_of_range& e) { - std::cerr << "Field " << name << " not registered in model\n"; + Logger().get(LogLevel::warning) + << "Field " << name << " not registered in model\n"; throw e; } GridBase<Real>& Model::getField(const std::string& name) try { return *fields.at(name); } catch (std::out_of_range& e) { - std::cerr << "Field " << name << " not registered in model\n"; + Logger().get(LogLevel::warning) + << "Field " << name << " not registered in model\n"; throw e; } /* -------------------------------------------------------------------------- */ std::ostream& operator<<(std::ostream& o, const Model& _this) { o << "Model<" << _this.getType() << "> (E = " << _this.getYoungModulus() << ", nu = " << _this.getPoissonRatio() << ")\n"; // Printing domain size o << " - domain = ["; std::for_each(_this.getSystemSize().begin(), _this.getSystemSize().end() - 1, [&o](const Real& x) { o << x << ", "; }); o << _this.getSystemSize().back() << "]\n"; // Printing discretization o << " - discretization = ["; std::for_each(_this.getDiscretization().begin(), _this.getDiscretization().end() - 1, [&o](const UInt& x) { o << x << ", "; }); o << _this.getDiscretization().back() << "]"; return o; } /* -------------------------------------------------------------------------- */ __END_TAMAAS__ diff --git a/src/solvers/beck_teboulle.cpp b/src/solvers/beck_teboulle.cpp index 903ada04..ce0072e4 100644 --- a/src/solvers/beck_teboulle.cpp +++ b/src/solvers/beck_teboulle.cpp @@ -1,107 +1,108 @@ /** * @file * @section LICENSE * * Copyright (©) 2016-19 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 <https://www.gnu.org/licenses/>. * */ /* -------------------------------------------------------------------------- */ #include "beck_teboulle.hh" +#include "logger.hh" #include <iomanip> /* -------------------------------------------------------------------------- */ __BEGIN_TAMAAS__ BeckTeboulle::BeckTeboulle(Model& model, const GridBase<Real>& surface, Real tolerance, Real mu) : Kato(model, surface, tolerance, mu) {} /* -------------------------------------------------------------------------- */ Real BeckTeboulle::solve(GridBase<Real>& g0) { if (g0.getNbPoints() != pressure->getNbComponents()) { TAMAAS_EXCEPTION( - "Target mean gap does not have the right number of components"); + "Target mean gap does not have the right number of components"); } Real cost = 0; switch (model.getType()) { case model_type::surface_1d: cost = solveTmpl<model_type::surface_1d>(g0); break; case model_type::surface_2d: cost = solveTmpl<model_type::surface_2d>(g0); break; default: break; } return cost; } template <model_type type> Real BeckTeboulle::solveTmpl(GridBase<Real>& g0) { // Printing column headers - std::cout << std::setw(5) << "Iter" - << " " << std::setw(15) << "Cost_f" - << " " << std::setw(15) << "Error" << '\n' - << std::fixed; + Logger().get(LogLevel::info) << std::setw(5) << "Iter" + << " " << std::setw(15) << "Cost_f" + << " " << std::setw(15) << "Error" << '\n' + << std::fixed; constexpr UInt comp = model_type_traits<type>::components; Real cost = 0; UInt n = 0; *pressure = 0; GridBase<Real> z(*pressure); GridBase<Real> pressure_old(*pressure); Real t0 = 1; Real t1 = 1; Real m = 0; Real lipschitz = engine.getNeumannNorm(); do { engine.solveNeumann(*pressure, *gap); addUniform<comp>(*gap, g0); *gap -= *surfaceComp; z = *gap; z /= -lipschitz; *pressure += z; enforcePressureCoulomb<comp>(); z = *pressure; t1 = 1 + std::sqrt(4 * t0 * t0 + 1) / 2; m = 1 + (t0 - 1) / t1; z -= pressure_old; z *= m; z += pressure_old; t0 = t1; pressure_old = *pressure; cost = computeCost(); printState(n, cost, cost); } while (cost > this->tolerance && n++ < this->max_iterations); computeFinalGap<comp>(); return cost; } __END_TAMAAS__ -/* -------------------------------------------------------------------------- */ \ No newline at end of file +/* -------------------------------------------------------------------------- */ diff --git a/src/solvers/condat.cpp b/src/solvers/condat.cpp index 582d6cb3..c8015bbd 100644 --- a/src/solvers/condat.cpp +++ b/src/solvers/condat.cpp @@ -1,149 +1,152 @@ /** * @file * @section LICENSE * * Copyright (©) 2016-19 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 <https://www.gnu.org/licenses/>. * */ /* -------------------------------------------------------------------------- */ #include "condat.hh" +#include "logger.hh" #include <iomanip> /* -------------------------------------------------------------------------- */ __BEGIN_TAMAAS__ -Condat::Condat(Model& model, const GridBase<Real>& surface, - Real tolerance, Real mu) +Condat::Condat(Model& model, const GridBase<Real>& surface, Real tolerance, + Real mu) : Kato(model, surface, tolerance, mu) { pressure_old = - allocateGrid<true, Real>(model.getType(), model.getDiscretization(), - model.getTraction().getNbComponents()); + allocateGrid<true, Real>(model.getType(), model.getDiscretization(), + model.getTraction().getNbComponents()); } /* -------------------------------------------------------------------------- */ Real Condat::solve(GridBase<Real>& p0, Real grad_step) { if (p0.getNbPoints() != pressure->getNbComponents()) { TAMAAS_EXCEPTION( - "Target mean pressure does not have the right number of components"); + "Target mean pressure does not have the right number of components"); } Real cost = 0; switch (model.getType()) { case model_type::surface_1d: cost = solveTmpl<model_type::surface_1d>(p0, grad_step); break; case model_type::surface_2d: cost = solveTmpl<model_type::surface_2d>(p0, grad_step); break; default: break; } return cost; } template <model_type type> Real Condat::solveTmpl(GridBase<Real>& p0, Real grad_step) { // Printing column headers - std::cout << std::setw(5) << "Iter" - << " " << std::setw(15) << "Cost_f" - << " " << std::setw(15) << "Error" << '\n' - << std::fixed; + Logger().get(LogLevel::info) << std::setw(5) << "Iter" + << " " << std::setw(15) << "Cost_f" + << " " << std::setw(15) << "Error" << '\n' + << std::fixed; constexpr UInt comp = model_type_traits<type>::components; Real cost = 0; UInt n = 0; pressure->uniformSetComponents(p0); *pressure_old = 0; - Grid<Real, 1> q({1}, comp); q = 0; + Grid<Real, 1> q({1}, comp); + q = 0; Real lipschitz = engine.getNeumannNorm(); Real sigma = 2 * grad_step / lipschitz; Vector<Real, comp> h_mean = computeMean<comp>(*surfaceComp); *surfaceComp -= h_mean; do { updateGap<comp>(sigma, grad_step, q); *pressure -= *gap; enforcePressureCoulomb<comp>(); updateLagrange<comp>(q, p0); *pressure_old = *pressure; cost = computeCost(); printState(n, cost, cost); - } while ((cost > this->tolerance || cost == 0.0) && n++ < this->max_iterations); + } while ((cost > this->tolerance || cost == 0.0) && + n++ < this->max_iterations); *surfaceComp += h_mean; computeFinalGap<comp>(); return cost; } /* -------------------------------------------------------------------------- */ // template <UInt comp> // void Condat::updateGap(Real sigma, Real grad_step, GridBase<Real>& q) { // engine.solveNeumann(*pressure, *gap); // VectorProxy<Real, comp> _q = q(0); // Vector<Real, comp> qt = _q; // qt *= (1 - grad_step); // Loop::stridedLoop( // [sigma, qt] CUDA_LAMBDA(VectorProxy<Real, comp>&& g, // VectorProxy<Real, comp>&& h) { // g -= h; // g *= sigma; // g += qt; // }, // *gap, *surfaceComp); // } template <UInt comp> void Condat::updateGap(Real sigma, Real grad_step, GridBase<Real>& q) { computeGradient<comp>(); VectorProxy<Real, comp> _q(q(0)); Vector<Real, comp> qt = _q; qt *= (1 - grad_step); Loop::loop( [sigma, qt] CUDA_LAMBDA(VectorProxy<Real, comp> g) { g *= sigma; g += qt; }, range<VectorProxy<Real, comp>>(*gap)); } /* -------------------------------------------------------------------------- */ template <UInt comp> void Condat::updateLagrange(GridBase<Real>& q, GridBase<Real>& p0) { *pressure_old *= -0.5; *pressure_old += *pressure; Vector<Real, comp> corr = computeMean<comp>(*pressure_old); corr *= 2; q += corr; q -= p0; } __END_TAMAAS__ /* -------------------------------------------------------------------------- */ diff --git a/src/solvers/contact_solver.cpp b/src/solvers/contact_solver.cpp index 31bb4fa5..85e9efd0 100644 --- a/src/solvers/contact_solver.cpp +++ b/src/solvers/contact_solver.cpp @@ -1,70 +1,70 @@ /** * @file * @section LICENSE * * Copyright (©) 2016-19 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 <https://www.gnu.org/licenses/>. * */ /* -------------------------------------------------------------------------- */ #include "contact_solver.hh" #include "fft_plan_manager.hh" +#include "logger.hh" #include <iomanip> #include <iostream> /* -------------------------------------------------------------------------- */ __BEGIN_TAMAAS__ ContactSolver::ContactSolver(Model& model, const GridBase<Real>& surface, Real tolerance) - : model(model), surface(), functional(), - tolerance(tolerance), surface_stddev(std::sqrt(surface.var())) { + : 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<true, Real>(model.getType(), discretization); - } /* -------------------------------------------------------------------------- */ 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; - std::cout << std::setw(5) << iter << " "; - std::cout << std::setw(15) << std::scientific << cost_f << " "; - std::cout << std::setw(15) << error << std::endl; - std::cout << std::fixed; + 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); } __END_TAMAAS__ /* -------------------------------------------------------------------------- */ diff --git a/src/solvers/epic.cpp b/src/solvers/epic.cpp index dd594ac9..ecbf0994 100644 --- a/src/solvers/epic.cpp +++ b/src/solvers/epic.cpp @@ -1,95 +1,96 @@ /** * @file * @section LICENSE * * Copyright (©) 2016-19 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 <https://www.gnu.org/licenses/>. * */ /* -------------------------------------------------------------------------- */ #include "epic.hh" +#include "logger.hh" /* -------------------------------------------------------------------------- */ namespace tamaas { /* -------------------------------------------------------------------------- */ EPICSolver::EPICSolver(ContactSolver& csolver, EPSolver& epsolver, Real tolerance, Real relaxation) : csolver(csolver), epsolver(epsolver), tolerance(tolerance), relaxation(relaxation) { auto& model = csolver.getModel(); surface.wrap(csolver.getSurface()); pressure.resize(model.getTraction().getNbPoints()); #define SET_VIEWS(_, __, type) \ case type: { \ setViews<type>(); \ break; \ } switch (model.getType()) { BOOST_PP_SEQ_FOR_EACH(SET_VIEWS, ~, TAMAAS_MODEL_TYPES); } #undef SET_VIEWS } /* -------------------------------------------------------------------------- */ void EPICSolver::solve(std::vector<Real> load) { UInt n = 0, nmax = 1000; Real error = 0.; const GridBase<Real> initial_surface(surface); Real normalizing_factor = std::sqrt(initial_surface.var()); GridBase<Real> previous_residual(*residual_disp); GridBase<Real> relaxation_direction(*residual_disp); previous_residual = 0; do { csolver.solve(load); // solve contact problem *pressure_inc -= pressure; // compute pressure increment epsolver.solve(); // compute residual displacement relaxation_direction = *residual_disp; relaxation_direction -= previous_residual; relaxation_direction *= relaxation; error = computeError(*residual_disp, previous_residual, normalizing_factor); previous_residual += relaxation_direction; surface = initial_surface; surface -= previous_residual; // update surface std::cout << "error = " << std::scientific << error << std::fixed << std::endl; } while (error > tolerance && n++ < nmax); // computing full pressure pressure += *pressure_inc; // setting the model pressure to full converged pressure *pressure_inc = pressure; // updating plastic state epsolver.updateState(); } /* -------------------------------------------------------------------------- */ Real EPICSolver::computeError(const GridBase<Real>& current, const GridBase<Real>& prev, Real factor) const { GridBase<Real> error(current); error -= prev; return std::sqrt(error.dot(error)) / factor; } /* -------------------------------------------------------------------------- */ } // namespace tamaas diff --git a/src/solvers/kato.cpp b/src/solvers/kato.cpp index 8a0ecf52..62d36129 100644 --- a/src/solvers/kato.cpp +++ b/src/solvers/kato.cpp @@ -1,511 +1,502 @@ /** * @file * @section LICENSE * * Copyright (©) 2016-19 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 <https://www.gnu.org/licenses/>. * */ /* -------------------------------------------------------------------------- */ #include "kato.hh" #include "elastic_functional.hh" -#include "loop.hh" #include "fft_plan_manager.hh" +#include "logger.hh" +#include "loop.hh" #include <iomanip> -#include <iterator> #include <iostream> +#include <iterator> /* -------------------------------------------------------------------------- */ __BEGIN_TAMAAS__ -Kato::Kato(Model& model, const GridBase<Real>& surface, Real tolerance, - Real mu) - : ContactSolver(model, surface, tolerance), - engine(model.getBEEngine()), mu(mu) { +Kato::Kato(Model& model, const GridBase<Real>& surface, Real tolerance, Real mu) + : ContactSolver(model, surface, tolerance), engine(model.getBEEngine()), + mu(mu) { if (model.getType() != model_type::surface_1d && model.getType() != model_type::surface_2d) { - TAMAAS_EXCEPTION( - "Model type is not compatible with Kato solver"); + TAMAAS_EXCEPTION("Model type is not compatible with Kato solver"); } - gap = this->_gap.get(); // locally allocated + gap = this->_gap.get(); // locally allocated pressure = &model.getTraction(); N = pressure->getNbPoints(); if (model.getType() == model_type::surface_1d) { initSurfaceWithComponents<model_type::surface_1d>(); } else { initSurfaceWithComponents<model_type::surface_2d>(); } engine.registerNeumann(); } /* -------------------------------------------------------------------------- */ Real Kato::solve(GridBase<Real>& p0, UInt proj_iter) { if (p0.getNbPoints() != pressure->getNbComponents()) { TAMAAS_EXCEPTION( - "Target mean pressure does not have the right number of components"); + "Target mean pressure does not have the right number of components"); } Real cost = 0; switch (model.getType()) { case model_type::surface_1d: cost = solveTmpl<model_type::surface_1d>(p0, proj_iter); break; case model_type::surface_2d: cost = solveTmpl<model_type::surface_2d>(p0, proj_iter); break; default: break; } return cost; } template <model_type type> Real Kato::solveTmpl(GridBase<Real>& p0, UInt proj_iter) { constexpr UInt comp = model_type_traits<type>::components; Real cost = 0; UInt n = 0; // Printing column headers - std::cout << std::setw(5) << "Iter" - << " " << std::setw(15) << "Cost_f" - << " " << std::setw(15) << "Error" << '\n' - << std::fixed; + Logger().get(LogLevel::info) << std::setw(5) << "Iter" + << " " << std::setw(15) << "Cost_f" + << " " << std::setw(15) << "Error" << '\n' + << std::fixed; pressure->uniformSetComponents(p0); do { computeGradient<comp>(); *pressure -= *gap; enforcePressureConstraints<comp>(p0, proj_iter); cost = computeCost(); printState(n, cost, cost); } while (cost > this->tolerance && n++ < this->max_iterations); computeFinalGap<comp>(); return cost; } /* -------------------------------------------------------------------------- */ Real Kato::solveRelaxed(GridBase<Real>& g0) { if (g0.getNbPoints() != pressure->getNbComponents()) { TAMAAS_EXCEPTION( - "Target mean gap does not have the right number of components"); + "Target mean gap does not have the right number of components"); } Real cost = 0; switch (model.getType()) { case model_type::surface_1d: cost = solveRelaxedTmpl<model_type::surface_1d>(g0); break; case model_type::surface_2d: cost = solveRelaxedTmpl<model_type::surface_2d>(g0); break; default: break; } return cost; } template <model_type type> Real Kato::solveRelaxedTmpl(GridBase<Real>& g0) { constexpr UInt comp = model_type_traits<type>::components; Real cost = 0; UInt n = 0; // Printing column headers - std::cout << std::setw(5) << "Iter" - << " " << std::setw(15) << "Cost_f" - << " " << std::setw(15) << "Error" << '\n' - << std::fixed; + Logger().get(LogLevel::info) << std::setw(5) << "Iter" + << " " << std::setw(15) << "Cost_f" + << " " << std::setw(15) << "Error" << '\n' + << std::fixed; *pressure = 0; do { engine.solveNeumann(*pressure, *gap); addUniform<comp>(*gap, g0); *gap -= *surfaceComp; *pressure -= *gap; enforcePressureCoulomb<comp>(); cost = computeCost(); printState(n, cost, cost); } while (cost > this->tolerance && n++ < this->max_iterations); computeFinalGap<comp>(); return cost; } /* -------------------------------------------------------------------------- */ Real Kato::solveRegularized(GridBase<Real>& p0, Real r) { if (p0.getNbPoints() != pressure->getNbComponents()) { TAMAAS_EXCEPTION( - "Target mean pressure does not have the right number of components"); + "Target mean pressure does not have the right number of components"); } Real cost = 0; switch (model.getType()) { case model_type::surface_1d: cost = solveRegularizedTmpl<model_type::surface_1d>(p0, r); break; case model_type::surface_2d: cost = solveRegularizedTmpl<model_type::surface_2d>(p0, r); break; default: break; } return cost; } template <model_type type> Real Kato::solveRegularizedTmpl(GridBase<Real>& p0, Real r) { constexpr UInt comp = model_type_traits<type>::components; Real cost = 0; UInt n = 0; // Printing column headers - std::cout << std::setw(5) << "Iter" - << " " << std::setw(15) << "Cost_f" - << " " << std::setw(15) << "Error" << '\n' - << std::fixed; + Logger().get(LogLevel::info) << std::setw(5) << "Iter" + << " " << std::setw(15) << "Cost_f" + << " " << std::setw(15) << "Error" << '\n' + << std::fixed; pressure->uniformSetComponents(p0); do { // enforcePressureMean<comp>(p0); engine.solveNeumann(*pressure, *gap); *gap -= *surfaceComp; // Impose zero tangential displacement in non-sliding zone UInt count_static = 0; Vector<Real, comp> g_static = Loop::reduce<operation::plus>( [&] CUDA_LAMBDA(VectorProxy<Real, comp> g, VectorProxy<Real, comp> p) -> Vector<Real, comp> { VectorProxy<Real, comp - 1> p_T(p(0)); Real p_T_norm = p_T.l2norm(); Real p_N = p(comp - 1); if (0.99 * mu * p_N > p_T_norm) { // non-sliding contact count_static++; return g; // to compute mean of g_T } else { return 0; } }, range<VectorProxy<Real, comp>>(*gap), range<VectorProxy<Real, comp>>(*pressure)); g_static /= count_static != 0 ? count_static : 1; g_static(comp - 1) = 0; Loop::loop( [this, r, g_static] CUDA_LAMBDA(VectorProxy<Real, comp> p, VectorProxy<Real, comp> g) { // Add frictional term to gradient of functional g -= g_static; // Vector<Real, comp> _g = g; // copy VectorProxy<Real, comp - 1> g_T(g(0)); VectorProxy<Real, 1> g_N(g(comp - 1)); Real g_T_norm = g_T.l2norm(); // g_N += mu * regularize(g_T_norm, r) * g_T_norm; g_N += mu * g_T_norm; // Update pressure with gradient // _g *= 0.1; p -= g; // Truncate negative normal pressure VectorProxy<Real, comp - 1> p_T(p(0)); VectorProxy<Real, 1> p_N(p(comp - 1)); if (p_N(0) < 0) p_N = 0; // Set tangential pressure p_T = g_T; if (g_T_norm != 0) p_T *= -mu * p_N(0) * regularize(g_T_norm, r) / g_T_norm; }, range<VectorProxy<Real, comp>>(*pressure), range<VectorProxy<Real, comp>>(*gap)); // enforcePressureMean<comp>(p0); // enforcePressureCoulomb<comp>(); enforcePressureConstraints<comp>(p0, 50); cost = computeCost(); printState(n, cost, cost); } while (std::abs(cost) > this->tolerance && n++ < this->max_iterations); computeFinalGap<comp>(); return cost; } /* -------------------------------------------------------------------------- */ template <model_type type> void Kato::initSurfaceWithComponents() { constexpr UInt comp = model_type_traits<type>::components; - surfaceComp = - allocateGrid<true, Real>(type, model.getDiscretization(), comp); + surfaceComp = allocateGrid<true, Real>(type, model.getDiscretization(), comp); *surfaceComp = 0; - Loop::loop( - [] CUDA_LAMBDA(Real& s, VectorProxy<Real, comp> sc) { - sc(comp - 1) = s; - }, - surface, range<VectorProxy<Real, comp>>(*surfaceComp)); + Loop::loop([] CUDA_LAMBDA(Real & s, + VectorProxy<Real, comp> sc) { sc(comp - 1) = s; }, + surface, range<VectorProxy<Real, comp>>(*surfaceComp)); } /* -------------------------------------------------------------------------- */ template <UInt comp> void Kato::enforcePressureMean(GridBase<Real>& p0) { Vector<Real, comp> corr = computeMean<comp>(*pressure); VectorProxy<Real, comp> _p0(p0(0)); corr -= _p0; *pressure -= corr; } /* -------------------------------------------------------------------------- */ // template <UInt comp> // void Kato::enforcePressureCoulomb() { // Loop::stridedLoop( // [this] CUDA_LAMBDA(VectorProxy<Real, comp>&& p) { // VectorProxy<Real, comp - 1> p_T(p(0)); // Real p_N = p(comp - 1); // Real p_T_sqrd= p_T.l2squared(); // // Projection normale au cône de friction // bool cond1 = (p_N >= 0 && p_T_sqrd <= mu * mu * p_N * p_N); // bool cond2 = (p_N <= 0 && p_T_sqrd <= p_N * p_N / mu / mu); // if (cond2) { // p_T = 0; // p(comp - 1) = 0; // } else if (!cond1) { // Real p_T_norm = std::sqrt(p_T_sqrd); // Real k = (p_N + mu * p_T_norm) / (1 + mu * mu); // p_T *= k * mu / p_T_norm; // p(comp - 1) = k; // } // }, // *pressure); // } /* -------------------------------------------------------------------------- */ template <UInt comp> void Kato::enforcePressureTresca() { Loop::loop( [this] CUDA_LAMBDA(VectorProxy<Real, comp> p) { VectorProxy<Real, comp - 1> p_T(p(0)); Real p_N = p(comp - 1); Real p_T_norm = p_T.l2norm(); if (p_N < 0) { p_T = 0; p(comp - 1) = 0; } else if (p_T_norm > mu) { // TODO: replace name of variable mu p_T *= mu / p_T_norm; // p(comp - 1) += p_T_norm - mu; } }, range<VectorProxy<Real, comp>>(*pressure)); } template void Kato::enforcePressureTresca<2>(); template void Kato::enforcePressureTresca<3>(); /* -------------------------------------------------------------------------- */ /** * Compute mean of the field taking each component separately. */ // template <UInt comp> // Vector<Real, comp> Kato::computeMean(GridBase<Real>& field) { // Vector<Real, comp> mean = Loop::stridedReduce<operation::plus>( // [] CUDA_LAMBDA(VectorProxy<Real, comp>&& f) -> Vector<Real, comp> { // return f; // }, // field); // mean /= N; // return mean; // } /* -------------------------------------------------------------------------- */ // template <UInt comp> // void Kato::addUniform(GridBase<Real>& field, GridBase<Real>& vec) { // VectorProxy<Real, comp> _vec(vec(0)); // field += _vec; // } /* -------------------------------------------------------------------------- */ Real Kato::computeCost(bool use_tresca) { UInt N = pressure->getNbPoints(); Grid<Real, 1> lambda({N}, 1); Grid<Real, 1> eta({N}, 1); Grid<Real, 1> p_N({N}, 1); Grid<Real, 1> p_C({N}, 1); switch (model.getType()) { case model_type::surface_1d: if (!use_tresca) { computeValuesForCost<model_type::surface_1d>(lambda, eta, p_N, p_C); } else { computeValuesForCostTresca<model_type::surface_1d>(lambda, eta, p_N, p_C); } break; case model_type::surface_2d: if (!use_tresca) { computeValuesForCost<model_type::surface_2d>(lambda, eta, p_N, p_C); } else { computeValuesForCostTresca<model_type::surface_2d>(lambda, eta, p_N, p_C); } break; default: break; } return p_N.dot(lambda) + p_C.dot(eta); } /* -------------------------------------------------------------------------- */ template <model_type type> void Kato::computeValuesForCost(GridBase<Real>& lambda, GridBase<Real>& eta, - GridBase<Real>& p_N, GridBase<Real>& p_C) { + GridBase<Real>& p_N, GridBase<Real>& p_C) { constexpr UInt comp = model_type_traits<type>::components; Real g_N_min = Loop::reduce<operation::min>( - [] CUDA_LAMBDA(VectorProxy<Real, comp> g) { - return g(comp - 1); - }, - range<VectorProxy<Real, comp>>(*gap)); + [] CUDA_LAMBDA(VectorProxy<Real, comp> g) { return g(comp - 1); }, + range<VectorProxy<Real, comp>>(*gap)); Loop::loop( - [this, g_N_min] CUDA_LAMBDA( - VectorProxy<Real, comp> p, VectorProxy<Real, comp> g, Real & lambda_, - Real & eta_, Real & p_N_, Real & p_C_) { + [this, g_N_min] CUDA_LAMBDA(VectorProxy<Real, comp> p, + VectorProxy<Real, comp> g, Real & lambda_, + Real & eta_, Real & p_N_, Real & p_C_) { VectorProxy<Real, comp - 1> g_T(g(0)); Real g_N = g(comp - 1); Real g_T_norm = g_T.l2norm(); lambda_ = g_N - g_N_min; eta_ = g_T_norm; VectorProxy<Real, comp - 1> p_T(p(0)); Real p_N = p(comp - 1); Real p_T_norm = p_T.l2norm(); p_N_ = p_N; p_C_ = (p_N > 0) ? mu * p_N - p_T_norm : 0; }, range<VectorProxy<Real, comp>>(*pressure), range<VectorProxy<Real, comp>>(*gap), lambda, eta, p_N, p_C); } /* -------------------------------------------------------------------------- */ template <model_type type> -void Kato::computeValuesForCostTresca(GridBase<Real>& lambda, GridBase<Real>& eta, - GridBase<Real>& p_N, GridBase<Real>& p_C) { +void Kato::computeValuesForCostTresca(GridBase<Real>& lambda, + GridBase<Real>& eta, GridBase<Real>& p_N, + GridBase<Real>& p_C) { constexpr UInt comp = model_type_traits<type>::components; Real g_N_min = Loop::reduce<operation::min>( - [] CUDA_LAMBDA(VectorProxy<Real, comp> g) { - return g(comp - 1); - }, - range<VectorProxy<Real, comp>>(*gap)); + [] CUDA_LAMBDA(VectorProxy<Real, comp> g) { return g(comp - 1); }, + range<VectorProxy<Real, comp>>(*gap)); Real p_C_min = Loop::reduce<operation::min>( - [this] CUDA_LAMBDA(VectorProxy<Real, comp> p) { - VectorProxy<Real, comp - 1> p_T(p(0)); - Real p_T_norm = p_T.l2norm(); - return mu - p_T_norm; - }, - range<VectorProxy<Real, comp>>(*pressure)); + [this] CUDA_LAMBDA(VectorProxy<Real, comp> p) { + VectorProxy<Real, comp - 1> p_T(p(0)); + Real p_T_norm = p_T.l2norm(); + return mu - p_T_norm; + }, + range<VectorProxy<Real, comp>>(*pressure)); Loop::loop( [this, g_N_min, p_C_min] CUDA_LAMBDA( VectorProxy<Real, comp> p, VectorProxy<Real, comp> g, Real & lambda_, Real & eta_, Real & p_N_, Real & p_C_) { VectorProxy<Real, comp - 1> g_T(g(0)); Real g_N = g(comp - 1); Real g_T_norm = g_T.l2norm(); lambda_ = g_N - g_N_min; eta_ = g_T_norm; VectorProxy<Real, comp - 1> p_T(p(0)); Real p_N = p(comp - 1); Real p_T_norm = p_T.l2norm(); p_N_ = p_N; p_C_ = (p_N > 0) ? mu - p_T_norm - p_C_min : 0; }, range<VectorProxy<Real, comp>>(*pressure), range<VectorProxy<Real, comp>>(*gap), lambda, eta, p_N, p_C); } /* -------------------------------------------------------------------------- */ template <UInt comp> void Kato::computeFinalGap() { engine.solveNeumann(*pressure, *gap); *gap -= *surfaceComp; Real g_N_min = Loop::reduce<operation::min>( - [] CUDA_LAMBDA(VectorProxy<Real, comp> g) { - return g(comp - 1); - }, - range<VectorProxy<Real, comp>>(*gap)); + [] CUDA_LAMBDA(VectorProxy<Real, comp> g) { return g(comp - 1); }, + range<VectorProxy<Real, comp>>(*gap)); Grid<Real, 1> g_shift({comp}, 1); g_shift = 0; g_shift(comp - 1) = -g_N_min; *gap += *surfaceComp; addUniform<comp>(*gap, g_shift); model.getDisplacement() = *gap; } /* -------------------------------------------------------------------------- */ Real Kato::regularize(Real x, Real r) { Real xr = x / r; return xr / (1 + std::abs(xr)); } __END_TAMAAS__ /* -------------------------------------------------------------------------- */ diff --git a/src/solvers/polonsky_keer_rey.cpp b/src/solvers/polonsky_keer_rey.cpp index 1a3d582e..eed2ea75 100644 --- a/src/solvers/polonsky_keer_rey.cpp +++ b/src/solvers/polonsky_keer_rey.cpp @@ -1,292 +1,293 @@ /** * @file * @section LICENSE * * Copyright (©) 2016-19 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 <https://www.gnu.org/licenses/>. * */ /* -------------------------------------------------------------------------- */ #include "polonsky_keer_rey.hh" #include "elastic_functional.hh" #include "fft_plan_manager.hh" +#include "logger.hh" #include "loop.hh" #include "model_type.hh" #include <boost/preprocessor/seq.hpp> #include <iomanip> #include <iterator> /* -------------------------------------------------------------------------- */ __BEGIN_TAMAAS__ PolonskyKeerRey::PolonskyKeerRey(Model& model, const GridBase<Real>& surface, Real tolerance, type variable_type, type constraint_type) : ContactSolver(model, surface, tolerance), variable_type(variable_type), constraint_type(constraint_type) { #define SET_VIEWS(_, __, type) \ case type: { \ setViews<type>(); \ break; \ } switch (model.getType()) { BOOST_PP_SEQ_FOR_EACH(SET_VIEWS, ~, TAMAAS_MODEL_TYPES); } #undef SET_VIEWS search_direction = allocateGrid<true, Real>( operation_type, model.getBoundaryDiscretization()); projected_search_direction = allocateGrid<true, Real>( operation_type, model.getBoundaryDiscretization()); switch (variable_type) { case gap: { model.getBEEngine().registerDirichlet(); primal = gap_view.get(); dual = pressure_view.get(); this->functional.addFunctionalTerm( new functional::ElasticFunctionalGap(*integral_op, this->surface), true); break; } case pressure: { model.getBEEngine().registerNeumann(); primal = pressure_view.get(); dual = gap_view.get(); this->functional.addFunctionalTerm( new functional::ElasticFunctionalPressure(*integral_op, this->surface), true); break; } } } /* -------------------------------------------------------------------------- */ Real PolonskyKeerRey::solve(std::vector<Real> target_v) { const Real target = target_v.back(); Real G = 0, Gold = 1, error = 0, error_norm = 1; UInt n = 0; bool delta = false; *search_direction = 0; //*dual = 0; // Printing column headers - std::cout << std::setw(5) << "Iter" - << " " << std::setw(15) << "Cost_f" - << " " << std::setw(15) << "Error" << '\n' - << std::fixed; + Logger().get(LogLevel::info) << std::setw(5) << "Iter" + << " " << std::setw(15) << "Cost_f" + << " " << std::setw(15) << "Error" << '\n' + << std::fixed; // Setting uniform value if constraint if (constraint_type == variable_type && std::abs(primal->sum()) <= 0) *primal = target; else if (constraint_type == variable_type) *primal *= target / primal->mean(); else if (constraint_type != variable_type) *primal = this->surface_stddev; do { // Computing functional gradient functional.computeGradF(*primal, *dual); Real dbar = meanOnUnsaturated(*dual); // Enforcing dual constraint via gradient if (constraint_type != variable_type) { *dual += 2 * target + dbar; } else { // Centering dual on primal > 0 *dual -= dbar; } // Computing gradient norm G = computeSquaredNorm(*dual); // Updating search direction (conjugate gradient) updateSearchDirection(delta * G / Gold); Gold = G; // Computing critical step Real tau = computeCriticalStep(target); // Update primal variable delta = updatePrimal(tau); // We should scale to constraint if (constraint_type == variable_type) *primal *= target / primal->mean(); error = computeError() / error_norm; Real cost_f = functional.computeF(*primal, *dual); printState(n, cost_f, error); } while (error > this->tolerance && n++ < this->max_iterations); // Final update of dual variable functional.computeGradF(*primal, *dual); Real dual_min = dual->min(); *dual -= dual_min; // Primal is pressure: need to make sure gap is positive if (variable_type == pressure) { *displacement_view = *dual; *displacement_view += this->surface; } // Primal is gap: need to make sure dual is positive (pressure + adhesion) else { *displacement_view = *primal; *displacement_view += this->surface; integral_op->apply(*displacement_view, *pressure_view); *pressure_view -= dual_min; } return error; } /* -------------------------------------------------------------------------- */ /** * Computes \f$ \frac{1}{\mathrm{card}(\{p > 0\})} \sum_{\{p > 0\}}{f_i} \f$ */ Real PolonskyKeerRey::meanOnUnsaturated(const GridBase<Real>& field) const { // Sum on unsaturated Real sum = Loop::reduce<operation::plus>( [] CUDA_LAMBDA(Real & p, const Real& f) { return (p > 0) ? f : 0; }, *primal, field); // Number of unsaturated points UInt n_unsat = Loop::reduce<operation::plus>( [] CUDA_LAMBDA(Real & p) -> UInt { return (p > 0); }, *primal); return sum / n_unsat; } /* -------------------------------------------------------------------------- */ /** * Computes \f$ \sum_{\{p > 0\}}{f_i^2} \f$ */ Real PolonskyKeerRey::computeSquaredNorm(const GridBase<Real>& field) const { return Loop::reduce<operation::plus>( [] CUDA_LAMBDA(Real & p, const Real& f) { return (p > 0) ? f * f : 0; }, *primal, field); } /* -------------------------------------------------------------------------- */ /** * Computes \f$ \tau = \frac{ \sum_{\{p > 0\}}{q_i't_i} }{ \sum_{\{p > 0\}}{r_i' * t_i} } \f$ */ Real PolonskyKeerRey::computeCriticalStep(Real target) { integral_op->apply(*search_direction, *projected_search_direction); Real rbar = meanOnUnsaturated(*projected_search_direction); if (constraint_type == variable_type) *projected_search_direction -= rbar; else { *projected_search_direction += 2 * target + rbar; } Real num = Loop::reduce<operation::plus>( [] CUDA_LAMBDA(const Real& p, const Real& q, const Real& t) { return (p > 0) ? q * t : 0; }, *primal, *dual, *search_direction); Real denum = Loop::reduce<operation::plus>( [] CUDA_LAMBDA(const Real& p, const Real& r, const Real& t) { return (p > 0) ? r * t : 0; }, *primal, *projected_search_direction, *search_direction); return num / denum; } /* -------------------------------------------------------------------------- */ /** * Update steps: * 1. \f$\mathbf{p} = \mathbf{p} - \tau \mathbf{t} \f$ * 2. Truncate all \f$p\f$ negative * 3. For all points in \f$I_\mathrm{na} = \{p = 0 \land q < 0 \}\f$ do \f$p_i = * p_i - \tau q_i\f$ */ bool PolonskyKeerRey::updatePrimal(Real step) { UInt na_num = Loop::reduce<operation::plus>( [step] CUDA_LAMBDA(Real & p, const Real& q, const Real& t) -> UInt { p -= step * t; // Updating primal if (p < 0) p = 0; // Truncating neg values if (p == 0 && q < 0) { // If non-admissible state p -= step * q; return 1; } else return 0; }, *primal, *dual, *search_direction); return na_num == 0; } /* -------------------------------------------------------------------------- */ /** * Error is based on \f$ \sum{p_i q_i} \f$ */ Real PolonskyKeerRey::computeError() { /// Making sure dual respects constraint *dual -= dual->min(); Real norm = 1; Real error = primal->dot(*dual); if (std::isnan(error)) TAMAAS_EXCEPTION("Encountered NaN in complementarity error: this may be " "caused by a contact area of a single node."); if (variable_type == pressure) norm = std::abs(primal->sum() * this->surface_stddev); else norm = std::abs(dual->sum() * this->surface_stddev); norm *= primal->getNbPoints(); return std::abs(error) / norm; } /* -------------------------------------------------------------------------- */ /** * Do \f$\mathbf{t} = \mathbf{q}' + \delta \frac{R}{R_\mathrm{old}}\mathbf{t} * \f$ */ void PolonskyKeerRey::updateSearchDirection(Real factor) { Loop::loop( [factor] CUDA_LAMBDA(Real & p, Real & q, Real & t) { t = (p > 0) ? q + factor * t : 0; }, *primal, *dual, *search_direction); } /* -------------------------------------------------------------------------- */ void PolonskyKeerRey::addFunctionalTerm(functional::Functional* func) { functional.addFunctionalTerm(func, false); } __END_TAMAAS__ /* -------------------------------------------------------------------------- */ diff --git a/src/surface/surface_generator_filter_fft.cpp b/src/surface/surface_generator_filter_fft.cpp index 4cc35974..ddac0fb9 100644 --- a/src/surface/surface_generator_filter_fft.cpp +++ b/src/surface/surface_generator_filter_fft.cpp @@ -1,55 +1,57 @@ /** * @file * @section LICENSE * * Copyright (©) 2016-19 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 <https://www.gnu.org/licenses/>. * */ /* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */ #include "surface_generator_filter_fft.hh" #include "isopowerlaw.hh" +#include "logger.hh" #include "surface_statistics.hh" /* -------------------------------------------------------------------------- */ __BEGIN_TAMAAS__ extern template class Grid<Real, 2>; SurfaceGeneratorFilterFFT::SurfaceGeneratorFilterFFT() { pl_filter = std::make_shared<Isopowerlaw<2>>(); generator.setFilter(pl_filter); } SurfaceGeneratorFilterFFT::~SurfaceGeneratorFilterFFT() {} Real SurfaceGeneratorFilterFFT::constrainRMS() { - std::cerr << "Waring /!\\ :: Statistical bias in using the generated RMS as " - "normalizing factor" - << std::endl; + Logger().get(LogLevel::warning) + << "Statistical bias in using the generated RMS as " + "normalizing factor" + << std::endl; Real hrms_grid = SurfaceStatistics::computeStdev(generator.grid); generator.grid *= rms / hrms_grid; return rms; } Grid<Real, 2>& SurfaceGeneratorFilterFFT::buildSurface() { generator.setSizes({{grid_size, grid_size}}); return generator.buildSurface(); } __END_TAMAAS__