feat(stroid): added command line and tests

This commit is contained in:
2026-01-30 13:21:43 -05:00
parent 58f59516ec
commit ce69b91fde
30 changed files with 975 additions and 46 deletions

View File

@@ -3,14 +3,42 @@
#include "mfem.hpp"
namespace stroid::IO {
/**
* @brief Visualization modes for GLVis display.
*/
enum class VISUALIZATION_MODE : uint8_t {
/** @brief No attribute visualization (default rendering). */
NONE,
/** @brief Color elements by their element attribute/ID. */
ELEMENT_ID,
/** @brief Color boundary-adjacent elements by boundary attribute/ID. */
BOUNDARY_ELEMENT_ID
};
/**
* @brief Save a mesh to MFEM's native `.mesh` format.
* @param mesh Mesh to serialize.
* @param filename Output path (including extension).
*/
void SaveMesh(const mfem::Mesh& mesh, const std::string& filename);
/**
* @brief Save a mesh as a ParaView VTU dataset.
* @param mesh Mesh to export.
* @param exportName Output base name (ParaView will add extensions).
*/
void SaveVTU(mfem::Mesh& mesh, const std::string& exportName);
void ViewMesh(mfem::Mesh &mesh, const std::string& title, VISUALIZATION_MODE mode);
/**
* @brief Stream a mesh to a running GLVis server for interactive viewing.
* @param mesh Mesh to display.
* @param title Window title shown in GLVis.
* @param mode Attribute visualization mode.
* @param vishost GLVis server host.
* @param visport GLVis server port.
*/
void ViewMesh(mfem::Mesh &mesh, const std::string& title, VISUALIZATION_MODE mode, const std::string &vishost, int visport);
/**
* @brief Visualize boundary face valence (1=surface, 2=internal).
* @param mesh Mesh whose boundary faces are inspected.
*/
void VisualizeFaceValence(mfem::Mesh& mesh);
}

View File

@@ -1,18 +1,63 @@
#pragma once
namespace stroid::config {
/**
* @brief Configuration parameters for stroid mesh generation.
*
* These values are typically loaded via
* `fourdst::config::Config<stroid::config::MeshConfig>` from a TOML file.
* The README shows the expected TOML layout under the `[main]` table.
* Unspecified keys use the defaults defined here.
*/
struct MeshConfig {
int refinement_levels = 3;
int order = 2;
/**
* @brief Number of uniform refinement passes applied after topology creation.
* @toml [main].refinement_levels
*/
int refinement_levels = 4;
/**
* @brief Polynomial order for high-order elements.
* @toml [main].order
*/
int order = 3;
/**
* @brief Whether to include an external domain extending to `r_infinity`.
* @details Currently this flag does not affect mesh generation.
* @toml [main].include_external_domain
*/
bool include_external_domain = false;
double r_core = 2.5;
/**
* @brief Radius of the stellar core region.
* @toml [main].r_core
*/
double r_core = 1.5;
/**
* @brief Radius of the stellar surface.
* @toml [main].r_star
*/
double r_star = 5.0;
/**
* @brief Flattening factor for spheroidal shaping (0 = spherical, >0 = oblate).
* @toml [main].flattening
*/
double flattening = 0;
/**
* @brief Outer radius of the external domain when enabled.
* @toml [main].r_infinity
*/
double r_infinity = 6.0;
/**
* @brief Radius inside which transformations are skipped to avoid singularities.
* @toml [main].r_instability
*/
double r_instability = 1e-14;
/**
* @brief Controls the smoothness/steepness of the core-to-envelope transition.
* @toml [main].core_steepness
*/
double core_steepness = 1.0;
};
}

View File

@@ -0,0 +1,27 @@
python_exe = import('python').find_installation('python3')
version_parser = '''
import sys, re
ver = sys.argv[1]
if ver.startswith("v"): ver = ver[1:]
m = re.match(r"^(\d+)\.(\d+)\.(\d+)(.*)$", ver)
if m:
print(f"{m.group(1)};{m.group(2)};{m.group(3)};{m.group(4)}")
else:
print("0;0;0;unknown")
'''
ver_res = run_command(python_exe, '-c', version_parser, meson.project_version(), check: true)
ver_parts = ver_res.stdout().strip().split(';')
config = configuration_data()
config.set('STROID_VERSION_MAJOR', ver_parts[0])
config.set('STROID_VERSION_MINOR', ver_parts[1])
config.set('STROID_VERSION_PATCH', ver_parts[2])
config.set('STROID_VERSION_TAG', ver_parts[3])
configure_file(
input : 'stroid.h.in',
output : 'stroid.h',
configuration : config ,
)

View File

@@ -0,0 +1,110 @@
#pragma once
#include "stroid/config/config.h"
#include "stroid/topology/topology.h"
#include "stroid/topology/mapping.h"
#include "stroid/topology/curvilinear.h"
#include "stroid/utils/mesh_utils.h"
#include "stroid/IO/mesh.h"
/**
* @namespace stroid
* @brief Public API surface for the stroid mesh generation library.
*
* This namespace aggregates configuration, topology construction, mapping, and
* mesh utilities for building curvilinear multi-block meshes.
*
* @par Example: build a mesh programmatically
* @code
* #include <memory>
* #include "mfem.hpp"
* #include "fourdst/config/config.h"
* #include "stroid/stroid.h"
*
* int main() {
* fourdst::config::Config<stroid::config::MeshConfig> cfg;
* auto mesh = stroid::topology::BuildSkeleton(cfg);
* stroid::topology::Finalize(*mesh, cfg);
* stroid::topology::PromoteToHighOrder(*mesh, cfg);
* stroid::topology::ProjectMesh(*mesh, cfg);
* stroid::IO::SaveVTU(*mesh, "stroid");
* }
* @endcode
*
* @par Example: tweak config then visualize
* @code
* fourdst::config::Config<stroid::config::MeshConfig> cfg;
* cfg->order = 4;
* cfg->flattening = 0.1;
* auto mesh = stroid::topology::BuildSkeleton(cfg);
* stroid::topology::Finalize(*mesh, cfg);
* stroid::topology::PromoteToHighOrder(*mesh, cfg);
* stroid::topology::ProjectMesh(*mesh, cfg);
* stroid::IO::ViewMesh(*mesh, "Stroid Mesh", stroid::IO::VISUALIZATION_MODE::ELEMENT_ID, "localhost", 19916);
* @endcode
*/
namespace stroid {
/**
* @brief Version helpers for the stroid library.
*/
struct version {
static constexpr int major = @STROID_VERSION_MAJOR@;
static constexpr int minor = @STROID_VERSION_MINOR@;
static constexpr int patch = @STROID_VERSION_PATCH@;
static constexpr const char* tag = "@STROID_VERSION_TAG@";
static std::string toString() {
std::string versionStr = std::to_string(major) + "." +
std::to_string(minor) + "." +
std::to_string(patch);
if (std::string(tag) != "") {
versionStr += "-" + std::string(tag);
}
return versionStr;
}
friend std::ostream& operator<<(std::ostream& os, const version&) {
os << toString();
return os;
}
};
}
/**
* @namespace std
* @brief Standard library extensions used by stroid.
*
* Provides a `std::formatter` specialization for `stroid::version` so it can
* be used with `std::format` and related APIs.
*/
// Overload format struct
template <>
struct std::formatter<stroid::version> : std::formatter<std::string> {
auto format(const stroid::version& v, auto& ctx) {
return std::formatter<std::string>::format(stroid::version::toString(), ctx);
}
};
/**
* @namespace stroid::config
* @brief Configuration types and defaults for mesh generation.
*/
namespace stroid::config {}
/**
* @namespace stroid::topology
* @brief Topology construction and curvilinear mapping utilities.
*/
namespace stroid::topology {}
/**
* @namespace stroid::IO
* @brief Mesh serialization and visualization helpers.
*/
namespace stroid::IO {}
/**
* @namespace stroid::utils
* @brief Mesh inspection and validation utilities.
*/
namespace stroid::utils {}

View File

@@ -5,6 +5,17 @@
#include "fourdst/config/config.h"
namespace stroid::topology {
/**
* @brief Promote a mesh to high-order by attaching an H1 nodal finite element space.
* @param mesh Mesh to update in-place.
* @param config Mesh configuration (uses `order`).
*/
void PromoteToHighOrder(mfem::Mesh& mesh, const fourdst::config::Config<config::MeshConfig> &config);
/**
* @brief Project high-order mesh nodes using the configured curvilinear mapping.
* @details Requires nodes to be present (call PromoteToHighOrder first).
* @param mesh Mesh to update in-place.
* @param config Mesh configuration (uses radii, flattening, and mapping parameters).
*/
void ProjectMesh(mfem::Mesh& mesh, const fourdst::config::Config<config::MeshConfig> &config);
}

View File

@@ -5,11 +5,31 @@
#include "fourdst/config/config.h"
namespace stroid::topology {
/**
* @brief Apply an equiangular (gnomonic) projection to a point on a cube.
* @param pos Position vector updated in-place.
*/
void ApplyEquiangular(mfem::Vector& pos);
/**
* @brief Apply spheroidal flattening along the Z axis.
* @param pos Position vector updated in-place.
* @param config Mesh configuration (uses `flattening`).
*/
void ApplySpheroidal(mfem::Vector& pos, const fourdst::config::Config<config::MeshConfig> &config);
/**
* @brief Apply Kelvin transform outside the stellar radius.
* @param pos Position vector updated in-place.
* @param config Mesh configuration (uses `r_star` and `r_infinity`).
*/
void ApplyKelvin(mfem::Vector& pos, const fourdst::config::Config<config::MeshConfig> &config);
/**
* @brief Map a point from the initial block topology to the curvilinear domain.
* @param pos Position vector updated in-place.
* @param config Mesh configuration (uses radii, flattening, instability radius, and core steepness).
* @param attribute_id Element attribute ID (currently unused).
*/
void TransformPoint(mfem::Vector& pos, const fourdst::config::Config<config::MeshConfig> &config, int attribute_id);
}

View File

@@ -6,6 +6,16 @@
#include <memory>
namespace stroid::topology {
/**
* @brief Build the initial multi-block mesh topology for the star model.
* @param config Mesh configuration (uses radii and domain flags).
* @return Newly allocated mesh skeleton (not yet refined or curved).
*/
std::unique_ptr<mfem::Mesh> BuildSkeleton(const fourdst::config::Config<config::MeshConfig> & config);
/**
* @brief Finalize topology, validate orientation, and apply uniform refinement.
* @param mesh Mesh to finalize in-place.
* @param config Mesh configuration (uses `refinement_levels`).
*/
void Finalize(mfem::Mesh& mesh, const fourdst::config::Config<config::MeshConfig> &config);
}

View File

@@ -3,6 +3,16 @@
#include "mfem.hpp"
namespace stroid::utils {
/**
* @brief Mark elements with negative Jacobian determinant.
* @details Elements detected as flipped are assigned attribute 999.
* @param mesh Mesh to scan and update in-place.
*/
void MarkFlippedElements(mfem::Mesh& mesh);
/**
* @brief Mark boundary elements whose outward normal orientation is flipped.
* @details Boundary elements detected as flipped are assigned attribute 500.
* @param mesh Mesh to scan and update in-place.
*/
void MarkFlippedBoundaryElements(mfem::Mesh& mesh);
}

View File

@@ -17,14 +17,13 @@ namespace stroid::IO {
void SaveVTU(mfem::Mesh &mesh, const std::string &exportName) {
mfem::ParaViewDataCollection pd(exportName, &mesh);
pd.SetDataFormat(mfem::VTKFormat::BINARY);
pd.SetHighOrderOutput(true);
pd.Save();
}
void ViewMesh(mfem::Mesh &mesh, const std::string& title, const VISUALIZATION_MODE mode) {
char vishost[] = "localhost";
int visport = 19916;
mfem::socketstream sol_sock(vishost, visport);
void ViewMesh(mfem::Mesh &mesh, const std::string& title, const VISUALIZATION_MODE mode, const std::string &vishost, int visport) {
mfem::socketstream sol_sock(vishost.c_str(), visport);
if (!sol_sock.is_open()) {
std::cerr << "Unable to connect to GLVis server at "
<< vishost << ':' << visport << std::endl;

View File

@@ -1,8 +1,10 @@
dependencies = [
mfem_dep,
config_dep
config_dep,
]
subdir('include/stroid')
stroid_include_files = include_directories('include')
stroid_sources = files(
'lib/topology/curvilinear.cpp',
@@ -13,7 +15,7 @@ stroid_sources = files(
)
stroid_lib = static_library(
'stroid',
'libstroid',
stroid_sources,
include_directories: stroid_include_files,
dependencies: dependencies,