diff --git a/CMakeLists.txt b/CMakeLists.txt index d7c7318458823c05c9f385c1b5c50118534f6a76..93eb3fa005eda92b4884d61cfa8963fc237b5354 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -706,6 +706,12 @@ else() message(" gnuplot: not found!") endif() +if (GMSH) + message(" gmsh: ${GMSH}") +else() + message(" gmsh: not found!") +endif() + if (PYGMENTIZE) message(" pygmentize: ${PYGMENTIZE}") else() @@ -718,7 +724,7 @@ else() message(" pdflatex: not found!") endif() -if (NOT EMACS OR NOT GNUPLOT_FOUND) +if (NOT EMACS OR NOT GNUPLOT_FOUND OR NOT GMSH) message(" ** Cannot build documentation: missing ") elseif(NOT LATEX_PDFLATEX_FOUND OR NOT PYGMENTIZE) message(" ** Cannot build pdf documentation: missing") @@ -729,6 +735,9 @@ endif() if (NOT GNUPLOT_FOUND) message(" - gnuplot") endif() +if (NOT GMSH) + message(" - gmsh") +endif() if (NOT LATEX_PDFLATEX_FOUND) message(" - pdflatex") endif() diff --git a/README.md b/README.md index 62d6b43773cb0164a29e8a3716a818c5ec219c7d..36e460a851a53d40b4ab442460594091fa22b20f 100644 --- a/README.md +++ b/README.md @@ -86,7 +86,7 @@ apt install slepc-dev ### User documentation -To build documentation one requires `emacs` and `gnuplot`, +To build documentation one requires `emacs`, `gmsh` and `gnuplot`, additionally since examples results are generated, the documentation can only be produced after the compilation of `pugs` itself. @@ -94,6 +94,10 @@ To install `emacs` on Debian-like systems ```shell apt install emacs ``` +To install `gmsh` on Debian-like systems +```shell +apt install gmsh +``` To install `gnuplot` one can either use ```shell apt install gnuplot-nox @@ -106,6 +110,7 @@ apt install gnuplot-x11 > When building the documentation for the first time, a local `emacs` > configuration is generated. *This requires an internet connection.* + These packages are enough to build the html documentation. To build the pdf documentation one requires a few more packages: `pdflatex` (actually a fresh texlive installation is probably necessary) and `pygmentize` diff --git a/cmake/PugsDoc.cmake b/cmake/PugsDoc.cmake index 1f8701ccc20f6416b529908f3893d971e86c7452..b067a5cc828ec0c245e8d51c0bda46a96dcce702 100644 --- a/cmake/PugsDoc.cmake +++ b/cmake/PugsDoc.cmake @@ -12,10 +12,13 @@ find_program(PYGMENTIZE pygmentize) # check for gnuplot find_package(Gnuplot) +# check for gmsh +find_program(GMSH NAMES gmsh) + add_custom_target(userdoc) add_custom_target(doc DEPENDS userdoc) -if (EMACS AND GNUPLOT_FOUND) +if (EMACS AND GNUPLOT_FOUND AND GMSH) add_custom_command( OUTPUT "${PUGS_BINARY_DIR}/doc" @@ -129,6 +132,13 @@ else() COMMAND ${CMAKE_COMMAND} -E cmake_echo_color --red --bold "gnuplot missing") add_dependencies(userdoc userdoc-missing-gnuplot) endif() + + if (NOT GMSH) + add_custom_target(userdoc-missing-gmsh + COMMAND ${CMAKE_COMMAND} -E cmake_echo_color --no-newline "Cannot build documentation: " + COMMAND ${CMAKE_COMMAND} -E cmake_echo_color --red --bold "gmsh missing") + add_dependencies(userdoc userdoc-missing-gmsh) + endif() endif() add_dependencies(doc userdoc) diff --git a/src/language/utils/OFStream.cpp b/src/language/utils/OFStream.cpp index 237305c0a1fc40c2ade92925ddad3acc76c9ce3c..497dac421a6773ff769f94bd7ef84617f6701290 100644 --- a/src/language/utils/OFStream.cpp +++ b/src/language/utils/OFStream.cpp @@ -1,10 +1,12 @@ #include <language/utils/OFStream.hpp> +#include <utils/Filesystem.hpp> #include <utils/Messenger.hpp> OFStream::OFStream(const std::string& filename) { if (parallel::rank() == 0) { + createDirectoryIfNeeded(filename); m_fstream.open(filename); if (m_fstream.is_open()) { m_ostream = &m_fstream; diff --git a/src/output/GnuplotWriter.cpp b/src/output/GnuplotWriter.cpp index 50969799eeb4250b28d21722e8476ab86a88d9eb..8ccf7767180b30524ba54f7c4f085c3ad35783d2 100644 --- a/src/output/GnuplotWriter.cpp +++ b/src/output/GnuplotWriter.cpp @@ -5,6 +5,7 @@ #include <mesh/Mesh.hpp> #include <mesh/MeshData.hpp> #include <mesh/MeshDataManager.hpp> +#include <utils/Filesystem.hpp> #include <utils/Messenger.hpp> #include <utils/PugsTraits.hpp> #include <utils/RevisionInfo.hpp> @@ -243,8 +244,16 @@ GnuplotWriter::_write(const MeshType& mesh, const OutputNamedItemDataSet& output_named_item_data_set, std::optional<double> time) const { + createDirectoryIfNeeded(_getFilename()); + if (parallel::rank() == 0) { std::ofstream fout{_getFilename()}; + if (not fout) { + std::ostringstream error_msg; + error_msg << "cannot create file \"" << rang::fgB::yellow << _getFilename() << rang::fg::reset << '"'; + throw NormalError(error_msg.str()); + } + fout.precision(15); fout.setf(std::ios_base::scientific); @@ -286,6 +295,12 @@ GnuplotWriter::_write(const MeshType& mesh, for (size_t i_rank = 0; i_rank < parallel::size(); ++i_rank) { if (i_rank == parallel::rank()) { std::ofstream fout(_getFilename(), std::ios_base::app); + if (not fout) { + std::ostringstream error_msg; + error_msg << "cannot open file \"" << rang::fgB::yellow << _getFilename() << rang::fg::reset << '"'; + throw NormalError(error_msg.str()); + } + fout.precision(15); fout.setf(std::ios_base::scientific); diff --git a/src/output/GnuplotWriter1D.cpp b/src/output/GnuplotWriter1D.cpp index a3156904843bdeb77be718de6a10614a3333afae..3e0740446e961505eb01e36eee5ba168e0f47dcc 100644 --- a/src/output/GnuplotWriter1D.cpp +++ b/src/output/GnuplotWriter1D.cpp @@ -5,6 +5,7 @@ #include <mesh/Mesh.hpp> #include <mesh/MeshData.hpp> #include <mesh/MeshDataManager.hpp> +#include <utils/Filesystem.hpp> #include <utils/Messenger.hpp> #include <utils/PugsTraits.hpp> #include <utils/RevisionInfo.hpp> @@ -322,6 +323,12 @@ GnuplotWriter1D::_write(const MeshType& mesh, if (parallel::rank() == 0) { fout.open(_getFilename()); + if (not fout) { + std::ostringstream error_msg; + error_msg << "cannot create file \"" << rang::fgB::yellow << _getFilename() << rang::fg::reset << '"'; + throw NormalError(error_msg.str()); + } + fout.precision(15); fout.setf(std::ios_base::scientific); fout << _getDateAndVersionComment(); diff --git a/src/output/VTKWriter.cpp b/src/output/VTKWriter.cpp index 0a40c7265e05a17d31e59bd41eeb10cb63935821..6ec6295e44b54bef9ea21c110120fdd873f71488 100644 --- a/src/output/VTKWriter.cpp +++ b/src/output/VTKWriter.cpp @@ -4,6 +4,7 @@ #include <mesh/Mesh.hpp> #include <mesh/MeshData.hpp> #include <mesh/MeshDataManager.hpp> +#include <utils/Filesystem.hpp> #include <utils/Messenger.hpp> #include <utils/RevisionInfo.hpp> #include <utils/Stringify.hpp> @@ -364,8 +365,16 @@ VTKWriter::_write(const MeshType& mesh, output_named_item_data_set.add(NamedItemData{"cell_number", mesh.connectivity().cellNumber()}); output_named_item_data_set.add(NamedItemData{"node_number", mesh.connectivity().nodeNumber()}); + createDirectoryIfNeeded(m_base_filename); + if (parallel::rank() == 0) { // write PVTK file std::ofstream fout(_getFilenamePVTU()); + if (not fout) { + std::ostringstream error_msg; + error_msg << "cannot create file \"" << rang::fgB::yellow << _getFilenamePVTU() << rang::fg::reset << '"'; + throw NormalError(error_msg.str()); + } + fout << "<?xml version=\"1.0\"?>\n"; fout << _getDateAndVersionComment(); fout << "<VTKFile type=\"PUnstructuredGrid\">\n"; @@ -424,7 +433,7 @@ VTKWriter::_write(const MeshType& mesh, fout << "</PCellData>\n"; for (size_t i_rank = 0; i_rank < parallel::size(); ++i_rank) { - fout << "<Piece Source=\"" << _getFilenameVTU(i_rank) << "\"/>\n"; + fout << "<Piece Source=" << std::filesystem::path{_getFilenameVTU(i_rank)}.filename() << "/>\n"; } fout << "</PUnstructuredGrid>\n"; fout << "</VTKFile>\n"; @@ -435,6 +444,13 @@ VTKWriter::_write(const MeshType& mesh, // write VTK files std::ofstream fout(_getFilenameVTU(parallel::rank())); + if (not fout) { + std::ostringstream error_msg; + error_msg << "cannot create file \"" << rang::fgB::yellow << _getFilenameVTU(parallel::rank()) << rang::fg::reset + << '"'; + throw NormalError(error_msg.str()); + } + fout << "<?xml version=\"1.0\"?>\n"; fout << _getDateAndVersionComment(); fout << "<VTKFile type=\"UnstructuredGrid\""; @@ -632,7 +648,13 @@ VTKWriter::_write(const MeshType& mesh, } if (parallel::rank() == 0) { // write PVD file - std::ofstream fout(m_base_filename + ".pvd"); + const std::string pvd_filename = m_base_filename + ".pvd"; + std::ofstream fout(pvd_filename); + if (not fout) { + std::ostringstream error_msg; + error_msg << "cannot create file \"" << rang::fgB::yellow << pvd_filename << rang::fg::reset << '"'; + throw NormalError(error_msg.str()); + } fout << "<?xml version=\"1.0\"?>\n"; fout << _getDateAndVersionComment(); @@ -645,11 +667,13 @@ VTKWriter::_write(const MeshType& mesh, sout << m_base_filename; sout << '.' << std::setfill('0') << std::setw(4) << i_time << ".pvtu"; - fout << "<DataSet timestep=\"" << m_period_manager->savedTime(i_time) << "\" file=\"" << sout.str() << "\"/>\n"; + fout << "<DataSet timestep=\"" << m_period_manager->savedTime(i_time) + << "\" file=" << std::filesystem::path{sout.str()}.filename() << "/>\n"; } - fout << "<DataSet timestep=\"" << *time << "\" file=\"" << _getFilenamePVTU() << "\"/>\n"; + fout << "<DataSet timestep=\"" << *time << "\" file=" << std::filesystem::path{_getFilenamePVTU()}.filename() + << "/>\n"; } else { - fout << "<DataSet file=\"" << _getFilenamePVTU() << "\"/>\n"; + fout << "<DataSet file=" << std::filesystem::path{_getFilenamePVTU()}.filename() << "/>\n"; } fout << "</Collection>\n"; diff --git a/src/utils/Filesystem.hpp b/src/utils/Filesystem.hpp new file mode 100644 index 0000000000000000000000000000000000000000..63af52b2e432f1baf55d2cbfd895485a408f7e10 --- /dev/null +++ b/src/utils/Filesystem.hpp @@ -0,0 +1,23 @@ +#ifndef FILESYSTEM_HPP +#define FILESYSTEM_HPP + +#include <utils/Exceptions.hpp> +#include <utils/PugsMacros.hpp> + +#include <filesystem> + +PUGS_INLINE void +createDirectoryIfNeeded(const std::string& filename) +{ + std::filesystem::path path = std::filesystem::path{filename}.parent_path(); + if (not path.empty()) { + try { + std::filesystem::create_directories(path); + } + catch (std::filesystem::filesystem_error& e) { + throw NormalError(e.what()); + } + } +} + +#endif // FILESYSTEM_HPP diff --git a/tests/test_OFStream.cpp b/tests/test_OFStream.cpp index fbb5818dbc70451ff212c820edacfa595963e961..6ca7d98df0a6f36bf30add2b891a659beba757c2 100644 --- a/tests/test_OFStream.cpp +++ b/tests/test_OFStream.cpp @@ -13,7 +13,8 @@ TEST_CASE("OFStream", "[language]") { SECTION("ofstream") { - const std::string basename = std::filesystem::path{PUGS_BINARY_DIR}.append("tests").append("ofstream_"); + const std::string basename = + std::filesystem::path{PUGS_BINARY_DIR}.append("tests").append("ofstream_dir").append("ofstream_"); const std::string filename = basename + stringify(parallel::rank()); // Ensures that the file is closed after this line @@ -47,13 +48,4 @@ TEST_CASE("OFStream", "[language]") REQUIRE(not std::filesystem::exists(filename)); } - - SECTION("invalid filename") - { - if (parallel::rank() == 0) { - const std::string filename = "badpath/invalidpath/ofstream"; - - REQUIRE_THROWS_WITH(std::make_shared<OFStream>(filename), "error: cannot create file " + filename); - } - } }