stroid can now generate non uniformly refined vacuum meshes. Note we still enforce that the stellar domain is fully conforming.
261 lines
10 KiB
C++
261 lines
10 KiB
C++
#include <iostream>
|
|
#include <fstream>
|
|
#include <string>
|
|
#include <map>
|
|
#include <memory>
|
|
#include <optional>
|
|
#include <ranges>
|
|
#include <algorithm>
|
|
#include <print>
|
|
#include <limits>
|
|
|
|
// ReSharper disable once CppUnusedIncludeDirective
|
|
#include "mfem.hpp"
|
|
#include "stroid/stroid.h"
|
|
#include "fourdst/config/config.h"
|
|
#include "CLI/CLI.hpp"
|
|
#include "magic_enum/magic_enum.hpp"
|
|
|
|
bool validate_filename_extension(const std::string& filename, const std::string& expected_ext) {
|
|
const auto pos = filename.rfind('.');
|
|
if (pos == std::string::npos) return false;
|
|
return filename.substr(pos + 1) == expected_ext;
|
|
}
|
|
|
|
enum class MESH_FORMATS : int {
|
|
VTK,
|
|
VTU,
|
|
MFEM,
|
|
NETGEN,
|
|
INFO,
|
|
PARAVIEW
|
|
};
|
|
|
|
struct OUTPUT_CONFIG {
|
|
struct VTK_SETTINGS {
|
|
int ref = 0;
|
|
int field_data = 0;
|
|
} vtk;
|
|
|
|
struct VTU_SETTINGS {
|
|
int ref = 1;
|
|
mfem::VTKFormat format = mfem::VTKFormat::ASCII;
|
|
bool high_order_output = true;
|
|
int compression_level = 0;
|
|
bool bdr_elements = false;
|
|
} vtu;
|
|
|
|
struct MFEM_SETTINGS {
|
|
std::string comments = "Generated by stroid";
|
|
} mfem;
|
|
};
|
|
|
|
int main(int argc, char** argv) {
|
|
fourdst::config::Config<stroid::config::MeshConfig> cfg;
|
|
OUTPUT_CONFIG out_cfg;
|
|
auto selected_format = MESH_FORMATS::MFEM;
|
|
|
|
CLI::App app{"stroid - A tool for generating multi-block meshes for stellar modeling"};
|
|
|
|
app.footer(
|
|
"\nEXAMPLES:\n"
|
|
"| stroid generate -c config.toml -o star_mesh.vtu vtu\n"
|
|
"| stroid generate --config config.toml --view --no-save\n"
|
|
"| stroid info --version\n"
|
|
"| stroid info --default\n"
|
|
);
|
|
|
|
auto* generate = app.add_subcommand("generate", "Generate a multi-block mesh");
|
|
auto* info = app.add_subcommand("info", "Access information about stroid");
|
|
auto* view = app.add_subcommand("view", "Display a mesh with glvis");
|
|
|
|
std::optional<std::string> config_filename;
|
|
std::optional<std::string> mesh_file;
|
|
std::string output_filename = "stroid.mesh";
|
|
bool view_mesh = false;
|
|
bool original_elements = false;
|
|
bool no_save = false;
|
|
std::string glvis_host = "localhost";
|
|
int glvis_port = 19916;
|
|
|
|
generate->add_option("-c,--config", config_filename, "Path to configuration file")->check(CLI::ExistingFile);
|
|
generate->add_flag("-v,--view", view_mesh, "View the generated mesh using GLVis");
|
|
generate->add_flag("--original-elements", original_elements, "Display original cells; curved hanging faces may show GLVis tessellation gaps");
|
|
generate->add_flag("-n,--no-save", no_save, "Do not save the generated mesh to a file");
|
|
generate->add_option("--glvis-host", glvis_host, "GLVis server host")->capture_default_str();
|
|
generate->add_option("--glvis-port", glvis_port, "GLVis server port")->capture_default_str();
|
|
generate->add_option("-o,--output", output_filename, "Output filename base")->capture_default_str();
|
|
|
|
view->add_option("--host", glvis_host, "GLVis server host")->capture_default_str();
|
|
view->add_option("--port", glvis_port, "GLVis server port")->capture_default_str();
|
|
view->add_flag("--original-elements", original_elements, "Display original cells; curved hanging faces may show GLVis tessellation gaps");
|
|
view->add_option("-f,--file", mesh_file, "Path to .mesh file")->check(CLI::ExistingFile);
|
|
|
|
auto to_lower = [](std::string s) {
|
|
std::string out;
|
|
out.reserve(s.size());
|
|
std::ranges::transform(s, std::back_inserter(out), [](unsigned char c) {
|
|
return static_cast<char>(std::tolower(c));
|
|
});
|
|
return out;
|
|
};
|
|
|
|
std::map<std::string, stroid::IO::VISUALIZATION_MODE> mode_map;
|
|
for (auto [value, name] : magic_enum::enum_entries<stroid::IO::VISUALIZATION_MODE>()) {
|
|
mode_map[to_lower(std::string(name))] = value;
|
|
}
|
|
|
|
stroid::IO::VISUALIZATION_MODE selected_mode;
|
|
|
|
view->add_option("-v,--vis-mode", selected_mode, "Select Visualization mode")
|
|
->transform(CLI::CheckedTransformer(mode_map, CLI::ignore_case))
|
|
->default_val(stroid::IO::VISUALIZATION_MODE::ELEMENT_ID);
|
|
view->add_flag_callback("-l,--list", [&]() {
|
|
std::println("Available Visualization Modes:");
|
|
for (const auto &name: mode_map | std::views::keys) {
|
|
std::println("\t - {}", name);
|
|
}
|
|
exit(0);
|
|
});
|
|
|
|
for (auto [value, name_view] : magic_enum::enum_entries<MESH_FORMATS>()) {
|
|
std::string name{name_view};
|
|
std::ranges::transform(name, name.begin(), ::tolower);
|
|
|
|
auto* sub = generate->add_subcommand(name, "Output as " + name);
|
|
|
|
switch (value) {
|
|
case MESH_FORMATS::VTK:
|
|
sub->add_option("--ref", out_cfg.vtk.ref, "Refinement level");
|
|
sub->add_option("--field-data", out_cfg.vtk.field_data, "Field data flag");
|
|
break;
|
|
case MESH_FORMATS::VTU:
|
|
sub->add_option("--ref", out_cfg.vtu.ref, "Refinement level");
|
|
sub->add_option("--compression", out_cfg.vtu.compression_level, "Compression level (0-9)");
|
|
sub->add_flag("--high-order", out_cfg.vtu.high_order_output, "High order output");
|
|
break;
|
|
case MESH_FORMATS::MFEM:
|
|
sub->add_option("--comments", out_cfg.mfem.comments, "Header comments");
|
|
break;
|
|
default: break;
|
|
}
|
|
|
|
sub->callback([&selected_format, value]() {
|
|
selected_format = value;
|
|
});
|
|
}
|
|
|
|
info->add_flag_callback("-v,--version", []() {
|
|
std::println("Stroid Version {}", stroid::version::toString());
|
|
exit(0);
|
|
}, "Display stroid version information");
|
|
|
|
info->add_flag_callback("-d,--default", [&cfg]() {
|
|
cfg.save("default.toml");
|
|
std::println("Default configuration saved to default.toml");
|
|
exit(0);
|
|
}, "Save the default configuration to default.toml");
|
|
|
|
try {
|
|
app.parse(argc, argv);
|
|
} catch (const CLI::ParseError &e) {
|
|
return app.exit(e);
|
|
}
|
|
|
|
if (*view) {
|
|
if (!mesh_file.has_value()) {
|
|
throw std::runtime_error("Mesh file must be specified");
|
|
}
|
|
mfem::Mesh mesh(mesh_file.value().c_str());
|
|
stroid::IO::ViewMesh(mesh,
|
|
"Mesh Viewer - Colored by Element ID",
|
|
selected_mode,
|
|
glvis_host,
|
|
glvis_port,
|
|
!original_elements);
|
|
exit(0);
|
|
|
|
}
|
|
|
|
if (*generate) {
|
|
if (config_filename.has_value()) {
|
|
cfg.load(config_filename.value());
|
|
}
|
|
|
|
|
|
auto generated = stroid::GenerateMesh(cfg);
|
|
mfem::Mesh* mesh = generated.mesh.get();
|
|
|
|
if (!no_save) {
|
|
const std::string& final_path = output_filename;
|
|
|
|
switch (selected_format) {
|
|
case MESH_FORMATS::MFEM: {
|
|
if (!validate_filename_extension(final_path, "mesh")) {
|
|
std::cerr << "WARNING! Saving to MFEM format without the standard '.mesh' extension. File will be called " << final_path << std::endl;
|
|
}
|
|
std::ofstream ofs(final_path);
|
|
ofs.precision(std::numeric_limits<double>::max_digits10);
|
|
mesh->Print(ofs);
|
|
break;
|
|
}
|
|
case MESH_FORMATS::VTU: {
|
|
if (!validate_filename_extension(final_path, "vtu")) {
|
|
std::cerr << "WARNING! Saving to VTU format without the standard '.vtu' extension. File will be called " << final_path << std::endl;
|
|
}
|
|
std::ofstream ofs(final_path);
|
|
ofs.precision(std::numeric_limits<double>::max_digits10);
|
|
// MFEM's stream overload writes an open Piece, allowing callers
|
|
// to append fields. Supply the enclosing document for a mesh export.
|
|
ofs << "<VTKFile type=\"UnstructuredGrid\" version=\"0.1\"";
|
|
if (out_cfg.vtu.compression_level != 0) ofs << " compressor=\"vtkZLibDataCompressor\"";
|
|
ofs << " byte_order=\"" << mfem::VTKByteOrder() << "\">\n<UnstructuredGrid>\n";
|
|
mesh->PrintVTU(ofs,
|
|
out_cfg.vtu.ref,
|
|
out_cfg.vtu.format,
|
|
out_cfg.vtu.high_order_output,
|
|
out_cfg.vtu.compression_level,
|
|
out_cfg.vtu.bdr_elements);
|
|
ofs << "</Piece>\n</UnstructuredGrid>\n</VTKFile>\n";
|
|
break;
|
|
}
|
|
case MESH_FORMATS::VTK: {
|
|
if (!validate_filename_extension(final_path, "vtk")) {
|
|
std::cerr << "WARNING! Saving to VTK format without the standard '.vtk' extension. File will be called " << final_path << std::endl;
|
|
}
|
|
std::ofstream ofs(final_path);
|
|
mesh->PrintVTK(ofs, out_cfg.vtk.ref, out_cfg.vtk.field_data);
|
|
break;
|
|
}
|
|
case MESH_FORMATS::NETGEN: {
|
|
if (!validate_filename_extension(final_path, "nmesh")) {
|
|
std::cerr << "WARNING! Saving to Netgen format without the standard '.nmesh' extension. File will be called " << final_path << std::endl;
|
|
}
|
|
std::ofstream ofs(final_path);
|
|
mesh->PrintXG(ofs);
|
|
break;
|
|
}
|
|
case MESH_FORMATS::INFO: {
|
|
mesh->PrintCharacteristics();
|
|
}
|
|
case MESH_FORMATS::PARAVIEW: {
|
|
stroid::IO::SaveVTU(*mesh, final_path);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (view_mesh) {
|
|
stroid::IO::ViewMesh(*mesh,
|
|
"Spheroidal Mesh - Colored by Element ID",
|
|
stroid::IO::VISUALIZATION_MODE::ELEMENT_ID,
|
|
glvis_host,
|
|
glvis_port,
|
|
!original_elements);
|
|
}
|
|
} else if (!*info) {
|
|
std::println("Usage: {} [generate|info|view] --help", argv[0]);
|
|
}
|
|
|
|
return 0;
|
|
}
|