#include <output/GnuplotWriter.hpp> #include <mesh/Connectivity.hpp> #include <mesh/ItemValue.hpp> #include <mesh/Mesh.hpp> #include <mesh/MeshData.hpp> #include <mesh/MeshDataManager.hpp> #include <utils/Messenger.hpp> #include <utils/PugsTraits.hpp> #include <utils/RevisionInfo.hpp> #include <utils/Demangle.hpp> #include <fstream> #include <iomanip> std::string GnuplotWriter::_getDateAndVersionComment() const { std::ostringstream os; std::time_t now = std::time(nullptr); os << "# Generated by pugs: " << std::ctime(&now); os << "# version: " << RevisionInfo::version() << '\n'; os << "# tag: " << RevisionInfo::gitTag() << '\n'; os << "# HEAD: " << RevisionInfo::gitHead() << '\n'; os << "# hash: " << RevisionInfo::gitHash() << " (" << ((RevisionInfo::gitIsClean()) ? "clean" : "dirty") << ")\n"; os << '\n'; return os.str(); } std::string GnuplotWriter::_getFilename() const { std::ostringstream sout; sout << m_base_filename; sout << '.' << std::setfill('0') << std::setw(4) << m_saved_times.size() << ".gnu"; return sout.str(); } template <size_t Dimension> void GnuplotWriter::_writePreamble(const OutputNamedItemValueSet& output_named_item_value_set, std::ostream& fout) const { fout << "# list of data\n"; fout << "# 1:x"; if constexpr (Dimension > 1) { fout << " 2:y"; } uint64_t i = Dimension + 1; for (const auto& i_named_item_value : output_named_item_value_set) { const std::string name = i_named_item_value.first; const auto& item_value_variant = i_named_item_value.second; std::visit( [&](auto&& item_value) { using ItemValueType = std::decay_t<decltype(item_value)>; using DataType = std::decay_t<typename ItemValueType::data_type>; if constexpr (std::is_arithmetic_v<DataType>) { fout << ' ' << i++ << ':' << name; } else if constexpr (is_tiny_vector_v<DataType>) { for (size_t j = 0; j < DataType{}.dimension(); ++j) { fout << ' ' << i++ << ':' << name << '[' << j << ']'; } } else if constexpr (is_tiny_matrix_v<DataType>) { for (size_t j = 0; j < DataType{}.nbRows(); ++j) { for (size_t k = 0; k < DataType{}.nbColumns(); ++k) { fout << ' ' << i++ << ':' << name << '(' << j << ',' << k << ')'; } } } else { throw UnexpectedError("invalid data type"); } }, item_value_variant); } fout << "\n\n"; } template <typename DataType> void GnuplotWriter::_writeCellValue(const CellValue<DataType>& cell_value, CellId cell_id, std::ostream& fout) const { const auto& value = cell_value[cell_id]; if constexpr (std::is_arithmetic_v<DataType>) { fout << ' ' << value; } else if constexpr (is_tiny_vector_v<std::decay_t<DataType>>) { for (size_t i = 0; i < value.dimension(); ++i) { fout << ' ' << value[i]; } } else if constexpr (is_tiny_matrix_v<std::decay_t<DataType>>) { for (size_t i = 0; i < value.nbRows(); ++i) { for (size_t j = 0; j < value.nbColumns(); ++j) { fout << ' ' << value(i, j); } } } else { throw UnexpectedError("invalid data type for cell value output: " + demangle<DataType>()); } } template <typename DataType, ItemType item_type> void GnuplotWriter::_writeValue(const ItemValue<DataType, item_type>& item_value, [[maybe_unused]] CellId cell_id, [[maybe_unused]] NodeId node_id, std::ostream& fout) const { if constexpr (item_type == ItemType::cell) { this->_writeCellValue(item_value, cell_id, fout); } else if constexpr (item_type == ItemType::node) { this->_writeNodeValue(item_value, node_id, fout); } else { throw UnexpectedError{"invalid item type"}; } } template <typename MeshType> void GnuplotWriter::_writeValues(const std::shared_ptr<const MeshType>& mesh, const OutputNamedItemValueSet& output_named_item_value_set, std::ostream& fout) const { if constexpr (MeshType::Dimension == 1) { auto cell_to_node_matrix = mesh->connectivity().cellToNodeMatrix(); auto cell_is_owned = mesh->connectivity().cellIsOwned(); for (CellId cell_id = 0; cell_id < mesh->numberOfCells(); ++cell_id) { if (cell_is_owned[cell_id]) { const auto& cell_nodes = cell_to_node_matrix[cell_id]; for (size_t i_node = 0; i_node < cell_nodes.size(); ++i_node) { const NodeId& node_id = cell_nodes[i_node]; const TinyVector<MeshType::Dimension>& xr = mesh->xr()[node_id]; fout << xr[0]; for (auto [name, item_value] : output_named_item_value_set) { std::visit([&](auto&& cell_value) { _writeValue(cell_value, cell_id, node_id, fout); }, item_value); } fout << '\n'; } fout << "\n\n"; } } } else if constexpr (MeshType::Dimension == 2) { auto cell_to_node_matrix = mesh->connectivity().cellToNodeMatrix(); auto cell_is_owned = mesh->connectivity().cellIsOwned(); for (CellId cell_id = 0; cell_id < mesh->numberOfCells(); ++cell_id) { if (cell_is_owned[cell_id]) { const auto& cell_nodes = cell_to_node_matrix[cell_id]; for (size_t i_node = 0; i_node < cell_nodes.size(); ++i_node) { const NodeId& node_id = cell_nodes[i_node]; const TinyVector<MeshType::Dimension>& xr = mesh->xr()[node_id]; fout << xr[0] << ' ' << xr[1]; for (auto [name, item_value] : output_named_item_value_set) { std::visit([&](auto&& cell_value) { _writeValue(cell_value, cell_id, node_id, fout); }, item_value); } fout << '\n'; } const NodeId& node_id = cell_nodes[0]; const TinyVector<MeshType::Dimension>& xr = mesh->xr()[node_id]; fout << xr[0] << ' ' << xr[1]; for (auto [name, item_value] : output_named_item_value_set) { std::visit([&](auto&& cell_value) { _writeValue(cell_value, cell_id, node_id, fout); }, item_value); } fout << "\n\n\n"; } } } else { throw UnexpectedError("invalid mesh dimension"); } } template <typename ValueType> void GnuplotWriter::_writeNodeValue(const NodeValue<ValueType>& node_value, NodeId node_id, std::ostream& fout) const { const auto& value = node_value[node_id]; if constexpr (std::is_arithmetic_v<ValueType>) { fout << ' ' << value; } else if constexpr (is_tiny_vector_v<std::decay_t<ValueType>>) { for (size_t i = 0; i < value.dimension(); ++i) { fout << ' ' << value[i]; } } else if constexpr (is_tiny_matrix_v<std::decay_t<ValueType>>) { for (size_t i = 0; i < value.nbRows(); ++i) { for (size_t j = 0; j < value.nbColumns(); ++j) { fout << ' ' << value(i, j); } } } else { throw UnexpectedError("invalid data type for cell value output: " + demangle<ValueType>()); } } template <typename MeshType> void GnuplotWriter::_write(const std::shared_ptr<const MeshType>& mesh, const OutputNamedItemValueSet& output_named_item_value_set, double time) const { if (parallel::rank() == 0) { std::ofstream fout{_getFilename()}; fout.precision(15); fout.setf(std::ios_base::scientific); fout << this->_getDateAndVersionComment(); fout << "# time = " << time << "\n\n"; this->_writePreamble<MeshType::Dimension>(output_named_item_value_set, fout); } 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); fout.precision(15); fout.setf(std::ios_base::scientific); this->_writeValues(mesh, output_named_item_value_set, fout); } parallel::barrier(); } } void GnuplotWriter::writeMesh(const std::shared_ptr<const IMesh>& p_mesh) const { OutputNamedItemValueSet output_named_item_value_set{}; switch (p_mesh->dimension()) { case 1: { this->_write(std::dynamic_pointer_cast<const Mesh<Connectivity<1>>>(p_mesh), output_named_item_value_set, 0); break; } case 2: { this->_write(std::dynamic_pointer_cast<const Mesh<Connectivity<2>>>(p_mesh), output_named_item_value_set, 0); break; } default: { throw NormalError("gnuplot format is not available in dimension " + std::to_string(p_mesh->dimension())); } } } void GnuplotWriter::write(const std::vector<std::shared_ptr<const NamedDiscreteFunction>>& named_discrete_function_list, double time) const { std::shared_ptr mesh = this->_getMesh(named_discrete_function_list); OutputNamedItemValueSet output_named_item_value_set = this->_getOutputNamedItemValueSet(named_discrete_function_list); switch (mesh->dimension()) { case 1: { this->_write(std::dynamic_pointer_cast<const Mesh<Connectivity<1>>>(mesh), output_named_item_value_set, time); break; } case 2: { this->_write(std::dynamic_pointer_cast<const Mesh<Connectivity<2>>>(mesh), output_named_item_value_set, time); break; } default: { throw NormalError("gnuplot format is not available in dimension " + std::to_string(mesh->dimension())); } } }