// 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. // // ------------------------------------------------------- // Twist Miniapp: Generate simple twisted periodic meshes // ------------------------------------------------------- // // This miniapp generates simple periodic meshes to demonstrate MFEM's handling // of periodic domains. MFEM's strategy is to use a discontinuous vector field // to define the mesh coordinates on a topologically periodic mesh. It works by // defining a stack of individual elements and stitching together the top and // bottom of the mesh. The stack can also be twisted so that the vertices of the // bottom and top can be joined with any integer offset (for tetrahedral and // wedge meshes only even offsets are supported). // // Compile with: make twist // // Sample runs: twist // twist -no-pm // twist -nt -2 -no-pm // twist -nt 2 -e 4 // twist -nt 2 -e 6 // twist -nt 3 -e 8 // #include "mfem.hpp" #include #include using namespace std; using namespace mfem; static Element::Type el_type_ = Element::WEDGE; static int order_ = 3; static int nz_ = 3; static int nt_ = 2; static double a_ = 1.0; static double b_ = 1.0; static double c_ = 3.0; void pts(int iphi, int t, double x[]); void trans(const Vector &x, Vector &p); int main(int argc, char *argv[]) { int ser_ref_levels = 0; int el_type = 8; bool per_mesh = true; bool dg_mesh = false; bool visualization = true; OptionsParser args(argc, argv); args.AddOption(&nz_, "-nz", "--num-elements-z", "Number of elements in z-direction."); args.AddOption(&nt_, "-nt", "--num-twists", "Number of node positions to twist the top of the mesh."); 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(&a_, "-a", "--base-x", "Width of the base in x-direction."); args.AddOption(&b_, "-b", "--base-y", "Width of the base in y-direction."); args.AddOption(&c_, "-c", "--height", "Height in z-direction."); args.AddOption(&el_type, "-e", "--element-type", "Element type: 4 - Tetrahedron, 6 - Wedge, 8 - Hexahedron."); args.AddOption(&per_mesh, "-pm", "--periodic-mesh", "-no-pm", "--non-periodic-mesh", "Enforce periodicity in z-direction " "(requires discontinuous mesh)."); 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.Parse(); if (!args.Good()) { args.PrintUsage(cout); return 1; } args.PrintOptions(cout); // The output mesh could be tetrahedra, hexahedra, or prisms switch (el_type) { case 4: el_type_ = Element::TETRAHEDRON; break; case 6: el_type_ = Element::WEDGE; break; case 8: el_type_ = Element::HEXAHEDRON; break; default: cout << "Unsupported element type" << endl; exit(1); break; } // Define the mesh Mesh mesh = Mesh::MakeCartesian3D(1, 1, nz_, el_type_, a_, b_, c_, false); // Promote to high order mesh and transform into a twisted shape if (order_ > 1 || dg_mesh || per_mesh) { mesh.SetCurvature(order_, dg_mesh || per_mesh, 3, Ordering::byVDIM); } if (nt_ != 0 ) { mesh.Transform(trans); } while (per_mesh) { // Verify geometric compatibility if (nt_ % 2 == 1 && fabs(a_ - b_) > 1e-6 * a_) { cout << "Base is rectangular so number of shifts must be even " << "for a periodic mesh!" << endl; exit(1); } // Verify topological compatibility if (nt_ % 2 == 1 && (el_type_ == Element::TETRAHEDRON || el_type_ == Element::WEDGE)) { cout << "Diagonal cuts on the base and top must line up " << "for a periodic mesh!" << endl; exit(1); } int nnode = 4; int noff = (nt_ >= 0) ? 0 : (nnode * (1 - nt_ / nnode)); Array v2v(mesh.GetNV()); for (int i = 0; i < v2v.Size() - nnode; i++) { v2v[i] = i; } // identify vertices at the extremes of the stack switch ((noff + nt_) % nnode) { case 0: v2v[v2v.Size() - nnode + 0] = 0; v2v[v2v.Size() - nnode + 1] = 1; v2v[v2v.Size() - nnode + 2] = 2; v2v[v2v.Size() - nnode + 3] = 3; break; case 1: v2v[v2v.Size() - nnode + 0] = 2; v2v[v2v.Size() - nnode + 1] = 0; v2v[v2v.Size() - nnode + 2] = 3; v2v[v2v.Size() - nnode + 3] = 1; break; case 2: v2v[v2v.Size() - nnode + 0] = 3; v2v[v2v.Size() - nnode + 1] = 2; v2v[v2v.Size() - nnode + 2] = 1; v2v[v2v.Size() - nnode + 3] = 0; break; case 3: v2v[v2v.Size() - nnode + 0] = 1; v2v[v2v.Size() - nnode + 1] = 3; v2v[v2v.Size() - nnode + 2] = 0; v2v[v2v.Size() - nnode + 3] = 2; break; } // 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(); break; } // 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::TETRAHEDRON) { oss << "twist-tet"; } else if (el_type_ == Element::WEDGE) { oss << "twist-wedge"; } else { oss << "twist-hex"; } oss << "-o" << order_ << "-s" << nt_; if (ser_ref_levels > 0) { oss << "-r" << ser_ref_levels; } if (per_mesh) { oss << "-p"; } else if (dg_mesh) { oss << "-d"; } else { oss << "-c"; } 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"; int visport = 19916; socketstream sol_sock(vishost, visport); sol_sock.precision(8); sol_sock << "mesh\n" << mesh << flush; } // Clean up and exit return 0; } void trans(const Vector &x, Vector &p) { double z = x[2]; double phi = 0.5 * M_PI * nt_ * z / c_; double cp = cos(phi); double sp = sin(phi); p[0] = 0.5 * a_ + (x[0] - 0.5 * a_) * cp - (x[1] - 0.5 * b_) * sp; p[1] = 0.5 * b_ + (x[0] - 0.5 * a_) * sp + (x[1] - 0.5 * b_) * cp; p[2] = z; }