// Copyright (c) 2010-2023, 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 #include using namespace std; using namespace mfem; double 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; 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.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 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"; int visport = 19916; 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) { double 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] ); }