267 lines
7.7 KiB
C++
267 lines
7.7 KiB
C++
// Copyright (c) 2010-2025, Lawrence Livermore National Security, LLC. Produced
|
|
// at the Lawrence Livermore National Laboratory. All Rights reserved. See files
|
|
// LICENSE and NOTICE for details. LLNL-CODE-806117.
|
|
//
|
|
// This file is part of the MFEM library. For more information and source code
|
|
// availability visit https://mfem.org.
|
|
//
|
|
// MFEM is free software; you can redistribute it and/or modify it under the
|
|
// terms of the BSD-3 license. We welcome feedback and contributions, see file
|
|
// CONTRIBUTING.md for details.
|
|
//
|
|
// ------------------------------------------------
|
|
// Toroid Miniapp: Generate simple toroidal meshes
|
|
// ------------------------------------------------
|
|
//
|
|
// This miniapp generates two types of toroidal meshes; one with triangular
|
|
// cross sections and one with square cross sections. It works by defining a
|
|
// stack of individual elements and bending them so that the bottom and top of
|
|
// the stack can be joined to form a torus. The stack can also be twisted so
|
|
// that the vertices of the bottom and top can be joined with any integer
|
|
// offset.
|
|
//
|
|
// Compile with: make toroid
|
|
//
|
|
// Sample runs: toroid
|
|
// toroid -nphi 6
|
|
// toroid -ns 1
|
|
// toroid -ns 0 -t0 -30
|
|
// toroid -R 2 -r 1 -ns 3
|
|
// toroid -R 2 -r 1 -ns -3
|
|
// toroid -R 2 -r 1 -ns 3 -e 1
|
|
// toroid -R 2 -r 1 -ns 3 -e 1 -rs 1
|
|
// toroid -nphi 2 -ns 10 -e 1 -o 4
|
|
|
|
#include "mfem.hpp"
|
|
#include <fstream>
|
|
#include <iostream>
|
|
|
|
using namespace std;
|
|
using namespace mfem;
|
|
|
|
static Element::Type el_type_ = Element::WEDGE;
|
|
static int order_ = 3;
|
|
static int nphi_ = 8;
|
|
static int ns_ = 0;
|
|
static real_t R_ = 1.0;
|
|
static real_t r_ = 0.2;
|
|
static real_t theta0_ = 0.0;
|
|
|
|
void pts(int iphi, int t, real_t x[]);
|
|
void trans(const Vector &x, Vector &p);
|
|
|
|
int main(int argc, char *argv[])
|
|
{
|
|
int ser_ref_levels = 0;
|
|
int el_type = 0;
|
|
bool dg_mesh = false;
|
|
int visport = 19916;
|
|
bool visualization = true;
|
|
|
|
OptionsParser args(argc, argv);
|
|
args.AddOption(&nphi_, "-nphi", "--num-elements-phi",
|
|
"Number of elements in phi-direction.");
|
|
args.AddOption(&ns_, "-ns", "--num-shifts",
|
|
"Number of shifts.");
|
|
args.AddOption(&order_, "-o", "--mesh-order",
|
|
"Order (polynomial degree) of the mesh elements.");
|
|
args.AddOption(&ser_ref_levels, "-rs", "--refine-serial",
|
|
"Number of times to refine the mesh uniformly in serial.");
|
|
args.AddOption(&R_, "-R", "--major-radius",
|
|
"Major radius of the torus.");
|
|
args.AddOption(&r_, "-r", "--minor-radius",
|
|
"Minor radius of the torus.");
|
|
args.AddOption(&theta0_, "-t0", "--initial-angle",
|
|
"Starting angle of the cross section (in degrees).");
|
|
args.AddOption(&el_type, "-e", "--element-type",
|
|
"Element type: 0 - Wedge, 1 - Hexahedron.");
|
|
args.AddOption(&dg_mesh, "-dm", "--discont-mesh", "-cm", "--cont-mesh",
|
|
"Use discontinuous or continuous space for the mesh nodes.");
|
|
args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
|
|
"--no-visualization",
|
|
"Enable or disable GLVis visualization.");
|
|
args.AddOption(&visport, "-p", "--send-port", "Socket for GLVis.");
|
|
args.Parse();
|
|
if (!args.Good())
|
|
{
|
|
args.PrintUsage(cout);
|
|
return 1;
|
|
}
|
|
args.PrintOptions(cout);
|
|
|
|
// The output mesh could be hexahedra or prisms
|
|
el_type_ = (el_type == 0) ? Element::WEDGE : Element::HEXAHEDRON;
|
|
if (el_type_ != Element::WEDGE && el_type_ != Element::HEXAHEDRON)
|
|
{
|
|
cout << "Unsupported element type" << endl;
|
|
exit(1);
|
|
}
|
|
|
|
// Determine the number of nodes in the cross section
|
|
int nnode = (el_type_ == Element::WEDGE)? 3:4;
|
|
int nshift = (ns_ >= 0) ? 0 : (nnode * (1 - ns_ / nnode));
|
|
|
|
// Convert initial angle from degrees to radians
|
|
theta0_ *= M_PI / 180.0;
|
|
|
|
// Define an empty mesh
|
|
Mesh *mesh;
|
|
mesh = new Mesh(3, nnode * (nphi_+1), nphi_);
|
|
|
|
// Add vertices for a stack of elements
|
|
real_t c[3];
|
|
for (int i=0; i<=nphi_; i++)
|
|
{
|
|
c[0] = 0.0; c[1] = 0.0; c[2] = i;
|
|
mesh->AddVertex(c);
|
|
|
|
c[0] = 1.0;
|
|
mesh->AddVertex(c);
|
|
|
|
if (el_type_ == Element::HEXAHEDRON)
|
|
{
|
|
c[0] = 1.0; c[1] = 1.0;
|
|
mesh->AddVertex(c);
|
|
}
|
|
|
|
c[0] = 0.0; c[1] = 1.0;
|
|
mesh->AddVertex(c);
|
|
}
|
|
|
|
// Add Elements of the desired type
|
|
{
|
|
int v[8];
|
|
for (int i=0; i < nphi_; i++)
|
|
{
|
|
if (el_type_ == Element::WEDGE)
|
|
{
|
|
for (int j = 0; j < 6; j++) { v[j] = 3*i+j; }
|
|
mesh->AddWedge(v);
|
|
}
|
|
else
|
|
{
|
|
for (int j = 0; j < 8; j++) { v[j] = 4*i+j; }
|
|
mesh->AddHex(v);
|
|
}
|
|
}
|
|
}
|
|
mesh->FinalizeTopology();
|
|
|
|
// Promote to high order mesh and transform into a torus shape
|
|
if (order_ > 1)
|
|
{
|
|
mesh->SetCurvature(order_, true, 3, Ordering::byVDIM);
|
|
}
|
|
mesh->Transform(trans);
|
|
|
|
// Stitch the ends of the stack together
|
|
{
|
|
Array<int> v2v(mesh->GetNV());
|
|
for (int i = 0; i < v2v.Size() - nnode; i++)
|
|
{
|
|
v2v[i] = i;
|
|
}
|
|
// identify vertices at the extremes of the stack of prisms
|
|
for (int i=0; i<nnode; i++)
|
|
{
|
|
v2v[v2v.Size() - nnode + i] = (nshift + ns_ + i) % nnode;
|
|
}
|
|
// renumber elements
|
|
for (int i = 0; i < mesh->GetNE(); i++)
|
|
{
|
|
Element *el = mesh->GetElement(i);
|
|
int *v = el->GetVertices();
|
|
int nv = el->GetNVertices();
|
|
for (int j = 0; j < nv; j++)
|
|
{
|
|
v[j] = v2v[v[j]];
|
|
}
|
|
}
|
|
// renumber boundary elements
|
|
for (int i = 0; i < mesh->GetNBE(); i++)
|
|
{
|
|
Element *el = mesh->GetBdrElement(i);
|
|
int *v = el->GetVertices();
|
|
int nv = el->GetNVertices();
|
|
for (int j = 0; j < nv; j++)
|
|
{
|
|
v[j] = v2v[v[j]];
|
|
}
|
|
}
|
|
mesh->RemoveUnusedVertices();
|
|
mesh->RemoveInternalBoundaries();
|
|
}
|
|
if (order_ > 1)
|
|
{
|
|
mesh->SetCurvature(order_, dg_mesh, 3, Ordering::byVDIM);
|
|
}
|
|
|
|
// Refine the mesh if desired
|
|
for (int lev = 0; lev < ser_ref_levels; lev++)
|
|
{
|
|
mesh->UniformRefinement();
|
|
}
|
|
|
|
// Output the resulting mesh to a file
|
|
{
|
|
ostringstream oss;
|
|
if (el_type_ == Element::WEDGE)
|
|
{
|
|
oss << "toroid-wedge";
|
|
}
|
|
else
|
|
{
|
|
oss << "toroid-hex";
|
|
}
|
|
oss << "-o" << order_ << "-s" << ns_;
|
|
if (ser_ref_levels > 0)
|
|
{
|
|
oss << "-r" << ser_ref_levels;
|
|
}
|
|
oss << ".mesh";
|
|
ofstream ofs(oss.str().c_str());
|
|
ofs.precision(8);
|
|
mesh->Print(ofs);
|
|
ofs.close();
|
|
}
|
|
|
|
// Output the resulting mesh to GLVis
|
|
if (visualization)
|
|
{
|
|
char vishost[] = "localhost";
|
|
socketstream sol_sock(vishost, visport);
|
|
sol_sock.precision(8);
|
|
sol_sock << "mesh\n" << *mesh << flush;
|
|
}
|
|
|
|
// Clean up and exit
|
|
delete mesh;
|
|
return 0;
|
|
}
|
|
|
|
void trans(const Vector &x, Vector &p)
|
|
{
|
|
int nnode = (el_type_ == Element::WEDGE)? 3:4;
|
|
|
|
real_t phi = 2.0 * M_PI * x[2] / nphi_;
|
|
real_t theta = theta0_ + phi * ns_ / nnode;
|
|
|
|
real_t u = (1.5 * (x[0] + x[1]) - 1.0) * r_;
|
|
real_t v = sqrt(0.75) * (x[0] - x[1]) * r_;
|
|
|
|
if (el_type_ == Element::WEDGE)
|
|
{
|
|
u = (1.5 * (x[0] + x[1]) - 1.0) * r_;
|
|
v = sqrt(0.75) * (x[0] - x[1]) * r_;
|
|
}
|
|
else
|
|
{
|
|
u = M_SQRT2 * (x[1] - 0.5) * r_;
|
|
v = M_SQRT2 * (x[0] - 0.5) * r_;
|
|
}
|
|
|
|
p[0] = ( R_ + u * cos(theta) + v * sin(theta)) * cos(phi);
|
|
p[1] = ( R_ + u * cos(theta) + v * sin(theta)) * sin(phi);
|
|
p[2] = v * cos(theta) - u * sin(theta);
|
|
}
|