feat(stroid): first working version
stroid generates o-grid topologies with proper boundary conditions applied. Currently the external domain does not work, this will be addressed in the next commit.
This commit is contained in:
41
src/lib/topology/curvilinear.cpp
Normal file
41
src/lib/topology/curvilinear.cpp
Normal file
@@ -0,0 +1,41 @@
|
||||
#include "stroid/topology/curvilinear.h"
|
||||
#include "stroid/topology/mapping.h"
|
||||
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
|
||||
namespace stroid::topology {
|
||||
void PromoteToHighOrder(mfem::Mesh &mesh, const fourdst::config::Config<config::MeshConfig> &config) {
|
||||
const auto* fec = new mfem::H1_FECollection(config->order, mesh.Dimension());
|
||||
auto* fes = new mfem::FiniteElementSpace(&mesh, fec, mesh.SpaceDimension());
|
||||
mesh.SetNodalFESpace(fes);
|
||||
}
|
||||
|
||||
void ProjectMesh(mfem::Mesh &mesh, const fourdst::config::Config<config::MeshConfig> &config) {
|
||||
if (!mesh.GetNodes()) {
|
||||
std::cerr << "Error: Mesh has no nodes to project. Call PromoteToHighOrder first." << std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
mfem::GridFunction& nodes = *mesh.GetNodes(); // Already confirmed not null
|
||||
const mfem::FiniteElementSpace* fes = nodes.FESpace();
|
||||
|
||||
const int vDim = fes->GetVDim();
|
||||
const int nDofs = fes->GetNDofs();
|
||||
|
||||
mfem::Vector pos(vDim);
|
||||
|
||||
for (int i = 0; i < nDofs; ++i) {
|
||||
for (int d = 0; d < vDim; ++d) {
|
||||
pos(d) = nodes(fes->DofToVDof(i, d));
|
||||
}
|
||||
|
||||
TransformPoint(pos, config, 0);
|
||||
|
||||
for (int d = 0; d < vDim; ++d) {
|
||||
nodes(fes->DofToVDof(i, d)) = pos(d);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
#include "stroid/topology/key.h"
|
||||
|
||||
namespace stroid::topology {
|
||||
CanonicalKey get_canonical_key(int block_id, size_t i, size_t j, size_t k, size_t N, size_t M) {
|
||||
uing32_t I = static_cast<uint32_t>(i);
|
||||
uint32_t J = static_cast<uint32_t>(j);
|
||||
uint32_t K = static_cast<uint32_t>(k);
|
||||
|
||||
uint32_t N = static_cast<uint32_t>(i);
|
||||
uint32_t M = static_cast<uint32_t>(j);
|
||||
|
||||
|
||||
if (block_id == 0) return {0, I, J, K};
|
||||
|
||||
if (k==0) {
|
||||
switch (block_id) {
|
||||
case 1: return {0, N, I, J};
|
||||
case 2: return {0, 0, I, J};
|
||||
case 3: return {0, I, N, J};
|
||||
case 4: return {0, I, 0, J};
|
||||
case 5: return {0, I, J, N};
|
||||
case 6: return {0, I, J, 0}
|
||||
}
|
||||
}
|
||||
|
||||
if (i == N) {
|
||||
uint32_t target_b = (b == 1 || b == 2) ? 3 : 1;
|
||||
if (target_b < block_id) {
|
||||
if (b == 3) return get_canonical_key(1, 0, j, k, N, M);
|
||||
if (b == 4) return get_canonical_key(1, N, j, k, N, M);
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
104
src/lib/topology/mapping.cpp
Normal file
104
src/lib/topology/mapping.cpp
Normal file
@@ -0,0 +1,104 @@
|
||||
#include "stroid/topology/mapping.h"
|
||||
#include <cmath>
|
||||
#include <algorithm>
|
||||
|
||||
namespace stroid::topology {
|
||||
void ApplyEquiangular(mfem::Vector &pos) {
|
||||
const double x = pos(0);
|
||||
const double y = pos(1);
|
||||
const double z = pos(2);
|
||||
|
||||
const double absX = std::abs(x);
|
||||
const double absY = std::abs(y);
|
||||
const double absZ = std::abs(z);
|
||||
|
||||
const double maxAbs = std::max({absX, absY, absZ});
|
||||
|
||||
if (maxAbs < 1e-14) return;
|
||||
|
||||
if (absX == maxAbs) {
|
||||
pos(1) = x * std::tan(M_PI / 4.0 * (y/x));
|
||||
pos(2) = x * std::tan(M_PI / 4.0 * (z/x));
|
||||
} else if (absY == maxAbs) {
|
||||
pos(0) = y * std::tan(M_PI / 4.0 * (x/y));
|
||||
pos(2) = y * std::tan(M_PI / 4.0 * (z/y));
|
||||
} else { // absZ == maxAbs
|
||||
pos(0) = z * std::tan(M_PI / 4.0 * (x/z));
|
||||
pos(1) = z * std::tan(M_PI / 4.0 * (y/z));
|
||||
}
|
||||
}
|
||||
|
||||
void ApplySpheroidal(mfem::Vector &pos, const fourdst::config::Config<config::MeshConfig> &config) {
|
||||
pos(2) *= (1.0 - config->flattening);
|
||||
}
|
||||
|
||||
void ApplyKelvin(mfem::Vector &pos, const fourdst::config::Config<config::MeshConfig> &config) {
|
||||
const double r = pos.Norml2();
|
||||
if (r <= config->r_star) {
|
||||
return;
|
||||
}
|
||||
|
||||
double xi = (r - config->r_star) / (config->r_infinity - config->r_star);
|
||||
xi = std::min(0.999, std::max(0.0, xi)); // Clamp xi to [0, 0.999]
|
||||
|
||||
const double r_new = config->r_star + xi / (1.0 - xi);
|
||||
const double scale = r_new / r;
|
||||
pos *= scale;
|
||||
}
|
||||
|
||||
void TransformPoint(mfem::Vector &pos, const fourdst::config::Config<config::MeshConfig> &config, int attribute_id) {
|
||||
double l_inf = 0.0;
|
||||
for (int i = 0; i < pos.Size(); ++i) {
|
||||
l_inf = std::max(l_inf, std::abs(pos(i)));
|
||||
}
|
||||
|
||||
if (l_inf < config->r_instability) return;
|
||||
|
||||
// Gnomonic projection
|
||||
const double r_log = pos.Norml2();
|
||||
mfem::Vector unit_dir = pos;
|
||||
unit_dir /= r_log;
|
||||
|
||||
ApplyEquiangular(unit_dir);
|
||||
unit_dir /= unit_dir.Norml2(); // Re-normalize
|
||||
|
||||
if (l_inf <= config->r_core) {
|
||||
const double t = l_inf / config->r_core;
|
||||
double alpha = std::pow(t, config->core_steepness);
|
||||
|
||||
// Smoothstep function to apply C1 continuity
|
||||
alpha = alpha * alpha * (3.0 - 2.0 * alpha);
|
||||
|
||||
mfem::Vector pos_cartesian = pos;
|
||||
mfem::Vector pos_spherical = unit_dir;
|
||||
|
||||
pos_spherical *= l_inf;
|
||||
|
||||
|
||||
for (int d = 0; d < pos.Size(); ++d) {
|
||||
pos(d) = (1.0 - alpha) * pos_cartesian(d) + alpha * pos_spherical(d);
|
||||
}
|
||||
|
||||
ApplySpheroidal(pos, config);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
|
||||
if (l_inf <= config->r_star) {
|
||||
const double xi = (l_inf - config->r_core) / (config->r_star - config->r_core);
|
||||
const double r_phys = config->r_core + xi * (config->r_star - config->r_core);
|
||||
|
||||
pos = unit_dir;
|
||||
pos *= r_phys;
|
||||
|
||||
ApplySpheroidal(pos, config);
|
||||
} else {
|
||||
pos = unit_dir;
|
||||
pos *= l_inf;
|
||||
|
||||
ApplyKelvin(pos, config);
|
||||
ApplySpheroidal(pos, config);
|
||||
}
|
||||
}
|
||||
}
|
||||
75
src/lib/topology/topology.cpp
Normal file
75
src/lib/topology/topology.cpp
Normal file
@@ -0,0 +1,75 @@
|
||||
#include "mfem.hpp"
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
|
||||
#include "stroid/config/config.h"
|
||||
#include "fourdst/config/config.h"
|
||||
|
||||
namespace stroid::topology {
|
||||
|
||||
std::unique_ptr<mfem::Mesh> BuildSkeleton(const fourdst::config::Config<config::MeshConfig> & config) {
|
||||
int nVert = config->include_external_domain ? 24 : 16;
|
||||
int nElem = config->include_external_domain ? 13 : 7;
|
||||
int nBev = 6;
|
||||
|
||||
auto mesh = std::make_unique<mfem::Mesh>(3, nVert, nElem, nBev, 3);
|
||||
|
||||
auto add_box = [&](double scale) {
|
||||
for (const double z : {-scale, scale})
|
||||
for (const double y : {-scale, scale})
|
||||
for (const double x : {-scale, scale})
|
||||
mesh->AddVertex(x, y, z);
|
||||
};
|
||||
|
||||
add_box(config->r_core);
|
||||
add_box(config->r_star);
|
||||
if (config->include_external_domain) {
|
||||
add_box(config->r_infinity);
|
||||
}
|
||||
|
||||
const int core_v[8] = {0, 1, 3, 2, 4, 5, 7, 6};
|
||||
mesh->AddHex(core_v, 1);
|
||||
|
||||
int shells[6][8] = {
|
||||
{8, 9, 11, 10, 0, 1, 3, 2},
|
||||
{4, 5, 7, 6, 12, 13, 15, 14}, // +Z face
|
||||
{0, 1, 5, 4, 8, 9, 13, 12}, // -Y face
|
||||
{10, 11, 15, 14, 2, 3, 7, 6},
|
||||
{1, 3, 7, 5, 9, 11, 15, 13}, // +X face
|
||||
{0, 4, 6, 2, 8, 12, 14, 10} // -X face
|
||||
};
|
||||
for (const auto & shell : shells) mesh->AddHex(shell, 2);
|
||||
|
||||
const int bdr_quads[6][4] = {
|
||||
{12, 13, 15, 14},
|
||||
{13, 9, 11, 15},
|
||||
{9, 8, 10, 11},
|
||||
{8, 12, 14, 10},
|
||||
{8, 9, 13, 12},
|
||||
{14, 15, 11, 10}
|
||||
};
|
||||
|
||||
for (const auto& bdr: bdr_quads) {
|
||||
mesh->AddBdrQuad(bdr, 1);
|
||||
}
|
||||
|
||||
return mesh;
|
||||
}
|
||||
|
||||
void Finalize(mfem::Mesh& mesh, const fourdst::config::Config<config::MeshConfig> &config) {
|
||||
mesh.FinalizeTopology();
|
||||
mesh.Finalize();
|
||||
mesh.CheckElementOrientation(true);
|
||||
mesh.CheckBdrElementOrientation(true);
|
||||
for (int i = 0; i < config->refinement_levels; ++i) {
|
||||
mesh.UniformRefinement();
|
||||
}
|
||||
|
||||
if (!mesh.Conforming()) {
|
||||
std::cerr << "WARNING: Mesh has been detected to be non conforming!" << std::endl;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user