165 lines
5.0 KiB
C++
165 lines
5.0 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.
|
|
//
|
|
// ---------------------------------------------------
|
|
// Mobius Strip Miniapp: Generate Mobius strip meshes
|
|
// ---------------------------------------------------
|
|
//
|
|
// This miniapp generates various Mobius strip-like surface meshes. It is a good
|
|
// way to generate complex surface meshes. Manipulating the mesh topology and
|
|
// performing mesh transformation are demonstrated. The mobius-strip mesh in the
|
|
// data/ directory was generated with this miniapp.
|
|
//
|
|
// Compile with: make mobius-strip
|
|
//
|
|
// Sample runs: mobius-strip
|
|
// mobius-strip -t 4.5 -nx 16
|
|
// mobius-strip -c 1 -t 1
|
|
// mobius-strip -c 1 -t 4 -nx 16
|
|
// mobius-strip -c 0 -t 0.75
|
|
|
|
#include "mfem.hpp"
|
|
#include <fstream>
|
|
#include <iostream>
|
|
|
|
using namespace std;
|
|
using namespace mfem;
|
|
|
|
real_t num_twists = 0.5;
|
|
void mobius_trans(const Vector &x, Vector &p);
|
|
|
|
int main(int argc, char *argv[])
|
|
{
|
|
const char *new_mesh_file = "mobius-strip.mesh";
|
|
int nx = 8;
|
|
int ny = 2;
|
|
int order = 3;
|
|
int close_strip = 2;
|
|
bool dg_mesh = false;
|
|
int visport = 19916;
|
|
bool visualization = true;
|
|
|
|
OptionsParser args(argc, argv);
|
|
args.AddOption(&new_mesh_file, "-m", "--mesh-out-file",
|
|
"Output Mesh file to write.");
|
|
args.AddOption(&nx, "-nx", "--num-elements-x",
|
|
"Number of elements in x-direction.");
|
|
args.AddOption(&ny, "-ny", "--num-elements-y",
|
|
"Number of elements in y-direction.");
|
|
args.AddOption(&order, "-o", "--mesh-order",
|
|
"Order (polynomial degree) of the mesh elements.");
|
|
args.AddOption(&close_strip, "-c", "--close-strip",
|
|
"How to close the strip: 0 - open, 1 - closed, 2 - twisted.");
|
|
args.AddOption(&dg_mesh, "-dm", "--discont-mesh", "-cm", "--cont-mesh",
|
|
"Use discontinuous or continuous space for the mesh nodes.");
|
|
args.AddOption(&num_twists, "-t", "--num-twists",
|
|
"Number of twists of the strip.");
|
|
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 mesh could use quads (default) or triangles
|
|
Element::Type el_type = Element::QUADRILATERAL;
|
|
// Element::Type el_type = Element::TRIANGLE;
|
|
Mesh mesh = Mesh::MakeCartesian2D(nx, ny, el_type, 1, 2*M_PI, 2.0);
|
|
|
|
mesh.SetCurvature(order, true, 3, Ordering::byVDIM);
|
|
|
|
if (close_strip)
|
|
{
|
|
Array<int> v2v(mesh.GetNV());
|
|
for (int i = 0; i < v2v.Size(); i++)
|
|
{
|
|
v2v[i] = i;
|
|
}
|
|
// identify vertices on vertical lines (with a flip)
|
|
for (int j = 0; j <= ny; j++)
|
|
{
|
|
int v_old = nx + j * (nx + 1);
|
|
int v_new = ((close_strip == 1) ? j : (ny - j)) * (nx + 1);
|
|
v2v[v_old] = v_new;
|
|
}
|
|
// 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();
|
|
}
|
|
|
|
mesh.Transform(mobius_trans);
|
|
|
|
if (!dg_mesh)
|
|
{
|
|
mesh.SetCurvature(order, false, 3, Ordering::byVDIM);
|
|
}
|
|
|
|
GridFunction &nodes = *mesh.GetNodes();
|
|
for (int i = 0; i < nodes.Size(); i++)
|
|
{
|
|
if (std::abs(nodes(i)) < 1e-12)
|
|
{
|
|
nodes(i) = 0.0;
|
|
}
|
|
}
|
|
|
|
ofstream ofs(new_mesh_file);
|
|
ofs.precision(8);
|
|
mesh.Print(ofs);
|
|
ofs.close();
|
|
|
|
if (visualization)
|
|
{
|
|
char vishost[] = "localhost";
|
|
socketstream sol_sock(vishost, visport);
|
|
sol_sock.precision(8);
|
|
sol_sock << "mesh\n" << mesh << flush;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
void mobius_trans(const Vector &x, Vector &p)
|
|
{
|
|
real_t a = 1.0 + 0.5 * (x[1] - 1.0) * cos( num_twists * x[0] );
|
|
|
|
p.SetSize(3);
|
|
p[0] = a * cos( x[0] );
|
|
p[1] = a * sin( x[0] );
|
|
p[2] = 0.5 * (x[1] - 1.0) * sin( num_twists * x[0] );
|
|
}
|