diff --git a/BaseLib/FileTools.cpp b/BaseLib/FileTools.cpp index ce1d34389d69d21457eaf2f26e3a52dd243e3c82..22205529a1f2f9679adee48e3fd6eed61e38686e 100644 --- a/BaseLib/FileTools.cpp +++ b/BaseLib/FileTools.cpp @@ -100,6 +100,16 @@ bool substituteKeyword(std::string& result, return false; } + if constexpr (std::is_same_v<std::remove_cvref_t<T>, bool>) + { + if (keyword == "converged") + { + std::string r = data ? "" : "_not_converged"; + result.replace(begin, end - begin + 1, r); + return true; + } + } + std::unordered_map<std::type_index, char> type_specification; type_specification[std::type_index(typeid(int))] = 'd'; type_specification[std::type_index(typeid(double))] = 'f'; // default @@ -127,7 +137,8 @@ std::string constructFormattedFileName(std::string const& format_specification, std::string const& mesh_name, int const timestep, double const t, - int const iteration) + int const iteration, + bool const converged) { char const open_char = '{'; char const close_char = '}'; @@ -144,7 +155,9 @@ std::string constructFormattedFileName(std::string const& format_specification, getParenthesizedString(result, open_char, close_char, begin); if (!substituteKeyword(result, str, begin, end, "timestep", timestep) && !substituteKeyword(result, str, begin, end, "time", t) && - !substituteKeyword(result, str, begin, end, "iteration", iteration)) + !substituteKeyword(result, str, begin, end, "iteration", + iteration) && + !substituteKeyword(result, str, begin, end, "converged", converged)) { substituteKeyword(result, str, begin, end, "meshname", mesh_name); } diff --git a/BaseLib/FileTools.h b/BaseLib/FileTools.h index 3174e872e29c873fc725cf4ca7c4faced016f527..7df3c474f3bd25f2b286ce75bde26d16111e0c9d 100644 --- a/BaseLib/FileTools.h +++ b/BaseLib/FileTools.h @@ -49,9 +49,9 @@ getParenthesizedString(std::string const& in, std::string constructFormattedFileName(std::string const& format_specification, std::string const& mesh_name, - int const timestep, - double const t, - int const iteration); + int const timestep, double const t, + int const iteration, + bool const converged); std::vector<double> readDoublesFromBinaryFile(const std::string& filename); diff --git a/Documentation/ProjectFile/prj/time_loop/output/t_prefix.md b/Documentation/ProjectFile/prj/time_loop/output/t_prefix.md index c07b08e47a0bfeb5aadace08a7e042e495e10630..05f143d9bd33a12671eb1a84f5ca9f7e6a5b4fb4 100644 --- a/Documentation/ProjectFile/prj/time_loop/output/t_prefix.md +++ b/Documentation/ProjectFile/prj/time_loop/output/t_prefix.md @@ -1,6 +1,6 @@ Template for the prefix which will be prepended to the output files. Allowed template expressions are: `{:meshname}`, `{:timestep}`, `{:time}`, -`{:iteration}`. +`{:iteration}`, `{:converged}`. Default value is `{:meshname}`. @@ -11,3 +11,6 @@ Furthermore, it is possible to specify the format of the expressions above. For instance {:0>3timestep} results in a 3-digit output, if necessary with preceding zeros. Also the time output can be formatted using the typical floating point precision (for instance 0.4) and type (e, E, f, F, g, G) modifiers. + +If the non-linear solver norms are not met the `{:converged}` expression is replaced with +`_not_converged` and removed otherwise. diff --git a/Documentation/ProjectFile/prj/time_loop/output/t_suffix.md b/Documentation/ProjectFile/prj/time_loop/output/t_suffix.md index 9eca90864d49ceaa573e92ccfb3240feabe7e10d..87310169ba15979c3c70546f10e75234695449bd 100644 --- a/Documentation/ProjectFile/prj/time_loop/output/t_suffix.md +++ b/Documentation/ProjectFile/prj/time_loop/output/t_suffix.md @@ -1,6 +1,6 @@ Template for the suffix which will be appended to the output files. Allowed template expressions are: `{:meshname}`, `{:timestep}`, `{:time}`, -`{:iteration}`. +`{:iteration}`, `{:converged}`. Default value is `_ts_{:timestep}_t_{:time}`. @@ -11,3 +11,6 @@ Furthermore, it is possible to specify the format of the expressions above. For instance {:0>3timestep} results in a 3-digit output, if necessary with preceding zeros. Also the time output can be formatted using the typical floating point precision (for instance 0.4) and type (e, E, f, F, g, G) modifiers. + +If the non-linear solver norms are not met the `{:converged}` expression is replaced with +`_not_converged` and removed otherwise. diff --git a/NumLib/ODESolver/NonlinearSolver.cpp b/NumLib/ODESolver/NonlinearSolver.cpp index f689ddba6a3082ec39c163628cbdb2ca55f68712..62ea46f0aa8a3f0f5da2b6168f813b56bcbcc952 100644 --- a/NumLib/ODESolver/NonlinearSolver.cpp +++ b/NumLib/ODESolver/NonlinearSolver.cpp @@ -126,7 +126,7 @@ void NonlinearSolver<NonlinearSolverTag::Picard>:: NonlinearSolverStatus NonlinearSolver<NonlinearSolverTag::Picard>::solve( std::vector<GlobalVector*>& x, std::vector<GlobalVector*> const& x_prev, - std::function<void(int, std::vector<GlobalVector*> const&)> const& + std::function<void(int, bool, std::vector<GlobalVector*> const&)> const& postIterationCallback, int const process_id) { @@ -212,7 +212,7 @@ NonlinearSolverStatus NonlinearSolver<NonlinearSolverTag::Picard>::solve( { if (postIterationCallback) { - postIterationCallback(iteration, x_new); + postIterationCallback(iteration, error_norms_met, x_new); } switch (sys.postIteration(x_new_process)) @@ -330,7 +330,7 @@ void NonlinearSolver<NonlinearSolverTag::Newton>:: NonlinearSolverStatus NonlinearSolver<NonlinearSolverTag::Newton>::solve( std::vector<GlobalVector*>& x, std::vector<GlobalVector*> const& x_prev, - std::function<void(int, std::vector<GlobalVector*> const&)> const& + std::function<void(int, bool, std::vector<GlobalVector*> const&)> const& postIterationCallback, int const process_id) { @@ -453,7 +453,7 @@ NonlinearSolverStatus NonlinearSolver<NonlinearSolverTag::Newton>::solve( if (postIterationCallback) { - postIterationCallback(iteration, x_new); + postIterationCallback(iteration, error_norms_met, x_new); } switch (sys.postIteration(*x_new[process_id])) diff --git a/NumLib/ODESolver/NonlinearSolver.h b/NumLib/ODESolver/NonlinearSolver.h index f4fede727ffd0908efd18c2f6d9ebf6d83327377..494085557f220117296d4be75ff54c5c2310a439 100644 --- a/NumLib/ODESolver/NonlinearSolver.h +++ b/NumLib/ODESolver/NonlinearSolver.h @@ -53,7 +53,7 @@ public: virtual NonlinearSolverStatus solve( std::vector<GlobalVector*>& x, std::vector<GlobalVector*> const& x_prev, - std::function<void(int, std::vector<GlobalVector*> const&)> const& + std::function<void(int, bool, std::vector<GlobalVector*> const&)> const& postIterationCallback, int const process_id) = 0; @@ -118,7 +118,7 @@ public: NonlinearSolverStatus solve( std::vector<GlobalVector*>& x, std::vector<GlobalVector*> const& x_prev, - std::function<void(int, std::vector<GlobalVector*> const&)> const& + std::function<void(int, bool, std::vector<GlobalVector*> const&)> const& postIterationCallback, int const process_id) override; @@ -202,7 +202,7 @@ public: NonlinearSolverStatus solve( std::vector<GlobalVector*>& x, std::vector<GlobalVector*> const& x_prev, - std::function<void(int, std::vector<GlobalVector*> const&)> const& + std::function<void(int, bool, std::vector<GlobalVector*> const&)> const& postIterationCallback, int const process_id) override; diff --git a/NumLib/ODESolver/PETScNonlinearSolver.cpp b/NumLib/ODESolver/PETScNonlinearSolver.cpp index 73691919bfbd6070a5a2fcf136362bd8e78959c5..01f99861805bd92dff499c94d4d81872ac2db99f 100644 --- a/NumLib/ODESolver/PETScNonlinearSolver.cpp +++ b/NumLib/ODESolver/PETScNonlinearSolver.cpp @@ -149,7 +149,7 @@ NonlinearSolverStatus PETScNonlinearSolver::solve( std::vector<GlobalVector*>& x, std::vector<GlobalVector*> const& x_prev, std::function<void( - int, + int, bool, std::vector<GlobalVector*> const&)> const& /*postIterationCallback*/, int const process_id) { diff --git a/NumLib/ODESolver/PETScNonlinearSolver.h b/NumLib/ODESolver/PETScNonlinearSolver.h index b216aa41cf16d40251dea2ab0ba31171ad9f052f..0a682ac9b84f9aed3ba0178bc2d41f80f96a4c0e 100644 --- a/NumLib/ODESolver/PETScNonlinearSolver.h +++ b/NumLib/ODESolver/PETScNonlinearSolver.h @@ -51,7 +51,7 @@ public: NonlinearSolverStatus solve( std::vector<GlobalVector*>& x, std::vector<GlobalVector*> const& x_prev, - std::function<void(int, std::vector<GlobalVector*> const&)> const& + std::function<void(int, bool, std::vector<GlobalVector*> const&)> const& postIterationCallback, int const process_id) override; diff --git a/ProcessLib/Output/Output.cpp b/ProcessLib/Output/Output.cpp index 36c7247c83c3ff3f29dee25e80cc5bd6d00d826c..84cf4df4d9a6fb9c0a7a7b69e1a85b91b05ff415 100644 --- a/ProcessLib/Output/Output.cpp +++ b/ProcessLib/Output/Output.cpp @@ -176,6 +176,7 @@ void Output::doNotProjectFromBulkMeshToSubmeshes( void Output::outputMeshes( const int timestep, const double t, const int iteration, + bool const converged, std::vector<std::reference_wrapper<const MeshLib::Mesh>> const& meshes) const { @@ -208,7 +209,7 @@ void Output::outputMeshes( } } } - _output_format->outputMeshes(timestep, t, iteration, meshes, + _output_format->outputMeshes(timestep, t, iteration, converged, meshes, _output_data_specification.output_variables); } @@ -291,6 +292,7 @@ void Output::doOutputAlways(Process const& process, int const timestep, const NumLib::Time& t, int const iteration, + bool const converged, std::vector<GlobalVector*> const& xs) const { BaseLib::RunTime time_output; @@ -327,7 +329,7 @@ void Output::doOutputAlways(Process const& process, } } - outputMeshes(timestep, t(), iteration, std::move(output_meshes)); + outputMeshes(timestep, t(), iteration, converged, std::move(output_meshes)); INFO("[time] Output of timestep {:d} took {:g} s.", timestep, time_output.elapsed()); @@ -338,11 +340,13 @@ void Output::doOutput(Process const& process, int const timestep, const NumLib::Time& t, int const iteration, + bool const converged, std::vector<GlobalVector*> const& xs) const { if (isOutputStep(timestep, t)) { - doOutputAlways(process, process_id, timestep, t, iteration, xs); + doOutputAlways(process, process_id, timestep, t, iteration, converged, + xs); } #ifdef OGS_USE_INSITU // Note: last time step may be output twice: here and in @@ -357,11 +361,13 @@ void Output::doOutputLastTimestep(Process const& process, int const timestep, const NumLib::Time& t, int const iteration, + bool const converged, std::vector<GlobalVector*> const& xs) const { if (!isOutputStep(timestep, t)) { - doOutputAlways(process, process_id, timestep, t, iteration, xs); + doOutputAlways(process, process_id, timestep, t, iteration, converged, + xs); } #ifdef OGS_USE_INSITU InSituLib::CoProcess(process.getMesh(), t, timestep, true, @@ -371,7 +377,7 @@ void Output::doOutputLastTimestep(Process const& process, void Output::doOutputNonlinearIteration( Process const& process, const int process_id, int const timestep, - const NumLib::Time& t, int const iteration, + const NumLib::Time& t, int const iteration, bool const converged, std::vector<GlobalVector*> const& xs) const { if (!_output_nonlinear_iteration_results) @@ -395,7 +401,7 @@ void Output::doOutputNonlinearIteration( } std::string const output_file_name = _output_format->constructFilename( - process.getMesh().getName(), timestep, t(), iteration); + process.getMesh().getName(), timestep, t(), iteration, converged); std::string const output_file_path = BaseLib::joinPaths(_output_format->directory, output_file_name); @@ -423,8 +429,10 @@ bool Output::isOutputStep(int const timestep, NumLib::Time const& t) const std::vector<std::string> Output::getFileNamesForOutput() const { auto construct_filename = ranges::views::transform( - [&](auto const& output_name) - { return _output_format->constructFilename(output_name, 0, 0, 0); }); + [&](auto const& output_name) { + return _output_format->constructFilename(output_name, 0, 0, 0, + true); + }); return _mesh_names_for_output | construct_filename | ranges::to<std::vector>; diff --git a/ProcessLib/Output/Output.h b/ProcessLib/Output/Output.h index 1fd79d73c552f6285c9366e64464415ff29f1a13..3dd872250b252a81fd58319550b6889d4d90e6a6 100644 --- a/ProcessLib/Output/Output.h +++ b/ProcessLib/Output/Output.h @@ -61,7 +61,7 @@ public: //! the given \c timestep. void doOutput(Process const& process, const int process_id, int const timestep, const NumLib::Time& t, - int const iteration, + int const iteration, bool const converged, std::vector<GlobalVector*> const& xs) const; //! Writes output for the given \c process if it has not been written yet. @@ -69,7 +69,7 @@ public: //! order to make sure that its results are written. void doOutputLastTimestep(Process const& process, const int process_id, int const timestep, const NumLib::Time& t, - int const iteration, + int const iteration, bool const converged, std::vector<GlobalVector*> const& xs) const; //! Writes output for the given \c process. @@ -77,7 +77,7 @@ public: //! It is intended to write output in error handling routines. void doOutputAlways(Process const& process, const int process_id, int const timestep, const NumLib::Time& t, - int const iteration, + int const iteration, bool const converged, std::vector<GlobalVector*> const& xs) const; //! Writes output for the given \c process. @@ -85,6 +85,7 @@ public: void doOutputNonlinearIteration(Process const& process, const int process_id, int const timestep, const NumLib::Time& t, const int iteration, + bool const converged, std::vector<GlobalVector*> const& xs) const; //! Tells if output will be written at the specified timestep/time. @@ -107,6 +108,7 @@ private: void outputMeshes( int const timestep, const double t, int const iteration, + bool const converged, std::vector<std::reference_wrapper<const MeshLib::Mesh>> const& meshes) const; diff --git a/ProcessLib/Output/OutputFormat.cpp b/ProcessLib/Output/OutputFormat.cpp index 0443c038c00c21e36e7271be5dee34da6d91497d..30ffe445d2f30a66b182043c8d88b9bdf0eb40b7 100644 --- a/ProcessLib/Output/OutputFormat.cpp +++ b/ProcessLib/Output/OutputFormat.cpp @@ -56,10 +56,11 @@ void outputMeshVtk(std::string const& file_name, MeshLib::Mesh const& mesh, void outputMeshVtk(OutputVTKFormat const& output_file, MeshLib::IO::PVDFile& pvd_file, MeshLib::Mesh const& mesh, - double const t, int const timestep, int const iteration) + double const t, int const timestep, int const iteration, + bool const converged) { - auto const name = - output_file.constructFilename(mesh.getName(), timestep, t, iteration); + auto const name = output_file.constructFilename(mesh.getName(), timestep, t, + iteration, converged); pvd_file.addVTUFile(name, t); auto const path = BaseLib::joinPaths(output_file.directory, name); @@ -76,7 +77,7 @@ std::string OutputVTKFormat::constructPVDName( { return BaseLib::joinPaths( directory, - BaseLib::constructFormattedFileName(prefix, mesh_name, 0, 0, 0) + + BaseLib::constructFormattedFileName(prefix, mesh_name, 0, 0, 0, true) + ".pvd"); } @@ -92,34 +93,36 @@ OutputFormat::OutputFormat(std::string const& directory, std::string prefix, std::string OutputVTKFormat::constructFilename(std::string const& mesh_name, int const timestep, double const t, - int const iteration) const + int const iteration, + bool const converged) const { return BaseLib::constructFormattedFileName(prefix, mesh_name, timestep, t, - iteration) + + iteration, converged) + BaseLib::constructFormattedFileName(suffix, mesh_name, timestep, t, - iteration) + + iteration, converged) + ".vtu"; } std::string OutputXDMFHDF5Format::constructFilename( std::string const& mesh_name, int const timestep, double const t, - int const iteration) const + int const iteration, bool const converged) const { return BaseLib::constructFormattedFileName(prefix, mesh_name, timestep, t, - iteration) + + iteration, converged) + ".xdmf"; } void OutputXDMFHDF5Format::outputMeshXdmf( std::set<std::string> const& output_variables, std::vector<std::reference_wrapper<const MeshLib::Mesh>> const& meshes, - int const timestep, double const t, int const iteration) const + int const timestep, double const t, int const iteration, + bool const converged) const { // \TODO The XdmfOutput will create on construction the XdmfHdfWriter if (!mesh_xdmf_hdf_writer) { auto name = constructFilename(meshes[0].get().getName(), timestep, t, - iteration); + iteration, converged); std::filesystem::path path(BaseLib::joinPaths(directory, name)); mesh_xdmf_hdf_writer = std::make_unique<MeshLib::IO::XdmfHdfWriter>( meshes, path, timestep, t, output_variables, compression, n_files, @@ -133,13 +136,14 @@ void OutputXDMFHDF5Format::outputMeshXdmf( void OutputVTKFormat::outputMeshes( const int timestep, const double t, const int iteration, + bool const converged, std::vector<std::reference_wrapper<const MeshLib::Mesh>> const& meshes, [[maybe_unused]] std::set<std::string> const& output_variables) const { for (auto const& mesh : meshes) { auto& pvd_file = findOrCreatePVDFile(mesh.get().getName()); - outputMeshVtk(*this, pvd_file, mesh, t, timestep, iteration); + outputMeshVtk(*this, pvd_file, mesh, t, timestep, iteration, converged); } } } // namespace ProcessLib diff --git a/ProcessLib/Output/OutputFormat.h b/ProcessLib/Output/OutputFormat.h index 8e1a3387064714bbeae0e6366f3d8ef68fa7e4e7..7068450d8fc96a88fcceb77c0c95b84e3ede5afb 100644 --- a/ProcessLib/Output/OutputFormat.h +++ b/ProcessLib/Output/OutputFormat.h @@ -39,12 +39,13 @@ struct OutputFormat virtual void outputMeshes( const int timestep, const double t, const int iteration, + bool const converged, std::vector<std::reference_wrapper<const MeshLib::Mesh>> const& meshes, std::set<std::string> const& output_variables) const = 0; virtual std::string constructFilename(std::string const& mesh_name, - int const timestep, - double const t, - int const iteration) const = 0; + int const timestep, double const t, + int const iteration, + bool const converged) const = 0; }; inline std::ostream& operator<<(std::ostream& os, OutputFormat const& of) @@ -69,6 +70,7 @@ struct OutputVTKFormat final : public OutputFormat void outputMeshes( const int timestep, const double t, const int iteration, + bool const converged, std::vector<std::reference_wrapper<const MeshLib::Mesh>> const& meshes, std::set<std::string> const& output_variables) const override; @@ -86,7 +88,8 @@ struct OutputVTKFormat final : public OutputFormat std::string constructFilename(std::string const& mesh_name, int const timestep, double const t, - int const iteration) const override; + int const iteration, + bool const converged) const override; std::string constructPVDName(std::string const& mesh_name) const; }; @@ -106,15 +109,18 @@ struct OutputXDMFHDF5Format final : public OutputFormat void outputMeshes( const int timestep, const double t, const int iteration, + bool const converged, std::vector<std::reference_wrapper<const MeshLib::Mesh>> const& meshes, std::set<std::string> const& output_variables) const override { - outputMeshXdmf(output_variables, meshes, timestep, t, iteration); + outputMeshXdmf(output_variables, meshes, timestep, t, iteration, + converged); } std::string constructFilename(std::string const& mesh_name, int const timestep, double const t, - int const iteration) const override; + int const iteration, + bool const converged) const override; mutable std::unique_ptr<MeshLib::IO::XdmfHdfWriter> mesh_xdmf_hdf_writer; //! Specifies the number of hdf5 output files. @@ -125,7 +131,8 @@ struct OutputXDMFHDF5Format final : public OutputFormat void outputMeshXdmf( std::set<std::string> const& output_variables, std::vector<std::reference_wrapper<const MeshLib::Mesh>> const& meshes, - int const timestep, double const t, int const iteration) const; + int const timestep, double const t, int const iteration, + bool const converged) const; }; void outputMeshVtk(std::string const& file_name, MeshLib::Mesh const& mesh, diff --git a/ProcessLib/TimeLoop.cpp b/ProcessLib/TimeLoop.cpp index 06d6e74c96cd30536c7305514211f0bec6a2094b..7bb55d5fdb289efe57d543f9c73568c665370b25 100644 --- a/ProcessLib/TimeLoop.cpp +++ b/ProcessLib/TimeLoop.cpp @@ -241,7 +241,8 @@ NumLib::NonlinearSolverStatus solveOneTimeStepOneProcess( time_disc.nextTimestep(t, delta_t); auto const post_iteration_callback = - [&](int iteration, std::vector<GlobalVector*> const& x) + [&](int const iteration, bool const converged, + std::vector<GlobalVector*> const& x) { // Note: We don't call the postNonLinearSolver(), preOutput(), // computeSecondaryVariable() and postTimestep() hooks here. This might @@ -249,7 +250,8 @@ NumLib::NonlinearSolverStatus solveOneTimeStepOneProcess( for (auto const& output : outputs) { output.doOutputNonlinearIteration(process, process_id, timestep, - NumLib::Time(t), iteration, x); + NumLib::Time(t), iteration, + converged, x); } }; @@ -667,6 +669,7 @@ NumLib::NonlinearSolverStatus TimeLoop::solveUncoupledEquationSystems( output.doOutputAlways( process_data->process, process_id, timestep_id, t, process_data->nonlinear_solver_status.number_iterations, + process_data->nonlinear_solver_status.error_norms_met, _process_solutions); } OGS_FATAL(timestepper_cannot_reduce_dt.data()); @@ -726,6 +729,7 @@ void TimeLoop::outputSolutions(unsigned timestep, const double t, (output_object.*output_class_member)( pcs, process_id, timestep, NumLib::Time(t), process_data->nonlinear_solver_status.number_iterations, + process_data->nonlinear_solver_status.error_norms_met, _process_solutions); } } diff --git a/Tests/BaseLib/TestConstructFormattedFileName.cpp b/Tests/BaseLib/TestConstructFormattedFileName.cpp index 3fe03fa24bc532da713808da2b22f897cb80c1bb..b3311cd7f4bfa57c84812f9f8958bcc68544dddf 100644 --- a/Tests/BaseLib/TestConstructFormattedFileName.cpp +++ b/Tests/BaseLib/TestConstructFormattedFileName.cpp @@ -23,56 +23,66 @@ TEST(BaseLib, constructFormattedFileName) { { auto const formatted_filename = BaseLib::constructFormattedFileName( - "test_{:timestep}", "mesh_name", 2, 0.2, 3); + "test_{:timestep}", "mesh_name", 2, 0.2, 3, true); ASSERT_EQ("test_2", formatted_filename); } { auto const formatted_filename_time = BaseLib::constructFormattedFileName("test_{:0.5time}", "mesh_name", - 2, 0.2, 3); + 2, 0.2, 3, true); ASSERT_EQ("test_0.20000", formatted_filename_time); } { auto const formatted_filename = BaseLib::constructFormattedFileName( - "test_{:iteration}", "mesh_name", 2, 0.2, 3); + "test_{:iteration}", "mesh_name", 2, 0.2, 3, true); ASSERT_EQ("test_3", formatted_filename); } { auto const formatted_filename_timestep_time = BaseLib::constructFormattedFileName("test_{:timestep}_{:0.5time}", - "mesh_name", 2, 0.2, 3); + "mesh_name", 2, 0.2, 3, true); ASSERT_EQ("test_2_0.20000", formatted_filename_timestep_time); } { auto const formatted_filename_time_timestep = BaseLib::constructFormattedFileName("test_{:.4time}_{:timestep}", - "mesh_name", 2, 0.2, 3); + "mesh_name", 2, 0.2, 3, true); ASSERT_EQ("test_0.2000_2", formatted_filename_time_timestep); } { auto const formatted_filename = BaseLib::constructFormattedFileName( - "test_{:.4time}_{:timestep}", "mesh_name", 2, 0.2, 3); + "test_{:.4time}_{:timestep}", "mesh_name", 2, 0.2, 3, true); ASSERT_EQ("test_0.2000_2", formatted_filename); } { auto const formatted_filename = BaseLib::constructFormattedFileName( - "_ts_{:timestep}_t_{:.4time}", "mesh_name", 2, 0.2, 3); + "_ts_{:timestep}_t_{:.4time}", "mesh_name", 2, 0.2, 3, true); ASSERT_EQ("_ts_2_t_0.2000", formatted_filename); } { auto const formatted_filename = BaseLib::constructFormattedFileName( - "_ts_{:0>3timestep}_t_{:.4time}", "mesh_name", 2, 0.2, 3); + "_ts_{:0>3timestep}_t_{:.4time}", "mesh_name", 2, 0.2, 3, true); ASSERT_EQ("_ts_002_t_0.2000", formatted_filename); } { auto const formatted_filename = BaseLib::constructFormattedFileName( - "_ts_{:0>3timestep}_t_{:.4etime}", "mesh_name", 2, 0.2, 3); + "_ts_{:0>3timestep}_t_{:.4etime}", "mesh_name", 2, 0.2, 3, true); ASSERT_EQ("_ts_002_t_2.0000e-01", formatted_filename); } { auto const formatted_filename = BaseLib::constructFormattedFileName( "{:meshname}_ts_{:0>3timestep}_t_{:.4etime}_iter_{:0>2iteration}", - "mesh_name", 2, 0.2, 3); + "mesh_name", 2, 0.2, 3, true); ASSERT_EQ("mesh_name_ts_002_t_2.0000e-01_iter_03", formatted_filename); } + { + auto const formatted_filename = BaseLib::constructFormattedFileName( + "{:meshname}{:converged}", "mesh_name", 2, 0.2, 3, true); + ASSERT_EQ("mesh_name", formatted_filename); + } + { + auto const formatted_filename = BaseLib::constructFormattedFileName( + "{:meshname}{:converged}", "mesh_name", 2, 0.2, 3, false); + ASSERT_EQ("mesh_name_not_converged", formatted_filename); + } }