From e27b8a76abd02e347a18747b8f3f84eb20f0e44e Mon Sep 17 00:00:00 2001 From: Alex Hocks Date: Wed, 6 May 2026 18:16:27 +0200 Subject: [PATCH 1/7] impl logging # Conflicts: # src/reader.cpp --- CMakeLists.txt | 5 + include/MaterialManager.h | 85 ++++++++------- include/general.h | 2 - include/logging.h | 123 ++++++++++++++++++++++ include/solver.h | 59 +++++------ include/solverCG.h | 10 +- include/solverFP.h | 6 +- src/logging.cpp | 214 ++++++++++++++++++++++++++++++++++++++ src/main.cpp | 19 ++-- src/reader.cpp | 59 +++++------ 10 files changed, 453 insertions(+), 129 deletions(-) create mode 100644 include/logging.h create mode 100644 src/logging.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index fa9c3fc..be600af 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -112,6 +112,9 @@ find_package(nlohmann_json REQUIRED) # TARGETS # ############################################################################## +set(FANS_VERBOSITY 3 CACHE STRING "Set verbosity level: 0 none, 4 all") +add_definitions(-DVERBOSITY=${FANS_VERBOSITY}) + option(FANS_BUILD_STATIC "Build static library" OFF) if (FANS_BUILD_STATIC) add_library(FANS_FANS STATIC) @@ -150,6 +153,7 @@ target_include_directories(FANS_FANS PUBLIC $ Matmodel *createMatmodel(const Reader &reader); @@ -110,51 +111,49 @@ class MaterialManager { compute_reference_stiffness(reader); // Print detailed information about material configuration for logging - if (reader.world_rank == 0) { - printf("\n# MaterialManager initialized:\n"); - printf("# Number of material models: %zu\n", models.size()); - printf("# Number of phases: %d\n#\n", n_phases); - - for (size_t i = 0; i < mats.size(); ++i) { - const auto &mg = mats[i]; - printf("# Material Model %zu: %s\n", i + 1, mg["matmodel"].get().c_str()); - - // Print phases - auto phases = mg["phases"].get>(); - printf("# Phases: ["); - for (size_t j = 0; j < phases.size(); ++j) { - printf("%d", phases[j]); - if (j < phases.size() - 1) - printf(", "); - } - printf("]\n"); - - // Print material properties - printf("# Material properties:\n"); - const auto &props = mg["material_properties"]; - for (auto it = props.begin(); it != props.end(); ++it) { - printf("# %s: ", it.key().c_str()); - if (it.value().is_array()) { - printf("["); - for (size_t k = 0; k < it.value().size(); ++k) { - if (it.value()[k].is_number()) { - printf("%.5g", it.value()[k].get()); - } else if (it.value()[k].is_string()) { - printf("\"%s\"", it.value()[k].get().c_str()); - } - if (k < it.value().size() - 1) - printf(", "); + Log::io->info() << "\n# MaterialManager initialized:\n"; + Log::io->info() << Log::format("# Number of material models: %zu\n", models.size()); + Log::io->info() << Log::format("# Number of phases: %d\n#\n", n_phases); + + for (size_t i = 0; i < mats.size(); ++i) { + const auto &mg = mats[i]; + Log::io->info() << Log::format("# Material Model %zu: %s\n", i + 1, mg["matmodel"].get().c_str()); + + // Print phases + auto phases = mg["phases"].get>(); + Log::io->info() << "# Phases: ["; + for (size_t j = 0; j < phases.size(); ++j) { + Log::io->info(true) << Log::format("%d", phases[j]); + if (j < phases.size() - 1) + Log::io->info(true) << ", "; + } + Log::io->info(true) << "]\n"; + + // Print material properties + Log::io->info() << "# Material properties:\n"; + const auto &props = mg["material_properties"]; + for (auto it = props.begin(); it != props.end(); ++it) { + Log::io->info() << Log::format("# %s: ", it.key().c_str()); + if (it.value().is_array()) { + Log::io->info(true) << "["; + for (size_t k = 0; k < it.value().size(); ++k) { + if (it.value()[k].is_number()) { + Log::io->info(true) << Log::format("%.5g", it.value()[k].get()); + } else if (it.value()[k].is_string()) { + Log::io->info(true) << Log::format("\"%s\"", it.value()[k].get().c_str()); } - printf("]"); - } else if (it.value().is_number()) { - printf("%.5g", it.value().get()); - } else if (it.value().is_string()) { - printf("\"%s\"", it.value().get().c_str()); + if (k < it.value().size() - 1) + Log::io->info(true) << ", "; } - printf("\n"); + Log::io->info(true) << "]"; + } else if (it.value().is_number()) { + Log::io->info(true) << Log::format("%.5g", it.value().get()); + } else if (it.value().is_string()) { + Log::io->info(true) << Log::format("\"%s\"", it.value().get().c_str()); } - printf("#\n"); + Log::io->info(true) << "\n"; } + Log::io->info() << "#\n"; } } @@ -191,9 +190,7 @@ class MaterialManager { throw std::invalid_argument("reference_material must be symmetric positive definite"); } - if (reader.world_rank == 0) { - cout << "# Using user-defined reference material for fundamental solution." << endl; - } + Log::io->info() << "# Using user-defined reference material for fundamental solution.\n"; } else { // Default: simple average of reference stiffness across all material models kapparef_mat = Matrix::Zero(); diff --git a/include/general.h b/include/general.h index 1128e8a..1949615 100644 --- a/include/general.h +++ b/include/general.h @@ -58,6 +58,4 @@ inline void FANS_free(V *p) } #endif // FANS_MALLOC_H -#define VERBOSITY 0 - // #define EIGEN_RUNTIME_NO_MALLOC diff --git a/include/logging.h b/include/logging.h new file mode 100644 index 0000000..806ab0a --- /dev/null +++ b/include/logging.h @@ -0,0 +1,123 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include "mpi.h" + +#include + +namespace Log { + +enum Level { + Error, + Info, + Warn, + Debug, + All +}; + +[[maybe_unused]] const std::string level_to_string(Level level); + +extern Level active_level; + +class Logger; +class MPI_TraceSync { + public: + explicit MPI_TraceSync(Logger &log, bool append); + ~MPI_TraceSync(); + + template + MPI_TraceSync &operator<<(const T &v); + MPI_TraceSync &operator<<(std::ostream &(*m)(std::ostream &) ); + + private: + Logger &_log; + bool _append; + std::ostringstream _buffer; +}; + +template +MPI_TraceSync &MPI_TraceSync::operator<<(const T &v) +{ + _buffer << v; + return *this; +} + +class Logger { + public: + friend class MPI_TraceSync; + explicit Logger(std::string prefix, int comm_rank, int comm_size, const MPI_Comm &comm); + + /// error > warn > debug > info > trace + std::ostream &error(bool append = false); + /// error > warn > debug > info > trace + std::ostream &info(bool append = false); + /// error > warn > debug > info > trace + std::ostream &warn(bool append = false); + /// error > warn > debug > info > trace + std::ostream &debug(bool append = false); + /// error > warn > debug > info > trace + MPI_TraceSync trace(bool append = false); + /// progress bar + void progress(const std::string &prefix, int step, int max) const; + + private: + std::ostream &trace_impl(bool append = false); + /// starting time + std::chrono::steady_clock::time_point _start_time; + /// computes the elapsed time in (hours, minutes, seconds) + std::tuple get_elapsed_time() const; + /// what the logger should always print first + const std::string _prefix; + /// empty stream to write nothing + std::ofstream _nullstr; + /// MPI comm rank + int _comm_rank; + /// MPI comm size + int _comm_size; + /// communicator + MPI_Comm _comm; +}; + +/** + * Creates all loggers and sets the level + * */ +void init(int comm_rank, int comm_size, const MPI_Comm &comm); + +/** + * Frees all memory + * */ +void finalize(); + +/** + * Formats a string according to fmt with the provided args + * @param fmt format string + * @param args format string arguments + * @return formatted string + */ +template +std::string format(const std::string &fmt, Args&&... args) +{ + const int buf_size = std::snprintf(nullptr, 0, fmt.c_str(), args...); + std::vector buf(buf_size + 1); + std::snprintf(buf.data(), buf.size(), fmt.c_str(), args...); + return {buf.data(), static_cast(buf_size)}; +} + +/** + * Set activate rank + */ +[[maybe_unused]] void setActiveRank(int rank); + +/// logger with prefix [GENERAL] +extern std::unique_ptr general; +/// logger with prefix [SOLVER] +extern std::unique_ptr solver; +/// logger with prefix [IO] +extern std::unique_ptr io; + +}; // namespace Log diff --git a/include/solver.h b/include/solver.h index 3858a42..a325e9d 100644 --- a/include/solver.h +++ b/include/solver.h @@ -3,6 +3,7 @@ #include "matmodel.h" #include "MaterialManager.h" +#include "logging.h" class J2Plasticity; @@ -133,12 +134,15 @@ Solver::Solver(Reader &reader, MaterialManager * rhat((std::complex *) v_r, local_n1 * n_x * (n_z / 2 + 1) * howmany), // actual initialization is below buffer_padding(fftw_alloc_real(n_y * (n_z + 2) * howmany)) { + Log::solver->trace() << "Start constructing solver\n"; + v_u_real.setZero(); for (ptrdiff_t i = local_n0 * n_y * n_z * howmany; i < (local_n0 + 1) * n_y * n_z * howmany; i++) { this->v_u[i] = 0; } std::memset(v_u_prev, 0, local_n0 * n_y * n_z * howmany * sizeof(double)); + Log::solver->trace() << "Init internal vars.\n"; matmanager->initialize_internal_variables(local_n0 * n_y * n_z, matmanager->models[0]->n_gp); computeFundamentalSolution(); @@ -147,9 +151,7 @@ Solver::Solver(Reader &reader, MaterialManager * template void Solver::computeFundamentalSolution() { - if (world_rank == 0) { - printf("\n# Start creating Fundamental Solution(s) \n"); - } + Log::solver->info() << "\n# Start creating Fundamental Solution(s) \n"; clock_t tot_time = clock(); Matrix Ker0 = matmanager->models[0]->Compute_Reference_ElementStiffness(matmanager->kapparef_mat); @@ -201,9 +203,7 @@ void Solver::computeFundamentalSolution() fundamentalSolution /= (double) (n_x * n_y * n_z); tot_time = clock() - tot_time; - if (world_rank == 0) { - printf("# Complete; Time for construction of Fundamental Solution(s): %f seconds\n", double(tot_time) / CLOCKS_PER_SEC); - } + Log::solver->info() << Log::format("# Complete; Time for construction of Fundamental Solution(s): %f seconds\n", double(tot_time) / CLOCKS_PER_SEC); } template @@ -291,14 +291,11 @@ void Solver::solve() clock_t tot_time = clock(); internalSolve(); tot_time = clock() - tot_time; - // if( VERBOSITY > 5 ){ - if (world_rank == 0) { - printf("# FFT Time per iteration ....... %2.6f sec\n", double(fft_time) / CLOCKS_PER_SEC / iter); - printf("# Total FFT Time ............... %2.6f sec\n", double(fft_time) / CLOCKS_PER_SEC); - printf("# Total Time per iteration ..... %2.6f sec\n", double(tot_time) / CLOCKS_PER_SEC / iter); - printf("# Total Time ................... %2.6f sec\n", double(tot_time) / CLOCKS_PER_SEC); - printf("# FFT contribution to total time %2.6f %% \n", 100. * double(fft_time) / double(tot_time)); - } + Log::solver->info() << Log::format("# FFT Time per iteration ....... %2.6f sec\n", double(fft_time) / CLOCKS_PER_SEC / iter); + Log::solver->info() << Log::format("# Total FFT Time ............... %2.6f sec\n", double(fft_time) / CLOCKS_PER_SEC); + Log::solver->info() << Log::format("# Total Time per iteration ..... %2.6f sec\n", double(tot_time) / CLOCKS_PER_SEC / iter); + Log::solver->info() << Log::format("# Total Time ................... %2.6f sec\n", double(tot_time) / CLOCKS_PER_SEC); + Log::solver->info() << Log::format("# FFT contribution to total time %2.6f %% \n", 100. * double(fft_time) / double(tot_time)); matmanager->update_internal_variables(); } @@ -436,12 +433,10 @@ double Solver::compute_error(RealArray &r) double err0 = err_all[0]; double err_rel = (iter == 0 ? 100 : err / err0); - if (world_rank == 0) { - if (iter == 0) { - printf("Before 1st iteration: %16.8e\n", err0); - } else { - printf("it %3lu .... err %16.8e / %8.4e, ratio: %4.8e, FFT time: %2.6f sec\n", iter, err, err / err0, (iter == 1 ? 0.0 : err / err_all[iter - 1]), double(buftime) / CLOCKS_PER_SEC); - } + if (iter == 0) { + Log::solver->info() << Log::format("Before 1st iteration: %16.8e\n", err0); + } else { + Log::solver->info(true) << Log::format("it %3lu .... err %16.8e / %8.4e, ratio: %4.8e, FFT time: %2.6f sec\n", iter, err, err / err0, (iter == 1 ? 0.0 : err / err_all[iter - 1]), double(buftime) / CLOCKS_PER_SEC); } const std::string &error_type = reader.errorParameters["type"].get(); @@ -576,16 +571,14 @@ void Solver::postprocess(Reader &reader, int load_idx, int time_ } } - if (world_rank == 0) { - printf("# Effective Stress .. ("); - for (int i = 0; i < n_str; ++i) - printf("%+.12f ", stress_average[i]); - printf(") \n"); - printf("# Effective Strain .. ("); - for (int i = 0; i < n_str; ++i) - printf("%+.12f ", strain_average[i]); - printf(") \n\n"); - } + Log::solver->info() << "# Effective Stress .. ("; + for (int i = 0; i < n_str; ++i) + Log::solver->info(true) << Log::format("%+.12f ", stress_average[i]); + Log::solver->info(true) << ") \n"; + Log::solver->info() << "# Effective Strain .. ("; + for (int i = 0; i < n_str; ++i) + Log::solver->info(true) << Log::format("%+.12f ", strain_average[i]); + Log::solver->info(true) << ") \n\n"; homogenized_stress = stress_average; homogenized_strain = strain_average; @@ -701,9 +694,9 @@ void Solver::postprocess(Reader &reader, int load_idx, int time_ homogenized_tangent = get_homogenized_tangent(1e-6); hsize_t dims[2] = {static_cast(n_str), static_cast(n_str)}; if (world_rank == 0) { - cout << "# Homogenized tangent: " << endl - << setprecision(12) << homogenized_tangent << endl - << endl; + Log::solver->info() << "# Homogenized tangent: \n" + << std::setprecision(12) << homogenized_tangent + << std::defaultfloat << "\n\n"; } reader.writeData("homogenized_tangent", load_idx, time_idx, homogenized_tangent.data(), dims, 2); } diff --git a/include/solverCG.h b/include/solverCG.h index 2d07849..369ad3a 100644 --- a/include/solverCG.h +++ b/include/solverCG.h @@ -2,6 +2,7 @@ #define SOLVER_CG_H #include "solver.h" +#include "logging.h" template class SolverCG : public Solver { @@ -62,8 +63,7 @@ double SolverCG::dotProduct(RealArray &a, RealArray &b) template void SolverCG::internalSolve() { - if (this->world_rank == 0) - printf("\n# Start FANS - Conjugate Gradient Solver \n"); + Log::solver->info() << "\t# Start FANS - Conjugate Gradient Solver \n"; bool islinear = this->matmanager->all_linear; alpha_warm = 0.1; @@ -116,8 +116,7 @@ void SolverCG::internalSolve() if (iter >= 2 && this->err_all[iter] > this->err_all[iter - 1] && this->err_all[iter - 1] > this->err_all[iter - 2]) ls_converged = false; // Force a CG restart } - if (this->world_rank == 0) - printf("# Complete FANS - Conjugate Gradient Solver \n"); + Log::solver->info() << "# Complete FANS - Conjugate Gradient Solver \n"; } template @@ -169,8 +168,7 @@ void SolverCG::LineSearchSecant() alpha_warm = 0.1; } - if (this->world_rank == 0) - printf("line search iter %i, alpha %f - error %e - ", _iter, alpha_curr, fabs(r1pd0) > 0.0 ? fabs(r1pd) / fabs(r1pd0) : 0.0); + Log::solver->info() << Log::format("line search iter %i, alpha %f - error %e - ", _iter, alpha_curr, fabs(r1pd0) > 0.0 ? fabs(r1pd) / fabs(r1pd0) : 0.0); } template diff --git a/include/solverFP.h b/include/solverFP.h index 67b99a4..189e2c8 100644 --- a/include/solverFP.h +++ b/include/solverFP.h @@ -2,6 +2,7 @@ #define SOLVER_FP_H #include "solver.h" +#include "logging.h" template class SolverFP : public Solver { @@ -32,8 +33,7 @@ SolverFP::SolverFP(Reader &reader, MaterialManager void SolverFP::internalSolve() { - if (this->world_rank == 0) - printf("\n# Start FANS - Fixed Point Solver \n"); + Log::solver->info() << "\n# Start FANS - Fixed Point Solver \n"; this->template compute_residual<2>(v_r_real, v_u_real); @@ -51,6 +51,6 @@ void SolverFP::internalSolve() err_rel = this->compute_error(v_r_real); } if (this->world_rank == 0) - printf("# Complete FANS - Fixed Point Solver \n"); + Log::solver->info() << "# Complete FANS - Fixed Point Solver \n"; } #endif diff --git a/src/logging.cpp b/src/logging.cpp new file mode 100644 index 0000000..efcb63d --- /dev/null +++ b/src/logging.cpp @@ -0,0 +1,214 @@ +#include "logging.h" +#include "mpi.h" +#include +#include + +int active_rank = 0; + +Log::MPI_TraceSync::MPI_TraceSync(Logger &log, bool append) + : _log(log), _append(append) {} +Log::MPI_TraceSync::~MPI_TraceSync() +{ + if (active_level >= All) { + MPI_Barrier(_log._comm); + const int size = _log._comm_size; + for (int i = 0; i < size; i++) { + Log::setActiveRank(i); + MPI_Barrier(_log._comm); + _log.trace_impl(_append) << _buffer.str() << std::flush; + } + Log::setActiveRank(0); + MPI_Barrier(_log._comm); + } +} + +Log::MPI_TraceSync &Log::MPI_TraceSync::operator<<(std::ostream &(*m)(std::ostream &) ) +{ + if (active_level >= All) + _buffer << m; + return *this; +} + +Log::Logger::Logger(std::string prefix, int comm_rank, int comm_size, const MPI_Comm &comm) + : _prefix(std::move(prefix)), _nullstr(), _comm_rank(comm_rank), _comm_size(comm_size), _comm(comm) +{ + _start_time = std::chrono::steady_clock::now(); + _nullstr.setstate(std::ios_base::badbit); +} + +std::ostream &Log::Logger::error(bool append) +{ + if (active_level >= Error && _comm_rank == active_rank) { + if (append) + return std::cout; + std::cout << _prefix; + const auto [h, m, s] = get_elapsed_time(); + std::cout << std::setw(3) << h << "h" << std::setw(2) << m << "m" << std::setw(2) << s << "s "; + return std::cout; + } else + return _nullstr; +} + +std::ostream &Log::Logger::info(bool append) +{ + if (active_level >= Info && _comm_rank == active_rank) { + if (append) + return std::cout; + std::cout << _prefix; + const auto [h, m, s] = get_elapsed_time(); + std::cout << std::setw(3) << h << "h" << std::setw(2) << m << "m" << std::setw(2) << s << "s "; + return std::cout; + } else + return _nullstr; +} + +std::ostream &Log::Logger::warn(bool append) +{ + if (active_level >= Warn && _comm_rank == active_rank) { + if (append) + return std::cout; + std::cout << _prefix; + const auto [h, m, s] = get_elapsed_time(); + std::cout << std::setw(3) << h << "h" << std::setw(2) << m << "m" << std::setw(2) << s << "s "; + return std::cout; + } else + return _nullstr; +} + +std::ostream &Log::Logger::debug(bool append) +{ + if (active_level >= Debug && _comm_rank == active_rank) { + if (append) + return std::cout; + std::cout << _prefix; + const auto [h, m, s] = get_elapsed_time(); + std::cout << std::setw(3) << h << "h" << std::setw(2) << m << "m" << std::setw(2) << s << "s "; + return std::cout; + } else + return _nullstr; +} + +Log::MPI_TraceSync Log::Logger::trace(bool append) +{ + return Log::MPI_TraceSync(*this, append); +} + +std::ostream &Log::Logger::trace_impl(bool append) +{ + if (active_level >= All && _comm_rank == active_rank) { + if (append) + return std::cout; + std::cout << _prefix << "(" << active_rank << ") "; + const auto [h, m, s] = get_elapsed_time(); + std::cout << std::setw(3) << h << "h" << std::setw(2) << m << "m" << std::setw(2) << s << "s "; + return std::cout; + } else + return _nullstr; +} + +std::tuple Log::Logger::get_elapsed_time() const +{ + const auto now = std::chrono::steady_clock::now(); + const auto elapsed_seconds = static_cast(std::chrono::duration_cast(now - _start_time).count()) / 1000; + const auto seconds = elapsed_seconds % 60; + const auto hours = elapsed_seconds / 3600; + const auto minutes = (elapsed_seconds - seconds - 3600 * hours) / 60; + + return std::make_tuple(hours, minutes, seconds); +} + + +void Log::Logger::progress(const std::string &prefix, int step, int max) const +{ + if (_comm_rank != active_rank) + return; + + if (step == 0) + std::cout << _prefix << prefix; + + int digits = 1; + int div = 10; + while ((max / div) > 0) { + digits++; + div *= 10; + } + if (step > 0) + for (int i = 0; i < digits; i++) + std::cout << '\b'; + + int step_digits = 1; + div = 10; + while ((step / div) > 0) { + step_digits++; + div *= 10; + } + + for (int i = 0; i < digits - step_digits; i++) + std::cout << ' '; + std::cout << step; + + if (step >= max - 1) { + for (int i = 0; i < digits; i++) + std::cout << '\b'; + for (int i = 0; i < prefix.size(); i++) + std::cout << '\b'; + for (int i = 0; i < _prefix.size(); i++) + std::cout << '\b'; + } + // if (step >= max) std::cout << '\n'; + std::cout.flush(); +} + +void Log::init(int comm_rank, int comm_size, const MPI_Comm &comm) +{ + Level level = Log::Error; + if constexpr (VERBOSITY <= 0) + level = Error; + if constexpr (VERBOSITY == 1) + level = Warn; + if constexpr (VERBOSITY == 2) + level = Debug; + if constexpr (VERBOSITY == 3) + level = Info; + if constexpr (VERBOSITY >= 4) + level = All; + + active_level = level; + general = std::make_unique("\t[FANS-GENERAL] ", comm_rank, comm_size, comm); + solver = std::make_unique("\t[FANS-SOLVER] ", comm_rank, comm_size, comm); + io = std::make_unique("\t[FANS-IO] ", comm_rank, comm_size, comm); +} + +void Log::finalize() +{ + general = nullptr; + solver = nullptr; + io = nullptr; +} + +Log::Level Log::active_level = Info; +std::unique_ptr Log::general = nullptr; +std::unique_ptr Log::solver = nullptr; +std::unique_ptr Log::io = nullptr; + +const std::string Log::level_to_string(Log::Level level) +{ + switch (level) { + case Error: + return "Error"; + case Info: + return "Info"; + case Warn: + return "Warn"; + case Debug: + return "Debug"; + case All: + return "All"; + } + return ""; +} + +void Log::setActiveRank(int rank) +{ + active_rank = rank; +} diff --git a/src/main.cpp b/src/main.cpp index 4832fc6..2b48989 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -2,6 +2,7 @@ #include "matmodel.h" #include "setup.h" #include "solver.h" +#include "logging.h" // Version #include "version.h" @@ -15,17 +16,13 @@ void runSolver(Reader &reader, char input_fn[]) MaterialManager *matmanager = createMaterialManager(reader); Solver *solver = createSolver(reader, matmanager); - if (reader.world_rank == 0) { - printf("\n╔════════════════════════════════════════════════════════════ Load case %zu/%zu: %zu time steps ════════════════════════════════════════════════════════════╗\n", + Log::general->info(true) << Log::format("\n╔════════════════════════════════════════════════════════════ Load case %zu/%zu: %zu time steps ════════════════════════════════════════════════════════════╗\n", load_path_idx + 1, reader.load_cases.size(), reader.load_cases[load_path_idx].n_steps); - } for (size_t time_step_idx = 0; time_step_idx < reader.load_cases[load_path_idx].n_steps; ++time_step_idx) { - if (reader.world_rank == 0) { - printf("║ ▶ Time step %zu/%zu (load case %zu/%zu) ◀ \n", + Log::general->info(true) << Log::format("║ ▶ Time step %zu/%zu (load case %zu/%zu) ◀ \n", time_step_idx + 1, reader.load_cases[load_path_idx].n_steps, load_path_idx + 1, reader.load_cases.size()); - } if (reader.load_cases[load_path_idx].mixed) { solver->enableMixedBC(reader.load_cases[load_path_idx].mbc, time_step_idx); } else { @@ -39,9 +36,7 @@ void runSolver(Reader &reader, char input_fn[]) } delete solver; delete matmanager; - if (reader.world_rank == 0) { - printf("╚══════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════╝\n"); - } + Log::general->info(true) << "╚══════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════╝\n"; } } @@ -59,6 +54,11 @@ int main(int argc, char *argv[]) MPI_Init(NULL, NULL); fftw_mpi_init(); + int rank, size; + MPI_Comm_rank(MPI_COMM_WORLD, &rank); + MPI_Comm_size(MPI_COMM_WORLD, &size); + Log::init(rank, size, MPI_COMM_WORLD); + Log::setActiveRank(0); Reader reader{MPI_COMM_WORLD}; reader.ReadInputFile(argv[1]); @@ -75,6 +75,7 @@ int main(int argc, char *argv[]) } reader.CloseResultsFile(); + Log::finalize(); MPI_Finalize(); return 0; } diff --git a/src/reader.cpp b/src/reader.cpp index 372a856..c62a7bb 100644 --- a/src/reader.cpp +++ b/src/reader.cpp @@ -1,5 +1,6 @@ #include "general.h" #include "reader.h" +#include "logging.h" #include "H5Cpp.h" #include "fftw3-mpi.h" @@ -42,10 +43,8 @@ void Reader::ComputeVolumeFractions() // Calculate total number of materials n_mat = global_max - global_min + 1; - if (world_rank == 0) { - printf("# Number of materials: %i (from %u to %u)\n", n_mat, global_min, global_max); - printf("# Volume fractions\n"); - } + Log::io->info() << Log::format("# Number of materials: %i (from %u to %u)\n", n_mat, global_min, global_max); + Log::io->info() << "# Volume fractions\n"; // Using dynamic allocation for arrays since we don't know size at compile time std::vector vol_frac(n_mat, 0); @@ -61,9 +60,8 @@ void Reader::ComputeVolumeFractions() long vf; MPI_Allreduce(&(vol_frac[i]), &vf, 1, MPI_LONG, MPI_SUM, communicator); v_frac[i] = double(vf) / double(dims[0] * dims[1] * dims[2]); - if (world_rank == 0) - printf("# material %4u vol. frac. %10.4f%% \n", - static_cast(i) + global_min, 100. * v_frac[i]); + Log::io->info() << Log::format("# material %4u vol. frac. %10.4f%% \n", + static_cast(i) + global_min, 100. * v_frac[i]); } } @@ -165,21 +163,20 @@ void Reader ::ReadInputFile(const std::string &input_fn) load_cases.push_back(std::move(lc)); } - if (world_rank == 0) { - printf("# microstructure file name: \t '%s'\n", ms_filename); - printf("# microstructure dataset name: \t '%s'\n", ms_datasetname); - printf("# strain type: \t %s\n", strain_type.c_str()); - printf("# problem type: \t %s\n", problemType.c_str()); - printf("# FE type: \t %s\n", FE_type.c_str()); - printf( - "# FANS error measure: \t %s %s error \n", - errorParameters["type"].get().c_str(), - errorParameters["measure"].get().c_str()); - printf("# FANS Tolerance: \t %10.5e\n", errorParameters["tolerance"].get()); - printf("# Max iterations: \t %6i\n", n_it); - } + Log::io->info() << Log::format("# microstructure file name: \t '%s'\n", ms_filename); + Log::io->info() << Log::format("# microstructure dataset name: \t '%s'\n", ms_datasetname); + Log::io->info() << Log::format("# strain type: \t %s\n", strain_type.c_str()); + Log::io->info() << Log::format("# problem type: \t %s\n", problemType.c_str()); + Log::io->info() << Log::format("# FE type: \t %s\n", FE_type.c_str()); + Log::io->info() << Log::format( + "# FANS error measure: \t %s %s error \n", + errorParameters["type"].get().c_str(), + errorParameters["measure"].get().c_str()); + Log::io->info() << Log::format("# FANS Tolerance: \t %10.5e\n", errorParameters["tolerance"].get()); + Log::io->info() << Log::format("# Max iterations: \t %6i\n", n_it); + } catch (const std::exception &e) { - fprintf(stderr, "ERROR trying to read input file '%s' for FANS: %s\n", input_fn.c_str(), e.what()); + Log::io->error() << Log::format("ERROR trying to read input file '%s' for FANS: %s\n", input_fn.c_str(), e.what()); exit(10); } } @@ -269,12 +266,10 @@ void Reader ::ReadMS(int hm) H5Aclose(attr_id); H5Tclose(attr_type); } - if (world_rank == 0) { - if (is_zyx) { - printf("# Using Z-Y-X dimension ordering for the microstructure data\n"); - } else { - printf("# Using X-Y-Z dimension ordering for the microstructure data\n"); - } + if (is_zyx) { + Log::io->info() << "# Using Z-Y-X dimension ordering for the microstructure data\n"; + } else { + Log::io->info() << "# Using X-Y-Z dimension ordering for the microstructure data\n"; } dims.resize(3); @@ -294,16 +289,16 @@ void Reader ::ReadMS(int hm) l_e[2] = L[2] / double(dims[2]); if (world_rank == 0) { - printf("# grid size set to [%i x %i x %i] --> %i voxels \nMicrostructure length: [%3.6f x %3.6f x %3.6f]\n", dims[0], dims[1], dims[2], dims[0] * dims[1] * dims[2], L[0], L[1], L[2]); + Log::io->info() << Log::format("# grid size set to [%i x %i x %i] --> %i voxels \nMicrostructure length: [%3.6f x %3.6f x %3.6f]\n", dims[0], dims[1], dims[2], dims[0] * dims[1] * dims[2], L[0], L[1], L[2]); if (dims[0] % 2 != 0) - fprintf(stderr, "[ FANS3D_Grid ] WARNING: n_x is not a multiple of 2\n"); + Log::io->error() << "[ FANS3D_Grid ] WARNING: n_x is not a multiple of 2\n"; if (dims[1] % 2 != 0) - fprintf(stderr, "[ FANS3D_Grid ] WARNING: n_y is not a multiple of 2\n"); + Log::io->error() << "[ FANS3D_Grid ] WARNING: n_y is not a multiple of 2\n"; if (dims[2] % 2 != 0) - fprintf(stderr, "[ FANS3D_Grid ] WARNING: n_z is not a multiple of 2\n"); + Log::io->error() << "[ FANS3D_Grid ] WARNING: n_z is not a multiple of 2\n"; if (dims[0] / 4 < world_size) throw std::runtime_error("[ FANS3D_Grid ] ERROR: Please decrease the number of processes or increase the grid size to ensure that each process has at least 4 boxels in the x direction."); - printf("Voxel length: [%1.8f, %1.8f, %1.8f]\n", l_e[0], l_e[1], l_e[2]); + Log::io->info() << Log::format("Voxel length: [%1.8f, %1.8f, %1.8f]\n", l_e[0], l_e[1], l_e[2]); } const ptrdiff_t n[3] = {dims[0], dims[1], dims[2] / 2 + 1}; From 6648ec3ce0689a79307210e9107e00b499da906c Mon Sep 17 00:00:00 2001 From: Alex Hocks Date: Wed, 6 May 2026 18:18:27 +0200 Subject: [PATCH 2/7] add doc --- README.md | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 90b6a6a..ebebbb4 100644 --- a/README.md +++ b/README.md @@ -185,15 +185,15 @@ cd ../test **Build options:** -| CMake Option | Description | Default | -| -------------- | ------------- | --------- | -| `CMAKE_BUILD_TYPE` | Build type: `Debug`, `Release`, `RelWithDebInfo` | `NONE` | -| `CMAKE_INTERPROCEDURAL_OPTIMIZATION` | Enable link-time optimization (LTO) | `ON` (if supported) | -| `FANS_BUILD_STATIC` | Build static library | `OFF` | -| `CMAKE_INSTALL_PREFIX` | Installation directory | System default | -| `FANS_LIBRARY_FOR_MICRO_MANAGER` | Build Python bindings using Pybind11 (needed) | `OFF` | -| `FANS_ENABLE_SANITIZERS` | Enable runtime sanitizers (AddressSanitizer and LeakSanitizer) for memory debugging | `OFF` | - +| CMake Option | Description | Default | +|--------------------------------------|-------------------------------------------------------------------------------------|---------------------| +| `CMAKE_BUILD_TYPE` | Build type: `Debug`, `Release`, `RelWithDebInfo` | `NONE` | +| `CMAKE_INTERPROCEDURAL_OPTIMIZATION` | Enable link-time optimization (LTO) | `ON` (if supported) | +| `FANS_BUILD_STATIC` | Build static library | `OFF` | +| `CMAKE_INSTALL_PREFIX` | Installation directory | System default | +| `FANS_LIBRARY_FOR_MICRO_MANAGER` | Build Python bindings using Pybind11 (needed) | `OFF` | +| `FANS_ENABLE_SANITIZERS` | Enable runtime sanitizers (AddressSanitizer and LeakSanitizer) for memory debugging | `OFF` | +| `FANS_VERBOSITY` | Set to value between 0 (none) and 4 (all) | `3` | --- ## Python environment for the FANS dashboard From b828603353630e728e29084e306247b18f152c55 Mon Sep 17 00:00:00 2001 From: Alex Hocks Date: Wed, 6 May 2026 18:18:59 +0200 Subject: [PATCH 3/7] fix format --- include/logging.h | 4 ++-- include/solver.h | 4 ++-- src/logging.cpp | 9 ++++----- src/main.cpp | 6 +++--- src/reader.cpp | 2 +- 5 files changed, 12 insertions(+), 13 deletions(-) diff --git a/include/logging.h b/include/logging.h index 806ab0a..7da07ba 100644 --- a/include/logging.h +++ b/include/logging.h @@ -100,9 +100,9 @@ void finalize(); * @return formatted string */ template -std::string format(const std::string &fmt, Args&&... args) +std::string format(const std::string &fmt, Args &&...args) { - const int buf_size = std::snprintf(nullptr, 0, fmt.c_str(), args...); + const int buf_size = std::snprintf(nullptr, 0, fmt.c_str(), args...); std::vector buf(buf_size + 1); std::snprintf(buf.data(), buf.size(), fmt.c_str(), args...); return {buf.data(), static_cast(buf_size)}; diff --git a/include/solver.h b/include/solver.h index a325e9d..0c17fcb 100644 --- a/include/solver.h +++ b/include/solver.h @@ -695,8 +695,8 @@ void Solver::postprocess(Reader &reader, int load_idx, int time_ hsize_t dims[2] = {static_cast(n_str), static_cast(n_str)}; if (world_rank == 0) { Log::solver->info() << "# Homogenized tangent: \n" - << std::setprecision(12) << homogenized_tangent - << std::defaultfloat << "\n\n"; + << std::setprecision(12) << homogenized_tangent + << std::defaultfloat << "\n\n"; } reader.writeData("homogenized_tangent", load_idx, time_idx, homogenized_tangent.data(), dims, 2); } diff --git a/src/logging.cpp b/src/logging.cpp index efcb63d..68b0d2e 100644 --- a/src/logging.cpp +++ b/src/logging.cpp @@ -108,16 +108,15 @@ std::ostream &Log::Logger::trace_impl(bool append) std::tuple Log::Logger::get_elapsed_time() const { - const auto now = std::chrono::steady_clock::now(); + const auto now = std::chrono::steady_clock::now(); const auto elapsed_seconds = static_cast(std::chrono::duration_cast(now - _start_time).count()) / 1000; - const auto seconds = elapsed_seconds % 60; - const auto hours = elapsed_seconds / 3600; - const auto minutes = (elapsed_seconds - seconds - 3600 * hours) / 60; + const auto seconds = elapsed_seconds % 60; + const auto hours = elapsed_seconds / 3600; + const auto minutes = (elapsed_seconds - seconds - 3600 * hours) / 60; return std::make_tuple(hours, minutes, seconds); } - void Log::Logger::progress(const std::string &prefix, int step, int max) const { if (_comm_rank != active_rank) diff --git a/src/main.cpp b/src/main.cpp index 2b48989..9602d2f 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -17,12 +17,12 @@ void runSolver(Reader &reader, char input_fn[]) Solver *solver = createSolver(reader, matmanager); Log::general->info(true) << Log::format("\n╔════════════════════════════════════════════════════════════ Load case %zu/%zu: %zu time steps ════════════════════════════════════════════════════════════╗\n", - load_path_idx + 1, reader.load_cases.size(), reader.load_cases[load_path_idx].n_steps); + load_path_idx + 1, reader.load_cases.size(), reader.load_cases[load_path_idx].n_steps); for (size_t time_step_idx = 0; time_step_idx < reader.load_cases[load_path_idx].n_steps; ++time_step_idx) { Log::general->info(true) << Log::format("║ ▶ Time step %zu/%zu (load case %zu/%zu) ◀ \n", - time_step_idx + 1, reader.load_cases[load_path_idx].n_steps, - load_path_idx + 1, reader.load_cases.size()); + time_step_idx + 1, reader.load_cases[load_path_idx].n_steps, + load_path_idx + 1, reader.load_cases.size()); if (reader.load_cases[load_path_idx].mixed) { solver->enableMixedBC(reader.load_cases[load_path_idx].mbc, time_step_idx); } else { diff --git a/src/reader.cpp b/src/reader.cpp index c62a7bb..1b77abc 100644 --- a/src/reader.cpp +++ b/src/reader.cpp @@ -61,7 +61,7 @@ void Reader::ComputeVolumeFractions() MPI_Allreduce(&(vol_frac[i]), &vf, 1, MPI_LONG, MPI_SUM, communicator); v_frac[i] = double(vf) / double(dims[0] * dims[1] * dims[2]); Log::io->info() << Log::format("# material %4u vol. frac. %10.4f%% \n", - static_cast(i) + global_min, 100. * v_frac[i]); + static_cast(i) + global_min, 100. * v_frac[i]); } } From 73719cb60b76dbf46df9abaa20da1ae55264652c Mon Sep 17 00:00:00 2001 From: Alex Hocks Date: Wed, 6 May 2026 18:24:13 +0200 Subject: [PATCH 4/7] add changelog entry --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index f981cc4..b68637b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ ## latest +- Added logging for FANS [#143](https://github.com/DataAnalyticsEngineering/FANS/pull/143) - Bugfix: mem-leak while reading microstructure and added wider CMake support [#140](https://github.com/DataAnalyticsEngineering/FANS/pull/140) - Added MPI communicator abstraction [#139](https://github.com/DataAnalyticsEngineering/FANS/pull/139) From 66e78cb04d2830a9325a5114d62a72f2d9168f40 Mon Sep 17 00:00:00 2001 From: Alex Hocks Date: Wed, 6 May 2026 21:35:40 +0200 Subject: [PATCH 5/7] fix doc format --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index ebebbb4..8c65160 100644 --- a/README.md +++ b/README.md @@ -194,6 +194,7 @@ cd ../test | `FANS_LIBRARY_FOR_MICRO_MANAGER` | Build Python bindings using Pybind11 (needed) | `OFF` | | `FANS_ENABLE_SANITIZERS` | Enable runtime sanitizers (AddressSanitizer and LeakSanitizer) for memory debugging | `OFF` | | `FANS_VERBOSITY` | Set to value between 0 (none) and 4 (all) | `3` | + --- ## Python environment for the FANS dashboard From b7d992f99444c3916fa68bb502c09ec7f61752ce Mon Sep 17 00:00:00 2001 From: Alex Hocks Date: Wed, 6 May 2026 21:36:51 +0200 Subject: [PATCH 6/7] Fix Segfault for pyFANS --- pyfans/micro.cpp | 4 ++++ src/logging.cpp | 8 +++++++- src/main.cpp | 1 - 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/pyfans/micro.cpp b/pyfans/micro.cpp index 2e9f7b9..ead9766 100644 --- a/pyfans/micro.cpp +++ b/pyfans/micro.cpp @@ -6,6 +6,7 @@ #include "micro.hpp" #include "setup.h" #include "matmodel.h" +#include "mpi.h" py::array_t merge_arrays(py::array_t array1, py::array_t array2) { @@ -58,6 +59,9 @@ MicroSimulation::MicroSimulation(int sim_id, const std::string &input_file, cons MPI_Comm_rank(reader.communicator, &reader.world_rank); MPI_Comm_size(reader.communicator, &reader.world_size); + // initialize loggers + Log::init(reader.world_rank, reader.world_size, reader.communicator); + // Input file name is hardcoded. TODO: Make it configurable reader.ReadInputFile(input_file); diff --git a/src/logging.cpp b/src/logging.cpp index 68b0d2e..ca10f60 100644 --- a/src/logging.cpp +++ b/src/logging.cpp @@ -2,8 +2,10 @@ #include "mpi.h" #include #include +#include -int active_rank = 0; +int active_rank = 0; +std::atomic is_initialized = 0; Log::MPI_TraceSync::MPI_TraceSync(Logger &log, bool append) : _log(log), _append(append) {} @@ -160,6 +162,9 @@ void Log::Logger::progress(const std::string &prefix, int step, int max) const void Log::init(int comm_rank, int comm_size, const MPI_Comm &comm) { + if (is_initialized.fetch_or(1)) + return; + Level level = Log::Error; if constexpr (VERBOSITY <= 0) level = Error; @@ -173,6 +178,7 @@ void Log::init(int comm_rank, int comm_size, const MPI_Comm &comm) level = All; active_level = level; + active_rank = 0; general = std::make_unique("\t[FANS-GENERAL] ", comm_rank, comm_size, comm); solver = std::make_unique("\t[FANS-SOLVER] ", comm_rank, comm_size, comm); io = std::make_unique("\t[FANS-IO] ", comm_rank, comm_size, comm); diff --git a/src/main.cpp b/src/main.cpp index 9602d2f..ca6dc4b 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -58,7 +58,6 @@ int main(int argc, char *argv[]) MPI_Comm_rank(MPI_COMM_WORLD, &rank); MPI_Comm_size(MPI_COMM_WORLD, &size); Log::init(rank, size, MPI_COMM_WORLD); - Log::setActiveRank(0); Reader reader{MPI_COMM_WORLD}; reader.ReadInputFile(argv[1]); From 28feaa18e210dfda2b23801f7276bb2ac88c253c Mon Sep 17 00:00:00 2001 From: Alex Hocks Date: Wed, 6 May 2026 21:37:12 +0200 Subject: [PATCH 7/7] Unify formatting --- include/MaterialManager.h | 6 ++++-- include/solver.h | 20 ++++++++++---------- include/solverCG.h | 8 +++++--- include/solverFP.h | 3 +-- src/reader.cpp | 23 +++++++++++------------ 5 files changed, 31 insertions(+), 29 deletions(-) diff --git a/include/MaterialManager.h b/include/MaterialManager.h index 859f870..82c074e 100644 --- a/include/MaterialManager.h +++ b/include/MaterialManager.h @@ -111,9 +111,11 @@ class MaterialManager { compute_reference_stiffness(reader); // Print detailed information about material configuration for logging - Log::io->info() << "\n# MaterialManager initialized:\n"; + Log::io->info() << "\n"; + Log::io->info() << "# MaterialManager initialized:\n"; Log::io->info() << Log::format("# Number of material models: %zu\n", models.size()); - Log::io->info() << Log::format("# Number of phases: %d\n#\n", n_phases); + Log::io->info() << Log::format("# Number of phases: %d\n", n_phases); + Log::io->info() << "#\n"; for (size_t i = 0; i < mats.size(); ++i) { const auto &mg = mats[i]; diff --git a/include/solver.h b/include/solver.h index 0c17fcb..bcfec35 100644 --- a/include/solver.h +++ b/include/solver.h @@ -61,7 +61,7 @@ class Solver : private MixedBCController { void postprocess(Reader &reader, int load_idx, int time_idx); //!< Computes Strain and stress void convolution(); - double compute_error(RealArray &r); + double compute_error(RealArray &r, bool has_log_prefix = false); void CreateFFTWPlans(double *in, fftw_complex *transformed, double *out); VectorXd homogenized_strain; @@ -151,7 +151,8 @@ Solver::Solver(Reader &reader, MaterialManager * template void Solver::computeFundamentalSolution() { - Log::solver->info() << "\n# Start creating Fundamental Solution(s) \n"; + Log::solver->info() << "\n"; + Log::solver->info() << "# Start creating Fundamental Solution(s) \n"; clock_t tot_time = clock(); Matrix Ker0 = matmanager->models[0]->Compute_Reference_ElementStiffness(matmanager->kapparef_mat); @@ -412,7 +413,7 @@ void Solver::convolution() } template -double Solver::compute_error(RealArray &r) +double Solver::compute_error(RealArray &r, const bool has_log_prefix) { double err_local; const std::string &measure = reader.errorParameters["measure"].get(); @@ -436,7 +437,7 @@ double Solver::compute_error(RealArray &r) if (iter == 0) { Log::solver->info() << Log::format("Before 1st iteration: %16.8e\n", err0); } else { - Log::solver->info(true) << Log::format("it %3lu .... err %16.8e / %8.4e, ratio: %4.8e, FFT time: %2.6f sec\n", iter, err, err / err0, (iter == 1 ? 0.0 : err / err_all[iter - 1]), double(buftime) / CLOCKS_PER_SEC); + Log::solver->info(has_log_prefix) << Log::format("it %3lu .... err %16.8e / %8.4e, ratio: %4.8e, FFT time: %2.6f sec\n", iter, err, err / err0, (iter == 1 ? 0.0 : err / err_all[iter - 1]), double(buftime) / CLOCKS_PER_SEC); } const std::string &error_type = reader.errorParameters["type"].get(); @@ -692,12 +693,11 @@ void Solver::postprocess(Reader &reader, int load_idx, int time_ // Compute homogenized tangent only if requested if (find(reader.resultsToWrite.begin(), reader.resultsToWrite.end(), "homogenized_tangent") != reader.resultsToWrite.end()) { homogenized_tangent = get_homogenized_tangent(1e-6); - hsize_t dims[2] = {static_cast(n_str), static_cast(n_str)}; - if (world_rank == 0) { - Log::solver->info() << "# Homogenized tangent: \n" - << std::setprecision(12) << homogenized_tangent - << std::defaultfloat << "\n\n"; - } + hsize_t dims[2] = {static_cast(n_str), static_cast(n_str)}; + IOFormat tangent_format{StreamPrecision, 0, " ", "\n", "\t\t"}; + Log::solver->info() << "# Homogenized tangent: \n" + << std::setprecision(12) << homogenized_tangent.format(tangent_format) + << std::defaultfloat << "\n\n"; reader.writeData("homogenized_tangent", load_idx, time_idx, homogenized_tangent.data(), dims, 2); } } diff --git a/include/solverCG.h b/include/solverCG.h index 369ad3a..f8eb844 100644 --- a/include/solverCG.h +++ b/include/solverCG.h @@ -63,7 +63,8 @@ double SolverCG::dotProduct(RealArray &a, RealArray &b) template void SolverCG::internalSolve() { - Log::solver->info() << "\t# Start FANS - Conjugate Gradient Solver \n"; + Log::solver->info() << "\n"; + Log::solver->info() << "# Start FANS - Conjugate Gradient Solver \n"; bool islinear = this->matmanager->all_linear; alpha_warm = 0.1; @@ -92,7 +93,8 @@ void SolverCG::internalSolve() delta0 = delta; delta = dotProduct(v_r_real, s_real); - if (islinear && !this->isMixedBCActive()) { + const bool no_lss = islinear && !this->isMixedBCActive(); + if (no_lss) { d_real = s_real + fmax(0.0, (delta - deltamid) / delta0) * d_real; Matrix res_e; this->template compute_residual_basic<0>(rnew_real, d_real, @@ -111,7 +113,7 @@ void SolverCG::internalSolve() } iter++; - err_rel = this->compute_error(v_r_real); + err_rel = this->compute_error(v_r_real, not no_lss); if (iter >= 2 && this->err_all[iter] > this->err_all[iter - 1] && this->err_all[iter - 1] > this->err_all[iter - 2]) ls_converged = false; // Force a CG restart diff --git a/include/solverFP.h b/include/solverFP.h index 189e2c8..6eb0a78 100644 --- a/include/solverFP.h +++ b/include/solverFP.h @@ -50,7 +50,6 @@ void SolverFP::internalSolve() iter++; err_rel = this->compute_error(v_r_real); } - if (this->world_rank == 0) - Log::solver->info() << "# Complete FANS - Fixed Point Solver \n"; + Log::solver->info() << "# Complete FANS - Fixed Point Solver \n"; } #endif diff --git a/src/reader.cpp b/src/reader.cpp index 1b77abc..d458783 100644 --- a/src/reader.cpp +++ b/src/reader.cpp @@ -288,18 +288,17 @@ void Reader ::ReadMS(int hm) l_e[1] = L[1] / double(dims[1]); l_e[2] = L[2] / double(dims[2]); - if (world_rank == 0) { - Log::io->info() << Log::format("# grid size set to [%i x %i x %i] --> %i voxels \nMicrostructure length: [%3.6f x %3.6f x %3.6f]\n", dims[0], dims[1], dims[2], dims[0] * dims[1] * dims[2], L[0], L[1], L[2]); - if (dims[0] % 2 != 0) - Log::io->error() << "[ FANS3D_Grid ] WARNING: n_x is not a multiple of 2\n"; - if (dims[1] % 2 != 0) - Log::io->error() << "[ FANS3D_Grid ] WARNING: n_y is not a multiple of 2\n"; - if (dims[2] % 2 != 0) - Log::io->error() << "[ FANS3D_Grid ] WARNING: n_z is not a multiple of 2\n"; - if (dims[0] / 4 < world_size) - throw std::runtime_error("[ FANS3D_Grid ] ERROR: Please decrease the number of processes or increase the grid size to ensure that each process has at least 4 boxels in the x direction."); - Log::io->info() << Log::format("Voxel length: [%1.8f, %1.8f, %1.8f]\n", l_e[0], l_e[1], l_e[2]); - } + Log::io->info() << Log::format("# grid size set to [%i x %i x %i] --> %i voxels \n", dims[0], dims[1], dims[2], dims[0] * dims[1] * dims[2]); + Log::io->info() << Log::format("Microstructure length: [%3.6f x %3.6f x %3.6f]\n", L[0], L[1], L[2]); + if (dims[0] % 2 != 0) + Log::io->error() << "[ FANS3D_Grid ] WARNING: n_x is not a multiple of 2\n"; + if (dims[1] % 2 != 0) + Log::io->error() << "[ FANS3D_Grid ] WARNING: n_y is not a multiple of 2\n"; + if (dims[2] % 2 != 0) + Log::io->error() << "[ FANS3D_Grid ] WARNING: n_z is not a multiple of 2\n"; + if (dims[0] / 4 < world_size) + throw std::runtime_error("[ FANS3D_Grid ] ERROR: Please decrease the number of processes or increase the grid size to ensure that each process has at least 4 boxels in the x direction."); + Log::io->info() << Log::format("Voxel length: [%1.8f, %1.8f, %1.8f]\n", l_e[0], l_e[1], l_e[2]); const ptrdiff_t n[3] = {dims[0], dims[1], dims[2] / 2 + 1}; ptrdiff_t block0 = FFTW_MPI_DEFAULT_BLOCK;