Merge pull request #4044 from mfem/named-attr-sets

Named attribute sets
This commit is contained in:
Tzanio Kolev
2024-04-03 13:42:46 -07:00
committed by GitHub
23 changed files with 1896 additions and 26 deletions
+3
View File
@@ -70,6 +70,9 @@ Miscellaneous
- RAJA backend will use seq_exec for serial loop execution when RAJA
v2023.06.00 and beyond is detected as loop_exec is deprecated.
- Adding named attribute sets and basic supporting methods to the Mesh class as
a convenient means of referring to sets of domain or boundary attribute
numbers. Also adding related serial and parallel examples which illustrate.
Version 4.6, released on September 27, 2023
===========================================
+118
View File
@@ -0,0 +1,118 @@
SetFactory("OpenCASCADE");
order = 1;
R = 1;
r = 0.2;
Point(1) = {0,0,0};
Point(2) = {r/Sqrt(2),r/Sqrt(2),0};
Point(3) = {-r/Sqrt(2),r/Sqrt(2),0};
Point(4) = {-r/Sqrt(2),-r/Sqrt(2),0};
Point(5) = {r/Sqrt(2),-r/Sqrt(2),0};
Point(6) = {R,0,0};
Point(7) = {R/Sqrt(2),R/Sqrt(2),0};
Point(8) = {0,R,0};
Point(9) = {-R/Sqrt(2),R/Sqrt(2),0};
Point(10) = {-R,0,0};
Point(11) = {-R/Sqrt(2),-R/Sqrt(2),0};
Point(12) = {0,-R,0};
Point(13) = {R/Sqrt(2),-R/Sqrt(2),0};
Line(1) = {1,2};
Line(2) = {1,3};
Line(3) = {1,4};
Line(4) = {1,5};
Line(5) = {1,6};
Line(6) = {1,8};
Line(7) = {1,10};
Line(8) = {1,12};
Line(9) = {2,6};
Line(10) = {2,8};
Line(11) = {3,8};
Line(12) = {3,10};
Line(13) = {4,10};
Line(14) = {4,12};
Line(15) = {5,12};
Line(16) = {5,6};
Line(17) = {6,7};
Line(18) = {7,8};
Line(19) = {8,9};
Line(20) = {9,10};
Line(21) = {10,11};
Line(22) = {11,12};
Line(23) = {12,13};
Line(24) = {13,6};
Transfinite Curve{1:24} = 2;
Physical Curve("ENE") = {17};
Physical Curve("NNE") = {18};
Physical Curve("NNW") = {19};
Physical Curve("WNW") = {20};
Physical Curve("WSW") = {21};
Physical Curve("SSW") = {22};
Physical Curve("SSE") = {23};
Physical Curve("ESE") = {24};
Curve Loop(1) = {9,17,18,-10};
Curve Loop(2) = {11,19,20,-12};
Curve Loop(3) = {13,21,22,-14};
Curve Loop(4) = {15,23,24,-16};
Plane Surface(1) = {1};
Plane Surface(2) = {2};
Plane Surface(3) = {3};
Plane Surface(4) = {4};
Transfinite Surface{1} = {2,6,7,8};
Transfinite Surface{2} = {3,8,9,10};
Transfinite Surface{3} = {4,10,11,12};
Transfinite Surface{4} = {5,12,13,6};
Recombine Surface{1:4};
Physical Surface("Base") = {1,2,3,4};
Curve Loop(5) = {1,10,-6};
Plane Surface(5) = {5};
Physical Surface("N Even") = {5};
Curve Loop(6) = {6,-11,-2};
Plane Surface(6) = {6};
Physical Surface("N Odd") = {6};
Curve Loop(7) = {2,12,-7};
Plane Surface(7) = {7};
Physical Surface("W Even") = {7};
Curve Loop(8) = {7,-13,-3};
Plane Surface(8) = {8};
Physical Surface("W Odd") = {8};
Curve Loop(9) = {3,14,-8};
Plane Surface(9) = {9};
Physical Surface("S Even") = {9};
Curve Loop(10) = {8,-15,-4};
Plane Surface(10) = {10};
Physical Surface("S Odd") = {10};
Curve Loop(11) = {4,16,-5};
Plane Surface(11) = {11};
Physical Surface("E Even") = {11};
Curve Loop(12) = {5,-9,-1};
Plane Surface(12) = {12};
Physical Surface("E Odd") = {12};
// Generate 2D mesh
Mesh 2;
SetOrder order;
Mesh.MshFileVersion = 2.2;
Save "compass.msh";
+95
View File
@@ -0,0 +1,95 @@
MFEM mesh v1.3
#
# MFEM Geometry Types (see mesh/geom.hpp):
#
# POINT = 0
# SEGMENT = 1
# TRIANGLE = 2
# SQUARE = 3
# TETRAHEDRON = 4
# CUBE = 5
# PRISM = 6
#
dimension
2
elements
12
10 2 7 0 1
11 2 0 7 2
12 2 9 0 2
13 2 0 9 3
14 2 11 0 3
15 2 0 11 4
16 2 5 0 4
17 2 0 5 1
9 3 1 5 6 7
9 3 2 7 8 9
9 3 3 9 10 11
9 3 4 11 12 5
attribute_sets
16
"Base" 1 9
"E Even" 1 16
"E Odd" 1 17
"East" 2 16 17
"N Even" 1 10
"N Odd" 1 11
"North" 2 10 11
"Rose" 8 10 11 12 13 14 15 16 17
"Rose Even" 4 10 12 14 16
"Rose Odd" 4 11 13 15 17
"S Even" 1 14
"S Odd" 1 15
"South" 2 14 15
"W Even" 1 12
"W Odd" 1 13
"West" 2 12 13
boundary
8
1 1 5 6
2 1 6 7
3 1 7 8
4 1 8 9
5 1 9 10
6 1 10 11
7 1 11 12
8 1 12 5
bdr_attribute_sets
13
"Boundary" 8 1 2 3 4 5 6 7 8
"ENE" 1 1
"ESE" 1 8
"Eastern Boundary" 2 1 8
"NNE" 1 2
"NNW" 1 3
"Northern Boundary" 2 2 3
"SSE" 1 7
"SSW" 1 6
"Southern Boundary" 2 6 7
"WNW" 1 4
"WSW" 1 5
"Western Boundary" 2 4 5
vertices
13
2
0 0
0.14142136 0.14142136
-0.14142136 0.14142136
-0.14142136 -0.14142136
0.14142136 -0.14142136
1 0
0.70710678 0.70710678
0 1
-0.70710678 0.70710678
-1 0
-0.70710678 -0.70710678
0 -1
0.70710678 -0.70710678
mfem_mesh_end
+62
View File
@@ -0,0 +1,62 @@
$MeshFormat
2.2 0 8
$EndMeshFormat
$PhysicalNames
17
1 1 "ENE"
1 2 "NNE"
1 3 "NNW"
1 4 "WNW"
1 5 "WSW"
1 6 "SSW"
1 7 "SSE"
1 8 "ESE"
2 9 "Base"
2 10 "N Even"
2 11 "N Odd"
2 12 "W Even"
2 13 "W Odd"
2 14 "S Even"
2 15 "S Odd"
2 16 "E Even"
2 17 "E Odd"
$EndPhysicalNames
$Nodes
13
1 0 0 0
2 0.1414213562373095 0.1414213562373095 0
3 -0.1414213562373095 0.1414213562373095 0
4 -0.1414213562373095 -0.1414213562373095 0
5 0.1414213562373095 -0.1414213562373095 0
6 1 0 0
7 0.7071067811865475 0.7071067811865475 0
8 0 1 0
9 -0.7071067811865475 0.7071067811865475 0
10 -1 0 0
11 -0.7071067811865475 -0.7071067811865475 0
12 0 -1 0
13 0.7071067811865475 -0.7071067811865475 0
$EndNodes
$Elements
20
1 1 2 1 17 6 7
2 1 2 2 18 7 8
3 1 2 3 19 8 9
4 1 2 4 20 9 10
5 1 2 5 21 10 11
6 1 2 6 22 11 12
7 1 2 7 23 12 13
8 1 2 8 24 13 6
9 2 2 10 5 1 2 8
10 2 2 11 6 1 8 3
11 2 2 12 7 1 3 10
12 2 2 13 8 1 10 4
13 2 2 14 9 1 4 12
14 2 2 15 10 1 12 5
15 2 2 16 11 1 5 6
16 2 2 17 12 1 6 2
17 3 2 9 1 2 6 7 8
18 3 2 9 2 3 8 9 10
19 3 2 9 3 4 10 11 12
20 3 2 9 4 5 12 13 6
$EndElements
+6 -6
View File
@@ -43,13 +43,9 @@ list(APPEND ALL_EXE_SRCS
ex34.cpp
ex36.cpp
ex37.cpp
)
if(MFEM_USE_LAPACK)
list(APPEND ALL_EXE_SRCS
ex38.cpp
ex39.cpp
)
endif()
if (MFEM_USE_MPI)
list(APPEND ALL_EXE_SRCS
@@ -90,7 +86,8 @@ if (MFEM_USE_MPI)
ex35p.cpp
ex36p.cpp
ex37p.cpp
)
ex39p.cpp
)
endif()
# Examples that return MFEM_SKIP_RETURN_VALUE in some cases:
@@ -101,6 +98,9 @@ endif()
if (MFEM_USE_SINGLE)
list(APPEND SKIP_TESTS ex33.cpp ex33p.cpp)
endif()
if (NOT MFEM_USE_LAPACK)
list(APPEND SKIP_TESTS ex38.cpp)
endif()
# Include the source directory where mfem.hpp and mfem-performance.hpp are.
include_directories(BEFORE ${PROJECT_BINARY_DIR})
+1 -1
View File
@@ -505,7 +505,7 @@ int main(int argc, char *argv[])
{
#ifndef MFEM_USE_LAPACK
cout << "MFEM must be built with LAPACK for this example." << endl;
return EXIT_FAILURE;
return MFEM_SKIP_RETURN_VALUE;
#else
// 1. Parse he command-line options.
int ref_levels = 3;
+285
View File
@@ -0,0 +1,285 @@
// MFEM Example 39
//
// Compile with: make ex39
//
// Sample runs: ex39
// ex39 -ess "Southern Boundary"
// ex39 -src Base
//
// Description: This example code demonstrates the use of named attribute
// sets in MFEM to specify material regions, boundary regions,
// or source regions by name rather than attribute numbers. It
// also demonstrates how new named attribute sets may be created
// from arbitrary groupings of attribute numbers and used as a
// convenient shorthand to refer to those groupings in other
// portions of the application or through the command line.
//
// The particular problem being solved here is nearly the same
// as that in example 1 i.e. a simple finite element
// discretization of the Laplace problem -Delta u = 1 with
// homogeneous Dirichlet boundary conditions and, in this case,
// an inhomogeneous diffusion coefficient. The diffusion
// coefficient is given a small default value throughout the
// domain which is increased by two separate amounts in two named
// regions.
//
// This example makes use of a specific input mesh, "compass.msh",
// containing named domain and boundary regions generated by Gmsh
// and stored in their "msh" format (version 2.2). This file
// defines eight boundary regions corresponding to eight compass
// headings; "ENE", "NNE", "NNW", "WSW", "SSW", "SSE", and "ESE".
// It also defines nine domain regions; "Base", "N Even", "N Odd",
// "W Even", "W Odd", "S Even", "S Odd", "E Even", and "E Odd".
// These regions split the four compass pointers into two halves
// each and also label the remaining elements as "Base". Starting
// with these named regions we test the construction of named
// sets as well as reading and writing these named groupings from
// and to mesh files.
//
// The example highlights the use of named attribute sets for
// both subdomains and boundaries in different contexts as well
// as basic methods to create named sets from existing attributes.
#include "mfem.hpp"
#include <fstream>
#include <iostream>
using namespace std;
using namespace mfem;
int main(int argc, char *argv[])
{
// 1. Parse command-line options.
const char *mesh_file = "../data/compass.msh";
int order = 1;
string source_name = "Rose Even";
string ess_name = "Boundary";
bool visualization = true;
OptionsParser args(argc, argv);
args.AddOption(&mesh_file, "-m", "--mesh",
"Mesh file to use.");
args.AddOption(&order, "-o", "--order",
"Finite element order (polynomial degree) or -1 for"
" isoparametric space.");
args.AddOption(&source_name,"-src","--source-attr-name",
"Name of attribute set containing source.");
args.AddOption(&ess_name,"-ess","--ess-attr-name",
"Name of attribute set containing essential BC.");
args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
"--no-visualization",
"Enable or disable GLVis visualization.");
args.ParseCheck();
// 2. Read the mesh from the given mesh file. We can handle triangular,
// quadrilateral, tetrahedral, hexahedral, surface and volume meshes with
// the same code.
Mesh mesh(mesh_file, 1, 1);
int dim = mesh.Dimension();
// 3. Refine the mesh to increase the resolution. In this example we do
// 'ref_levels' of uniform refinement. We choose 'ref_levels' to be the
// largest number that gives a final mesh with no more than 50,000
// elements.
{
int ref_levels =
(int)floor(log(50000./mesh.GetNE())/log(2.)/dim);
for (int l = 0; l < ref_levels; l++)
{
mesh.UniformRefinement();
}
}
// 4a. Display attribute set names contained in the initial mesh
AttributeSets &attr_sets = mesh.attribute_sets;
AttributeSets &bdr_attr_sets = mesh.bdr_attribute_sets;
{
std::set<string> names = attr_sets.GetAttributeSetNames();
cout << "Element Attribute Set Names: ";
for (auto const &set_name : names)
{
cout << " \"" << set_name << "\"";
}
cout << endl;
std::set<string> bdr_names = bdr_attr_sets.GetAttributeSetNames();
cout << "Boundary Attribute Set Names: ";
for (auto const &bdr_set_name : bdr_names)
{
cout << " \"" << bdr_set_name << "\"";
}
cout << endl;
}
// 4b. Define new regions based on existing attribute sets
{
Array<int> & Na = attr_sets.GetAttributeSet("N Even");
Array<int> & Nb = attr_sets.GetAttributeSet("N Odd");
Array<int> & Sa = attr_sets.GetAttributeSet("S Even");
Array<int> & Sb = attr_sets.GetAttributeSet("S Odd");
Array<int> & Ea = attr_sets.GetAttributeSet("E Even");
Array<int> & Eb = attr_sets.GetAttributeSet("E Odd");
Array<int> & Wa = attr_sets.GetAttributeSet("W Even");
Array<int> & Wb = attr_sets.GetAttributeSet("W Odd");
// Create a new set spanning the North point
attr_sets.SetAttributeSet("North", Na);
attr_sets.AddToAttributeSet("North", Nb);
// Create a new set spanning the South point
attr_sets.SetAttributeSet("South", Sa);
attr_sets.AddToAttributeSet("South", Sb);
// Create a new set spanning the East point
attr_sets.SetAttributeSet("East", Ea);
attr_sets.AddToAttributeSet("East", Eb);
// Create a new set spanning the West point
attr_sets.SetAttributeSet("West", Wa);
attr_sets.AddToAttributeSet("West", Wb);
// Create a new set consisting of the "a" sides of the compass rose
attr_sets.SetAttributeSet("Rose Even", Na);
attr_sets.AddToAttributeSet("Rose Even", Sa);
attr_sets.AddToAttributeSet("Rose Even", Ea);
attr_sets.AddToAttributeSet("Rose Even", Wa);
// Create a new set consisting of the "b" sides of the compass rose
attr_sets.SetAttributeSet("Rose Odd", Nb);
attr_sets.AddToAttributeSet("Rose Odd", Sb);
attr_sets.AddToAttributeSet("Rose Odd", Eb);
attr_sets.AddToAttributeSet("Rose Odd", Wb);
// Create a new set consisting of the full compass rose
Array<int> & Ra = attr_sets.GetAttributeSet("Rose Even");
Array<int> & Rb = attr_sets.GetAttributeSet("Rose Odd");
attr_sets.SetAttributeSet("Rose", Ra);
attr_sets.AddToAttributeSet("Rose", Rb);
}
// 4c. Define new boundary regions based on existing boundary attribute sets
{
Array<int> & NNE = bdr_attr_sets.GetAttributeSet("NNE");
Array<int> & NNW = bdr_attr_sets.GetAttributeSet("NNW");
Array<int> & ENE = bdr_attr_sets.GetAttributeSet("ENE");
Array<int> & ESE = bdr_attr_sets.GetAttributeSet("ESE");
Array<int> & SSE = bdr_attr_sets.GetAttributeSet("SSE");
Array<int> & SSW = bdr_attr_sets.GetAttributeSet("SSW");
Array<int> & WNW = bdr_attr_sets.GetAttributeSet("WNW");
Array<int> & WSW = bdr_attr_sets.GetAttributeSet("WSW");
bdr_attr_sets.SetAttributeSet("Northern Boundary", NNE);
bdr_attr_sets.AddToAttributeSet("Northern Boundary", NNW);
bdr_attr_sets.SetAttributeSet("Southern Boundary", SSE);
bdr_attr_sets.AddToAttributeSet("Southern Boundary", SSW);
bdr_attr_sets.SetAttributeSet("Eastern Boundary", ENE);
bdr_attr_sets.AddToAttributeSet("Eastern Boundary", ESE);
bdr_attr_sets.SetAttributeSet("Western Boundary", WNW);
bdr_attr_sets.AddToAttributeSet("Western Boundary", WSW);
bdr_attr_sets.SetAttributeSet("Boundary",
bdr_attr_sets.GetAttributeSet
("Northern Boundary"));
bdr_attr_sets.AddToAttributeSet("Boundary",
bdr_attr_sets.GetAttributeSet
("Southern Boundary"));
bdr_attr_sets.AddToAttributeSet("Boundary",
bdr_attr_sets.GetAttributeSet
("Eastern Boundary"));
bdr_attr_sets.AddToAttributeSet("Boundary",
bdr_attr_sets.GetAttributeSet
("Western Boundary"));
}
// 5. Define a finite element space on the mesh. Here we use continuous
// Lagrange finite elements of the specified order.
H1_FECollection fec(order, mesh.Dimension());
FiniteElementSpace fespace(&mesh, &fec);
cout << "Number of finite element unknowns: "
<< fespace.GetTrueVSize() << endl;
// 6. Determine the list of true (i.e. conforming) essential boundary dofs.
// In this example, the boundary conditions are defined by marking all
// the boundary regions corresponding to the boundary attributes
// contained in the set named "ess_name" as essential (Dirichlet) and
// converting them to a list of true dofs.
Array<int> ess_tdof_list;
if (bdr_attr_sets.AttributeSetExists(ess_name))
{
Array<int> ess_bdr_marker = bdr_attr_sets.GetAttributeSetMarker(ess_name);
fespace.GetEssentialTrueDofs(ess_bdr_marker, ess_tdof_list);
}
// 7. Set up the linear form b(.) which corresponds to the right-hand side of
// the FEM linear system, which in this case is (1_s,phi_i) where phi_i
// are the basis functions in fespace and 1_s is an indicator function
// equal to 1 on the region defined by the named set "source_name" and
// zero elsewhere.
Array<int> source_marker = attr_sets.GetAttributeSetMarker(source_name);
LinearForm b(&fespace);
ConstantCoefficient one(1.0);
b.AddDomainIntegrator(new DomainLFIntegrator(one), source_marker);
b.Assemble();
// 8. Define the solution vector x as a finite element grid function
// corresponding to fespace. Initialize x with initial guess of zero,
// which satisfies the boundary conditions.
GridFunction x(&fespace);
x = 0.0;
// 9. Set up the bilinear form a(.,.) on the finite element space
// corresponding to the Laplacian operator -Delta, by adding the
// Diffusion domain integrator.
BilinearForm a(&fespace);
ConstantCoefficient defaultCoef(1.0e-6);
ConstantCoefficient baseCoef(1.0);
ConstantCoefficient roseCoef(2.0);
Array<int> base_marker = attr_sets.GetAttributeSetMarker("Base");
Array<int> rose_marker = attr_sets.GetAttributeSetMarker("Rose Even");
// Impose a very small diffusion coefficient across the entire mesh
a.AddDomainIntegrator(new DiffusionIntegrator(defaultCoef));
// Impose an additional, stronger diffusion coefficient in select regions
a.AddDomainIntegrator(new DiffusionIntegrator(baseCoef), base_marker);
a.AddDomainIntegrator(new DiffusionIntegrator(roseCoef), rose_marker);
// 10. Assemble the bilinear form and the corresponding linear system,
// applying any necessary transformations.
a.Assemble();
SparseMatrix A;
Vector B, X;
a.FormLinearSystem(ess_tdof_list, x, b, A, X, B);
cout << "Size of linear system: " << A.Height() << endl;
// 11. Solve the system using PCG with symmetric Gauss-Seidel preconditioner.
GSSmoother M(A);
PCG(A, M, B, X, 1, 800, 1e-12, 0.0);
// 12. Recover the solution as a finite element grid function.
a.RecoverFEMSolution(X, b, x);
// 13. Save the refined mesh and the solution. This output can be viewed
// later using GLVis: "glvis -m refined.mesh -g sol.gf".
mesh.Save("refined.mesh");
x.Save("sol.gf");
// 14. Send the solution by socket to a GLVis server.
if (visualization)
{
char vishost[] = "localhost";
int visport = 19916;
socketstream sol_sock(vishost, visport);
sol_sock.precision(8);
sol_sock << "solution\n" << mesh << x << "keys Rjmm" << flush;
}
return 0;
}
+314
View File
@@ -0,0 +1,314 @@
// MFEM Example 39 - Parallel Version
//
// Compile with: make ex39p
//
// Sample runs: mpirun -np 4 ex39p
// mpirun -np 4 ex39p -ess "Southern Boundary"
// mpirun -np 4 ex39p -src Base
//
// Description: This example code demonstrates the use of named attribute
// sets in MFEM to specify material regions, boundary regions,
// or source regions by name rather than attribute numbers. It
// also demonstrates how new named attribute sets may be created
// from arbitrary groupings of attribute numbers and used as a
// convenient shorthand to refer to those groupings in other
// portions of the application or through the command line.
//
// The particular problem being solved here is nearly the same
// as that in example 1 i.e. a simple finite element
// discretization of the Laplace problem -Delta u = 1 with
// homogeneous Dirichlet boundary conditions and, in this case,
// an inhomogeneous diffusion coefficient. The diffusion
// coefficient is given a small default value throughout the
// domain which is increased by two separate amounts in two named
// regions.
//
// This example makes use of a specific input mesh, "compass.msh",
// containing named domain and boundary regions generated by Gmsh
// and stored in their "msh" format (version 2.2). This file
// defines eight boundary regions corresponding to eight compass
// headings; "ENE", "NNE", "NNW", "WSW", "SSW", "SSE", and "ESE".
// It also defines nine domain regions; "Base", "N Even", "N Odd",
// "W Even", "W Odd", "S Even", "S Odd", "E Even", and "E Odd".
// These regions split the four compass pointers into two halves
// each and also label the remaining elements as "Base". Starting
// with these named regions we test the construction of named
// sets as well as reading and writing these named groupings from
// and to mesh files.
//
// The example highlights the use of named attribute sets for
// both subdomains and boundaries in different contexts as well
// as basic methods to create named sets from existing attributes.
#include "mfem.hpp"
#include <fstream>
#include <iostream>
using namespace std;
using namespace mfem;
int main(int argc, char *argv[])
{
// 1. Initialize MPI and HYPRE.
Mpi::Init();
Hypre::Init();
// 2. Parse command-line options.
const char *mesh_file = "../data/compass.msh";
int order = 1;
string source_name = "Rose Even";
string ess_name = "Boundary";
bool visualization = true;
OptionsParser args(argc, argv);
args.AddOption(&mesh_file, "-m", "--mesh",
"Mesh file to use.");
args.AddOption(&order, "-o", "--order",
"Finite element order (polynomial degree) or -1 for"
" isoparametric space.");
args.AddOption(&source_name,"-src","--source-attr-name",
"Name of attribute set containing source.");
args.AddOption(&ess_name,"-ess","--ess-attr-name",
"Name of attribute set containing essential BC.");
args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
"--no-visualization",
"Enable or disable GLVis visualization.");
args.ParseCheck();
// 3. Read the serial mesh from the given mesh file.
Mesh mesh(mesh_file, 1, 1);
int dim = mesh.Dimension();
// 4. Refine the serial mesh on all processors to increase the resolution. In
// this example we do 'ref_levels' of uniform refinement. We choose
// 'ref_levels' to be the largest number that gives a final mesh with no
// more than 10,000 elements.
{
int ref_levels =
(int)floor(log(10000./mesh.GetNE())/log(2.)/dim);
for (int l = 0; l < ref_levels; l++)
{
mesh.UniformRefinement();
}
}
// 5. Define a parallel mesh by a partitioning of the serial mesh. Refine
// this mesh further in parallel to increase the resolution. Once the
// parallel mesh is defined, the serial mesh can be deleted.
ParMesh pmesh(MPI_COMM_WORLD, mesh);
mesh.Clear();
{
int par_ref_levels = 2;
for (int l = 0; l < par_ref_levels; l++)
{
pmesh.UniformRefinement();
}
}
// 6a. Display attribute set names contained in the initial mesh
AttributeSets &attr_sets = pmesh.attribute_sets;
AttributeSets &bdr_attr_sets = pmesh.bdr_attribute_sets;
if (Mpi::Root())
{
std::set<string> names = attr_sets.GetAttributeSetNames();
cout << "Element Attribute Set Names: ";
for (auto const &set_name : names)
{
cout << " \"" << set_name << "\"";
}
cout << endl;
std::set<string> bdr_names = bdr_attr_sets.GetAttributeSetNames();
cout << "Boundary Attribute Set Names: ";
for (auto const &bdr_set_name : bdr_names)
{
cout << " \"" << bdr_set_name << "\"";
}
cout << endl;
}
// 6b. Define new regions based on existing attribute sets
{
Array<int> & Na = attr_sets.GetAttributeSet("N Even");
Array<int> & Nb = attr_sets.GetAttributeSet("N Odd");
Array<int> & Sa = attr_sets.GetAttributeSet("S Even");
Array<int> & Sb = attr_sets.GetAttributeSet("S Odd");
Array<int> & Ea = attr_sets.GetAttributeSet("E Even");
Array<int> & Eb = attr_sets.GetAttributeSet("E Odd");
Array<int> & Wa = attr_sets.GetAttributeSet("W Even");
Array<int> & Wb = attr_sets.GetAttributeSet("W Odd");
// Create a new set spanning the North point
attr_sets.SetAttributeSet("North", Na);
attr_sets.AddToAttributeSet("North", Nb);
// Create a new set spanning the South point
attr_sets.SetAttributeSet("South", Sa);
attr_sets.AddToAttributeSet("South", Sb);
// Create a new set spanning the East point
attr_sets.SetAttributeSet("East", Ea);
attr_sets.AddToAttributeSet("East", Eb);
// Create a new set spanning the West point
attr_sets.SetAttributeSet("West", Wa);
attr_sets.AddToAttributeSet("West", Wb);
// Create a new set consisting of the "a" sides of the compass rose
attr_sets.SetAttributeSet("Rose Even", Na);
attr_sets.AddToAttributeSet("Rose Even", Sa);
attr_sets.AddToAttributeSet("Rose Even", Ea);
attr_sets.AddToAttributeSet("Rose Even", Wa);
// Create a new set consisting of the "b" sides of the compass rose
attr_sets.SetAttributeSet("Rose Odd", Nb);
attr_sets.AddToAttributeSet("Rose Odd", Sb);
attr_sets.AddToAttributeSet("Rose Odd", Eb);
attr_sets.AddToAttributeSet("Rose Odd", Wb);
// Create a new set consisting of the full compass rose
Array<int> & Ra = attr_sets.GetAttributeSet("Rose Even");
Array<int> & Rb = attr_sets.GetAttributeSet("Rose Odd");
attr_sets.SetAttributeSet("Rose", Ra);
attr_sets.AddToAttributeSet("Rose", Rb);
}
// 6c. Define new boundary regions based on existing boundary attribute sets
{
Array<int> & NNE = bdr_attr_sets.GetAttributeSet("NNE");
Array<int> & NNW = bdr_attr_sets.GetAttributeSet("NNW");
Array<int> & ENE = bdr_attr_sets.GetAttributeSet("ENE");
Array<int> & ESE = bdr_attr_sets.GetAttributeSet("ESE");
Array<int> & SSE = bdr_attr_sets.GetAttributeSet("SSE");
Array<int> & SSW = bdr_attr_sets.GetAttributeSet("SSW");
Array<int> & WNW = bdr_attr_sets.GetAttributeSet("WNW");
Array<int> & WSW = bdr_attr_sets.GetAttributeSet("WSW");
bdr_attr_sets.SetAttributeSet("Northern Boundary", NNE);
bdr_attr_sets.AddToAttributeSet("Northern Boundary", NNW);
bdr_attr_sets.SetAttributeSet("Southern Boundary", SSE);
bdr_attr_sets.AddToAttributeSet("Southern Boundary", SSW);
bdr_attr_sets.SetAttributeSet("Eastern Boundary", ENE);
bdr_attr_sets.AddToAttributeSet("Eastern Boundary", ESE);
bdr_attr_sets.SetAttributeSet("Western Boundary", WNW);
bdr_attr_sets.AddToAttributeSet("Western Boundary", WSW);
bdr_attr_sets.SetAttributeSet("Boundary",
bdr_attr_sets.GetAttributeSet
("Northern Boundary"));
bdr_attr_sets.AddToAttributeSet("Boundary",
bdr_attr_sets.GetAttributeSet
("Southern Boundary"));
bdr_attr_sets.AddToAttributeSet("Boundary",
bdr_attr_sets.GetAttributeSet
("Eastern Boundary"));
bdr_attr_sets.AddToAttributeSet("Boundary",
bdr_attr_sets.GetAttributeSet
("Western Boundary"));
}
// 7. Define a parallel finite element space on the parallel mesh. Here we
// use continuous Lagrange finite elements of the specified order. If
// order < 1, we instead use an isoparametric/isogeometric space.
H1_FECollection fec(order, dim);
ParFiniteElementSpace fespace(&pmesh, &fec);
HYPRE_BigInt size = fespace.GlobalTrueVSize();
if (Mpi::Root())
{
cout << "Number of finite element unknowns: " << size << endl;
}
// 8. Determine the list of true (i.e. parallel conforming) essential
// boundary dofs. In this example, the boundary conditions are defined
// by marking all the boundary regions corresponding to the boundary
// attributes contained in the set named "ess_name" as essential
// (Dirichlet) and converting them to a list of true dofs.
Array<int> ess_tdof_list;
if (bdr_attr_sets.AttributeSetExists(ess_name))
{
Array<int> ess_bdr_marker = bdr_attr_sets.GetAttributeSetMarker(ess_name);
fespace.GetEssentialTrueDofs(ess_bdr_marker, ess_tdof_list);
}
// 9. Set up the parallel linear form b(.) which corresponds to the
// right-hand side of the FEM linear system, which in this case is
// (1_s,phi_i) where phi_i are the basis functions in fespace and 1_s
// is an indicator function equal to 1 on the region defined by the
// named set "source_name" and zero elsewhere.
Array<int> source_marker = attr_sets.GetAttributeSetMarker(source_name);
ParLinearForm b(&fespace);
ConstantCoefficient one(1.0);
b.AddDomainIntegrator(new DomainLFIntegrator(one), source_marker);
b.Assemble();
// 10. Define the solution vector x as a parallel finite element grid
// function corresponding to fespace. Initialize x with initial guess of
// zero, which satisfies the boundary conditions.
ParGridFunction x(&fespace);
x = 0.0;
// 11. Set up the parallel bilinear form a(.,.) on the finite element space
// corresponding to the Laplacian operator -Delta, by adding the
// Diffusion domain integrator.
ParBilinearForm a(&fespace);
ConstantCoefficient defaultCoef(1.0e-6);
ConstantCoefficient baseCoef(1.0);
ConstantCoefficient roseCoef(2.0);
Array<int> base_marker = attr_sets.GetAttributeSetMarker("Base");
Array<int> rose_marker = attr_sets.GetAttributeSetMarker("Rose Even");
// Impose a very small diffusion coefficient across the entire mesh
a.AddDomainIntegrator(new DiffusionIntegrator(defaultCoef));
// Impose an additional, stronger diffusion coefficient in select regions
a.AddDomainIntegrator(new DiffusionIntegrator(baseCoef), base_marker);
a.AddDomainIntegrator(new DiffusionIntegrator(roseCoef), rose_marker);
// 12. Assemble the parallel bilinear form and the corresponding linear
// system, applying any necessary transformations.
a.Assemble();
HypreParMatrix A;
Vector B, X;
a.FormLinearSystem(ess_tdof_list, x, b, A, X, B);
// 13. Solve the system using PCG with hypre's BoomerAMG preconditioner.
HypreBoomerAMG M(A);
CGSolver cg(MPI_COMM_WORLD);
cg.SetRelTol(1e-12);
cg.SetMaxIter(2000);
cg.SetPrintLevel(1);
cg.SetPreconditioner(M);
cg.SetOperator(A);
cg.Mult(B, X);
// 14. Recover the parallel grid function corresponding to X. This is the
// local finite element solution on each processor.
a.RecoverFEMSolution(X, b, x);
// 15. Save the refined mesh and the solution in parallel. This output can
// be viewed later using GLVis: "glvis -np <np> -m mesh -g sol".
pmesh.Save("mesh");
x.Save("sol");
// 16. Send the solution by socket to a GLVis server.
if (visualization)
{
char vishost[] = "localhost";
int visport = 19916;
int num_procs = Mpi::WorldSize();
int myid = Mpi::WorldRank();
socketstream sol_sock(vishost, visport);
sol_sock << "parallel " << num_procs << " " << myid << "\n";
sol_sock.precision(8);
sol_sock << "solution\n" << pmesh << x << "keys Rjmm" << flush;
}
return 0;
}
+2 -2
View File
@@ -23,11 +23,11 @@ MFEM_LIB_FILE = mfem_is_not_built
SEQ_EXAMPLES = ex0 ex1 ex2 ex3 ex4 ex5 ex6 ex7 ex8 ex9 ex10 ex14 ex15 ex16 \
ex17 ex18 ex19 ex20 ex21 ex22 ex23 ex24 ex25 ex26 ex27 ex28 ex29 ex30 \
ex31 ex33 ex34 ex36 ex37
ex31 ex33 ex34 ex36 ex37 ex38 ex39
PAR_EXAMPLES = ex0p ex1p ex2p ex3p ex4p ex5p ex6p ex7p ex8p ex9p ex10p ex11p \
ex12p ex13p ex14p ex15p ex16p ex17p ex18p ex19p ex20p ex21p ex22p ex24p \
ex25p ex26p ex27p ex28p ex29p ex30p ex31p ex32p ex33p ex34p ex35p ex36p \
ex37p
ex37p ex39p
SEQ_DEVICE_EXAMPLES = ex1 ex3 ex4 ex5 ex6 ex9 ex22 ex24 ex25 ex26 ex34
PAR_DEVICE_EXAMPLES = ex1p ex2p ex3p ex4p ex5p ex6p ex7p ex9p ex13p ex22p \
ex24p ex25p ex26p ex34p ex35p
+1
View File
@@ -36,6 +36,7 @@ list(APPEND SRCS
list(APPEND HDRS
annotation.hpp
array.hpp
arrays_by_name.hpp
backends.hpp
binaryio.hpp
cuda.hpp
+291
View File
@@ -0,0 +1,291 @@
// Copyright (c) 2010-2024, 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.
#ifndef MFEM_ARRAYS_BY_NAME
#define MFEM_ARRAYS_BY_NAME
#include "../config/config.hpp"
#include "array.hpp"
#include <iostream>
#include <map>
#include <set>
#include <string>
namespace mfem
{
/**
Container class for storing arrays indexed by strings.
The Array<T> objects stored within this container must all be based on the
same underlying generic type T, which must be a trivial type, see
`std::is_trivial`.
In order to provide some level of protection against typos this class will
not create new named arrays when access to unrecognized names is requested.
New named arrays must be explicitly created using `CreateArray()`. To
facilitate this behavior and avoid such errors the method `EntryExists()` is
provided.
This container does not store pointers to pre-existing arrays. It will copy
or move entries as appropriate from existing Array<T> objects into new
Array<T> objects stored within this container.
*/
template <class T>
class ArraysByName
{
protected:
/// Reusing STL map iterators
using container = std::map<std::string,Array<T> >;
using iterator = typename container::iterator;
using const_iterator = typename container::const_iterator;
/// Map containing the data sorted alphabetically by name
container data;
public:
/// Default constructor
ArraysByName() = default;
/// Copy constructor: deep copy from @a src
ArraysByName(const ArraysByName &src) = default;
/// Move constructor
ArraysByName(ArraysByName &&src) noexcept = default;
/// Return the number of named arrays in the container
int Size() const { return data.size(); }
/// Return an STL set of strings giving the names of the arrays
inline std::set<std::string> GetNames() const;
/// @brief Return true if an array with the given name is present in the
/// container
inline bool EntryExists(const std::string &name) const;
/// @brief Reference access to the named entry.
///
/// @note Passing a name for a nonexistent array will print an error
/// message and halt execution. This is intended to call attention to
/// possible typos or other errors. To handle such errors more gracefully
/// consider first calling EntryExists.
inline Array<T> &operator[](const std::string &name);
/// @brief Const reference access to the named entry.
///
/// @note Passing a name for a nonexistent array will print an error
/// message and halt execution. This is intended to call attention to
/// possible typos or other errors. To handle such errors more gracefully
/// consider first calling EntryExists.
inline const Array<T> &operator[](const std::string &name) const;
/// @brief Create a new empty array with the given name
///
/// @note Passing a name for an already existent array will print an error
/// message and halt execution. This is intended to call attention to
/// possible typos or other errors. To handle such errors more gracefully
/// consider first calling EntryExists.
inline Array<T> &CreateArray(const std::string &name);
/// Delete all named arrays from the container
inline void DeleteAll();
/// @brief Delete the named array from the container
///
/// @note Passing a name for a nonexistent array will print an error
/// message and halt execution. This is intended to call attention to
/// possible typos or other errors. To handle such errors more gracefully
/// consider first calling EntryExists.
inline void DeleteArray(const std::string &name);
/// Copy assignment operator: deep copy from 'src'.
ArraysByName<T> &operator=(const ArraysByName<T> &src) = default;
/// Move assignment operator
ArraysByName<T> &operator=(ArraysByName<T> &&src) noexcept = default;
/// @brief Print the contents of the container to an output stream
///
/// @note Each array will be printed on at least three lines; the name on
/// one line, the length of the associated array, lastly the array contents
/// with @a width entries per line. A specific number of entries per line
/// can be used by changing the @a width argument.
inline void Print(std::ostream &out = mfem::out, int width = -1) const;
/// @brief Load the contents of the container from an input stream
///
/// @note This method will not first empty the container. First call
/// DeleteAll if this behavior is needed.
void Load(std::istream &in);
/// Sort each named array in the container
inline void SortAll();
/// @brief Remove duplicates from each, previously sorted, named array
///
/// @note Identical entries may exist in multiple arrays but will only occur
/// at most once in each array.
inline void UniqueAll();
/// STL-like begin. Returns pointer to the first entry of the container.
iterator begin() { return data.begin(); }
/// STL-like end. Returns pointer after the last entry of the container.
iterator end() { return data.end(); }
/// @brief STL-like begin. Returns const pointer to the first entry of the
/// container.
const_iterator begin() const { return data.cbegin(); }
/// @brief STL-like end. Returns const pointer after the last entry of the
/// container.
const_iterator end() const { return data.cend(); }
};
template <class T>
inline bool operator==(const ArraysByName<T> &LHS, const ArraysByName<T> &RHS)
{
if ( LHS.Size() != RHS.Size() ) { return false; }
for (auto it1 = LHS.begin(), it2 = RHS.begin();
it1 != LHS.end() && it2 != RHS.end(); it1++, it2++)
{
if (it1->first != it2->first) { return false; }
if (it1->second != it2->second) { return false; }
}
return true;
}
template<class T>
inline std::set<std::string> ArraysByName<T>::GetNames() const
{
std::set<std::string> names;
for (auto const &entry : data)
{
names.insert(entry.first);
}
return names;
}
template<class T>
inline bool ArraysByName<T>::EntryExists(const std::string &name) const
{
return data.find(name) != data.end();
}
template<class T>
inline Array<T> &ArraysByName<T>::operator[](const std::string &name)
{
MFEM_VERIFY( data.find(name) != data.end(),
"Access to unknown named array \"" << name << "\"");
return data[name];
}
template<class T>
inline const Array<T> &ArraysByName<T>::operator[](const std::string &name)
const
{
MFEM_VERIFY( data.find(name) != data.end(),
"Access to unknown named array \"" << name << "\"");
return data.at(name);
}
template<class T>
inline Array<T> &ArraysByName<T>::CreateArray(const std::string &name)
{
MFEM_VERIFY( data.find(name) == data.end(),
"Named array \"" << name << "\" already exists");
Array<T> empty_array;
data.insert(std::pair<std::string,Array<T> >(name,empty_array));
return data[name];
}
template<class T>
inline void ArraysByName<T>::DeleteAll()
{
data.clear();
}
template<class T>
inline void ArraysByName<T>::DeleteArray(const std::string &name)
{
MFEM_VERIFY( data.find(name) != data.end(),
"Attempting to delete unknown named array \"" << name << "\"");
data.erase(name);
}
template <class T>
inline void ArraysByName<T>::SortAll()
{
for (auto &a : data)
{
a.second.Sort();
}
}
template <class T>
inline void ArraysByName<T>::UniqueAll()
{
for (auto &a : data)
{
a.second.Unique();
}
}
template <class T>
inline void ArraysByName<T>::Print(std::ostream &os, int width) const
{
os << data.size() << '\n';
for (auto const &it : data)
{
os << '"' << it.first << '"' << '\n' << it.second.Size() << '\n';
it.second.Print(os, width > 0 ? width : it.second.Size());
}
}
template <class T>
void ArraysByName<T>::Load(std::istream &in)
{
int NumArrays;
in >> NumArrays;
std::string ArrayLine, ArrayName;
for (int i=0; i < NumArrays; i++)
{
in >> std::ws;
getline(in, ArrayLine);
std::size_t q0 = ArrayLine.find('"');
std::size_t q1 = ArrayLine.rfind('"');
if (q0 != std::string::npos && q1 > q0)
{
// Locate set name between first and last double quote
ArrayName = ArrayLine.substr(q0+1,q1-q0-1);
}
else
{
// If no double quotes found locate set name using white space
q1 = ArrayLine.find(' ');
ArrayName = ArrayLine.substr(0,q1-1);
}
// Ignore the remainder of the line which may contain explanatory comments
data[ArrayName].Load(in, 0);
}
}
}
#endif
+2
View File
@@ -10,6 +10,7 @@
# CONTRIBUTING.md for details.
set(SRCS
attribute_sets.cpp
element.cpp
gmsh.cpp
hexahedron.cpp
@@ -34,6 +35,7 @@ set(SRCS
)
set(HDRS
attribute_sets.hpp
element.hpp
gmsh.hpp
hexahedron.hpp
+121
View File
@@ -0,0 +1,121 @@
// Copyright (c) 2010-2024, 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.
#include "attribute_sets.hpp"
namespace mfem
{
AttributeSets::AttributeSets(const Array<int> &attr)
: attributes(attr)
{}
void AttributeSets::Copy(AttributeSets &copy) const
{
copy.attr_sets = attr_sets;
}
bool AttributeSets::SetsExist() const
{
return attr_sets.Size() > 0;
}
std::set<std::string> AttributeSets::GetAttributeSetNames() const
{
return attr_sets.GetNames();
}
bool AttributeSets::AttributeSetExists(const std::string &name) const
{
return attr_sets.EntryExists(name);
}
Array<int> & AttributeSets::CreateAttributeSet(const std::string &set_name)
{
return attr_sets.CreateArray(set_name);
}
void AttributeSets::DeleteAttributeSet(const std::string &set_name)
{
attr_sets.DeleteArray(set_name);
}
void AttributeSets::SetAttributeSet(const std::string &set_name,
const Array<int> &attr)
{
if (!attr_sets.EntryExists(set_name))
{
attr_sets.CreateArray(set_name);
}
attr_sets[set_name] = attr;
attr_sets[set_name].Sort();
attr_sets[set_name].Unique();
}
void AttributeSets::AddToAttributeSet(const std::string &set_name, int attr)
{
attr_sets[set_name].Append(attr);
attr_sets[set_name].Sort();
attr_sets[set_name].Unique();
}
void AttributeSets::AddToAttributeSet(const std::string &set_name,
const Array<int> &attr)
{
attr_sets[set_name].Append(attr);
attr_sets[set_name].Sort();
attr_sets[set_name].Unique();
}
void AttributeSets::RemoveFromAttributeSet(const std::string &set_name,
int attr)
{
if (!attr_sets.EntryExists(set_name))
{
mfem::err << "Unrecognized attribute set name \"" << set_name
<< "\" in AttributeSets::RemoveFromAttributeSet" << std::endl;
}
Array<int> &attr_set = attr_sets[set_name];
attr_set.DeleteFirst(attr);
}
void AttributeSets::Print(std::ostream &os, int width) const
{
attr_sets.Print(os, width > 0 ? width : def_width);
}
Array<int> & AttributeSets::GetAttributeSet(const std::string & set_name)
{
return attr_sets[set_name];
}
Array<int> AttributeSets::GetAttributeSetMarker(const std::string & set_name)
{
return AttrToMarker(attributes.Max(), GetAttributeSet(set_name));
}
Array<int> AttributeSets::AttrToMarker(int max_attr, const Array<int> &attrs)
{
MFEM_ASSERT(attrs.Max() <= max_attr, "Invalid attribute number present.");
Array<int> marker(max_attr);
marker = 0;
for (auto const &attr : attrs)
{
MFEM_VERIFY(attr > 0, "Attribute number less than one!");
marker[attr-1] = 1;
}
return marker;
}
} // namespace mfem
+156
View File
@@ -0,0 +1,156 @@
// Copyright (c) 2010-2024, 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.
#ifndef MFEM_ATTRIBUTE_SETS
#define MFEM_ATTRIBUTE_SETS
#include "../config/config.hpp"
#include "../general/arrays_by_name.hpp"
#include <iostream>
#include <map>
#include <set>
#include <string>
namespace mfem
{
class AttributeSets
{
private:
const Array<int> & attributes;
const int def_width = 10;
public:
/// Named sets of attributes
ArraysByName<int> attr_sets;
AttributeSets(const Array<int> &attr);
/// @brief Create a copy of the internal data to the provided @a copy.
void Copy(AttributeSets &copy) const;
/// @brief Return true if any named sets are currently defined
bool SetsExist() const;
/// @brief Return all attribute set names as an STL set
std::set<std::string> GetAttributeSetNames() const;
/// @brief Return true is the named attribute set is present
bool AttributeSetExists(const std::string &name) const;
/// @brief Create an empty named attribute set
Array<int> & CreateAttributeSet(const std::string &set_name);
/// @brief Delete a named attribute set
void DeleteAttributeSet(const std::string &set_name);
/// @brief Create a new attribute set
/**
@param[in] set_name The name of the new set
@param[in] attr An array of attribute numbers making up the new set
@note If an attribute set matching this name already exists, that set
will be replaced with this new attribute set.
@note The attribute numbers are not checked for validity or
existence within the mesh.
*/
void SetAttributeSet(const std::string &set_name, const Array<int> &attr);
/// @brief Add a single entry to an existing attribute set
/**
@param[in] set_name The name of the set being augmented
@param[in] attr A single attribute number to be inserted in the set
@note If the named set does not exist an error message will be printed
and execution will halt. `AttributeSetExists()` may be used to verify
existence of a named set.
@note Duplicate entries will be ignored and the resulting sets will be
sorted.
*/
void AddToAttributeSet(const std::string &set_name, int attr);
/// @brief Add an array of entries to an existing attribute set
/**
@param[in] set_name The name of the set being augmented
@param[in] attr Array of attribute numbers to be inserted in the set
@note If the named set does not exist an error message will be printed
and execution will halt. `AttributeSetExists()` may be used to verify
existence of a named set.
@note Duplicate entries will be ignored and the resulting sets will be
sorted.
*/
void AddToAttributeSet(const std::string &set_name, const Array<int> &attr);
/// @brief Remove a single entry from an existing attribute set
/**
@param[in] set_name The name of the set being modified
@param[in] attr A single attribute number to be removed from the set
@note If the named set does not exist an error message will be printed
and execution will halt. `AttributeSetExists()` may be used to verify
existence of a named set.
@note If @a attr is not a member of the named set the set will not
be modified and no error will occur.
*/
void RemoveFromAttributeSet(const std::string &set_name, int attr);
/// @brief Print the contents of the container to an output stream
///
/// @note The array entries will contain 10 entries per line. A specific
/// number of entries per line can be used by changing the @a width argument.
void Print(std::ostream &out = mfem::out, int width = -1) const;
/// @brief Access a named attribute set
/**
@param[in] set_name The name of the set being accessed
@note If the named set does not exist an error message will be printed
and execution will halt. `AttributeSetExists()` may be used to verify
existence of a named set.
@note The reference returned by this method can be invalidated by
subsequent calls to SetAttributeSet, ClearAttributeSet, or
RemoveFromAttributeSet. AddToAttributeSet should not invalidate this
reference.
*/
Array<int> & GetAttributeSet(const std::string & set_name);
/// @brief Return a marker array corresponding to a named attribute set
/**
@param[in] set_name The name of the set being accessed
@note If the named set does not exist an error message will be printed
and execution will halt. `AttributeSetExists()` may be used to verify
existence of a named set.
*/
Array<int> GetAttributeSetMarker(const std::string & set_name);
/// @brief Prepares a marker array corresponding to an array of element
/// attributes
/**
@param[in] max_attr Number of entries to create in the @a marker array
@param[in] attrs An array of attribute numbers which should be
activated
The returned marker array will be of size @a max_attr and it will contain
only zeroes and ones. Ones indicate which attribute numbers are present
in the @a attrs array.
*/
static Array<int> AttrToMarker(int max_attr, const Array<int> &attrs);
};
} // namespace mfem
#endif
+37 -5
View File
@@ -4084,6 +4084,7 @@ void Mesh::Make1D(int n, real_t sx)
}
Mesh::Mesh(const Mesh &mesh, bool copy_nodes)
: attribute_sets(attributes), bdr_attribute_sets(bdr_attributes)
{
Dim = mesh.Dim;
spaceDim = mesh.spaceDim;
@@ -4157,6 +4158,10 @@ Mesh::Mesh(const Mesh &mesh, bool copy_nodes)
mesh.attributes.Copy(attributes);
mesh.bdr_attributes.Copy(bdr_attributes);
// Copy attribute and bdr_attribute names
mesh.attribute_sets.Copy(attribute_sets);
mesh.bdr_attribute_sets.Copy(bdr_attribute_sets);
// Deep copy the NURBSExtension.
#ifdef MFEM_USE_MPI
ParNURBSExtension *pNURBSext =
@@ -4300,6 +4305,7 @@ Mesh Mesh::MakeRefined(Mesh &orig_mesh, const Array<int> &ref_factors,
Mesh::Mesh(const std::string &filename, int generate_edges, int refine,
bool fix_orientation)
: attribute_sets(attributes), bdr_attribute_sets(bdr_attributes)
{
// Initialization as in the default constructor
SetEmpty();
@@ -4318,6 +4324,7 @@ Mesh::Mesh(const std::string &filename, int generate_edges, int refine,
Mesh::Mesh(std::istream &input, int generate_edges, int refine,
bool fix_orientation)
: attribute_sets(attributes), bdr_attribute_sets(bdr_attributes)
{
SetEmpty();
Load(input, generate_edges, refine, fix_orientation);
@@ -4353,6 +4360,7 @@ Mesh::Mesh(real_t *vertices_, int num_vertices,
int *boundary_indices, Geometry::Type boundary_type,
int *boundary_attributes, int num_boundary_elements,
int dimension, int space_dimension)
: attribute_sets(attributes), bdr_attribute_sets(bdr_attributes)
{
if (space_dimension == -1)
{
@@ -4536,6 +4544,7 @@ void Mesh::Loader(std::istream &input, int generate_edges,
int mfem_version = 0;
if (mesh_type == "MFEM mesh v1.0") { mfem_version = 10; } // serial
else if (mesh_type == "MFEM mesh v1.2") { mfem_version = 12; } // parallel
else if (mesh_type == "MFEM mesh v1.3") { mfem_version = 13; } // attr sets
// MFEM nonconforming mesh format
// (NOTE: previous v1.1 is now under this branch for backward compatibility)
@@ -4549,7 +4558,7 @@ void Mesh::Loader(std::istream &input, int generate_edges,
// section in the stream. A user provided parse tag can also be provided
// via the arguments. For example, if this is called from parallel mesh
// object, it can indicate to read until parallel mesh section begins.
if (mfem_version == 12 && parse_tag.empty())
if (mfem_version >= 12 && parse_tag.empty())
{
parse_tag = "mfem_mesh_end";
}
@@ -4707,7 +4716,7 @@ void Mesh::Loader(std::istream &input, int generate_edges,
// If a parse tag was supplied, keep reading the stream until the tag is
// encountered.
if (mfem_version == 12)
if (mfem_version >= 12)
{
string line;
do
@@ -4737,6 +4746,7 @@ void Mesh::Loader(std::istream &input, int generate_edges,
}
Mesh::Mesh(Mesh *mesh_array[], int num_pieces)
: attribute_sets(attributes), bdr_attribute_sets(bdr_attributes)
{
int i, j, ie, ib, iv, *v, nv;
Element *el;
@@ -4879,6 +4889,7 @@ Mesh::Mesh(Mesh *mesh_array[], int num_pieces)
}
Mesh::Mesh(Mesh *orig_mesh, int ref_factor, int ref_type)
: attribute_sets(attributes), bdr_attribute_sets(bdr_attributes)
{
Array<int> ref_factors(orig_mesh->GetNE());
ref_factors = ref_factor;
@@ -10349,6 +10360,7 @@ void Mesh::InitFromNCMesh(const NCMesh &ncmesh_)
}
Mesh::Mesh(const NCMesh &ncmesh_)
: attribute_sets(attributes), bdr_attribute_sets(bdr_attributes)
{
Init();
InitTables();
@@ -11332,8 +11344,16 @@ void Mesh::Printer(std::ostream &os, std::string section_delimiter,
}
// serial/parallel conforming mesh format
os << (section_delimiter.empty()
? "MFEM mesh v1.0\n" : "MFEM mesh v1.2\n");
const bool set_names = attribute_sets.SetsExist() ||
bdr_attribute_sets.SetsExist();
os << (!set_names && section_delimiter.empty()
? "MFEM mesh v1.0\n" :
(!set_names ? "MFEM mesh v1.2\n" : "MFEM mesh v1.3\n"));
if (set_names && section_delimiter.empty())
{
section_delimiter = "mfem_mesh_end";
}
// optional
if (!comments.empty()) { os << '\n' << comments << '\n'; }
@@ -11358,12 +11378,24 @@ void Mesh::Printer(std::ostream &os, std::string section_delimiter,
PrintElement(elements[i], os);
}
if (set_names)
{
os << "\nattribute_sets\n";
attribute_sets.Print(os);
}
os << "\nboundary\n" << NumOfBdrElements << '\n';
for (i = 0; i < NumOfBdrElements; i++)
{
PrintElement(boundary[i], os);
}
if (set_names)
{
os << "\nbdr_attribute_sets\n";
bdr_attribute_sets.Print(os);
}
os << "\nvertices\n" << NumOfVertices << '\n';
if (Nodes == NULL)
{
@@ -11387,7 +11419,7 @@ void Mesh::Printer(std::ostream &os, std::string section_delimiter,
if (!section_delimiter.empty())
{
os << section_delimiter << endl; // only with format v1.2
os << section_delimiter << endl; // only with formats v1.2 and above
}
}
+13 -1
View File
@@ -15,6 +15,7 @@
#include "../config/config.hpp"
#include "../general/stable3d.hpp"
#include "../general/globals.hpp"
#include "attribute_sets.hpp"
#include "triangle.hpp"
#include "tetrahedron.hpp"
#include "vertex.hpp"
@@ -277,6 +278,12 @@ public:
/// A list of all unique boundary attributes used by the Mesh.
Array<int> bdr_attributes;
/// Named sets of element attributes
AttributeSets attribute_sets;
/// Named sets of boundary element attributes
AttributeSets bdr_attribute_sets;
NURBSExtension *NURBSext; ///< Optional NURBS mesh extension.
NCMesh *ncmesh; ///< Optional nonconforming mesh extension.
Array<GeometricFactors*> geom_factors; ///< Optional geometric factors.
@@ -632,7 +639,8 @@ public:
/// a variety of common forms. For more specialized constructors see
/// @ref mfem_Mesh_named_ctors "Named mesh constructors".
/// @{
Mesh() { SetEmpty(); }
Mesh() : attribute_sets(attributes), bdr_attribute_sets(bdr_attributes)
{ SetEmpty(); }
/** Copy constructor. Performs a deep copy of (almost) all data, so that the
source mesh can be modified (e.g. deleted, refined) without affecting the
@@ -674,6 +682,7 @@ public:
themselves can later be added using methods from the
@ref mfem_Mesh_construction "Mesh construction" group. */
Mesh(int Dim_, int NVert, int NElem, int NBdrElem = 0, int spaceDim_ = -1)
: attribute_sets(attributes), bdr_attribute_sets(bdr_attributes)
{
if (spaceDim_ == -1) { spaceDim_ = Dim_; }
InitMesh(Dim_, spaceDim_, NVert, NElem, NBdrElem);
@@ -1110,6 +1119,7 @@ public:
Mesh(int nx, int ny, int nz, Element::Type type, bool generate_edges = false,
real_t sx = 1.0, real_t sy = 1.0, real_t sz = 1.0,
bool sfc_ordering = true)
: attribute_sets(attributes), bdr_attribute_sets(bdr_attributes)
{
Make3D(nx, ny, nz, type, sx, sy, sz, sfc_ordering);
Finalize(true); // refine = true
@@ -1119,6 +1129,7 @@ public:
MFEM_DEPRECATED
Mesh(int nx, int ny, Element::Type type, bool generate_edges = false,
real_t sx = 1.0, real_t sy = 1.0, bool sfc_ordering = true)
: attribute_sets(attributes), bdr_attribute_sets(bdr_attributes)
{
Make2D(nx, ny, type, sx, sy, generate_edges, sfc_ordering);
Finalize(true); // refine = true
@@ -1127,6 +1138,7 @@ public:
/// Deprecated: see @a MakeCartesian1D.
MFEM_DEPRECATED
explicit Mesh(int n, real_t sx = 1.0)
: attribute_sets(attributes), bdr_attribute_sets(bdr_attributes)
{
Make1D(n, sx);
// Finalize(); // reminder: not needed
+88 -2
View File
@@ -38,8 +38,8 @@ bool Mesh::remove_unused_vertices = true;
void Mesh::ReadMFEMMesh(std::istream &input, int version, int &curved)
{
// Read MFEM mesh v1.0 or v1.2 format
MFEM_VERIFY(version == 10 || version == 12,
// Read MFEM mesh v1.0, v1.2, or v1.3 format
MFEM_VERIFY(version == 10 || version == 12 || version == 13,
"unknown MFEM mesh version");
string ident;
@@ -62,6 +62,18 @@ void Mesh::ReadMFEMMesh(std::istream &input, int version, int &curved)
elements[j] = ReadElement(input);
}
if (version == 13)
{
skip_comment_lines(input, '#');
input >> ident; // 'attribute_sets'
MFEM_VERIFY(ident == "attribute_sets", "invalid mesh file");
attribute_sets.attr_sets.Load(input);
attribute_sets.attr_sets.SortAll();
attribute_sets.attr_sets.UniqueAll();
}
skip_comment_lines(input, '#');
input >> ident; // 'boundary'
@@ -73,6 +85,18 @@ void Mesh::ReadMFEMMesh(std::istream &input, int version, int &curved)
boundary[j] = ReadElement(input);
}
if (version == 13)
{
skip_comment_lines(input, '#');
input >> ident; // 'bdr_attribute_sets'
MFEM_VERIFY(ident == "bdr_attribute_sets", "invalid mesh file");
bdr_attribute_sets.attr_sets.Load(input);
bdr_attribute_sets.attr_sets.SortAll();
bdr_attribute_sets.attr_sets.UniqueAll();
}
skip_comment_lines(input, '#');
input >> ident; // 'vertices'
@@ -1513,6 +1537,12 @@ void Mesh::ReadGmshMesh(std::istream &input, int &curved, int &read_gf)
// starting from 1, not 0)
map<int, int> vertices_map;
// A map containing names of physical curves, surfaces, and volumes.
// The first index is the dimension of the physical manifold, the second
// index is the element attribute number of the set, and the string is
// the assigned name.
map<int,map<int,std::string> > phys_names_by_dim;
// Gmsh always outputs coordinates in 3D, but MFEM distinguishes between the
// mesh element dimension (Dim) and the dimension of the space in which the
// mesh is embedded (spaceDim). For example, a 2D MFEM mesh has Dim = 2 and
@@ -2637,6 +2667,38 @@ void Mesh::ReadGmshMesh(std::istream &input, int &curved, int &read_gf)
MFEM_CONTRACT_VAR(elem_domain);
} // section '$Elements'
else if (buff == "$PhysicalNames") // Named element sets
{
int num_names = 0;
int mdim,num;
string name;
input >> num_names;
for (int i=0; i < num_names; i++)
{
input >> mdim >> num;
getline(input, name);
// Trim leading white space
while (!name.empty() &&
(*name.begin() == ' ' || *name.begin() == '\t'))
{ name.erase(0,1);}
// Trim trailing white space
while (!name.empty() &&
(*name.rbegin() == ' ' || *name.rbegin() == '\t' ||
*name.rbegin() == '\n' || *name.rbegin() == '\r'))
{ name.resize(name.length()-1);}
// Remove enclosing quotes
if ( (*name.begin() == '"' || *name.begin() == '\'') &&
(*name.rbegin() == '"' || *name.rbegin() == '\''))
{
name = name.substr(1,name.length()-2);
}
phys_names_by_dim[mdim][num] = name;
}
}
else if (buff == "$Periodic") // Reading master/slave node pairs
{
curved = 1;
@@ -2736,6 +2798,30 @@ void Mesh::ReadGmshMesh(std::istream &input, int &curved, int &read_gf)
}
} // we reach the end of the file
// Process set names
if (phys_names_by_dim.size() > 0)
{
// Process boundary attribute set names
for (auto const &bdr_attr : phys_names_by_dim[Dim-1])
{
if (!bdr_attribute_sets.AttributeSetExists(bdr_attr.second))
{
bdr_attribute_sets.CreateAttributeSet(bdr_attr.second);
}
bdr_attribute_sets.AddToAttributeSet(bdr_attr.second, bdr_attr.first);
}
// Process element attribute set names
for (auto const &attr : phys_names_by_dim[Dim])
{
if (!attribute_sets.AttributeSetExists(attr.second))
{
attribute_sets.CreateAttributeSet(attr.second);
}
attribute_sets.AddToAttributeSet(attr.second, attr.first);
}
}
this->RemoveUnusedVertices();
if (periodic)
{
+42 -1
View File
@@ -145,6 +145,10 @@ ParMesh::ParMesh(MPI_Comm comm, Mesh &mesh, int *partitioning_,
mesh.attributes.Copy(attributes);
mesh.bdr_attributes.Copy(bdr_attributes);
// Copy attribute and bdr_attribute names
mesh.attribute_sets.Copy(attribute_sets);
mesh.bdr_attribute_sets.Copy(bdr_attribute_sets);
GenerateNCFaceInfo();
}
else // mesh.Conforming()
@@ -181,6 +185,10 @@ ParMesh::ParMesh(MPI_Comm comm, Mesh &mesh, int *partitioning_,
mesh.attributes.Copy(attributes);
mesh.bdr_attributes.Copy(bdr_attributes);
// Copy attribute and bdr_attribute names
mesh.attribute_sets.Copy(attribute_sets);
mesh.bdr_attribute_sets.Copy(bdr_attribute_sets);
NumOfEdges = NumOfFaces = 0;
if (Dim > 1)
@@ -3918,6 +3926,10 @@ void ParMesh::NonconformingRefinement(const Array<Refinement> &refinements,
attributes.Copy(pmesh2->attributes);
bdr_attributes.Copy(pmesh2->bdr_attributes);
// Copy attribute and bdr_attribute names
attribute_sets.Copy(pmesh2->attribute_sets);
bdr_attribute_sets.Copy(pmesh2->bdr_attribute_sets);
// now swap the meshes, the second mesh will become the old coarse mesh
// and this mesh will be the new fine mesh
Mesh::Swap(*pmesh2, false);
@@ -3976,6 +3988,10 @@ bool ParMesh::NonconformingDerefinement(Array<real_t> &elem_error,
attributes.Copy(mesh2->attributes);
bdr_attributes.Copy(mesh2->bdr_attributes);
// Copy attribute and bdr_attribute names
attribute_sets.Copy(mesh2->attribute_sets);
bdr_attribute_sets.Copy(mesh2->bdr_attribute_sets);
Mesh::Swap(*mesh2, false);
delete mesh2;
@@ -4027,6 +4043,10 @@ void ParMesh::RebalanceImpl(const Array<int> *partition)
attributes.Copy(pmesh2->attributes);
bdr_attributes.Copy(pmesh2->bdr_attributes);
// Copy attribute and bdr_attribute names
attribute_sets.Copy(pmesh2->attribute_sets);
bdr_attribute_sets.Copy(pmesh2->bdr_attribute_sets);
Mesh::Swap(*pmesh2, false);
delete pmesh2;
@@ -4825,7 +4845,10 @@ void ParMesh::Print(std::ostream &os, const std::string &comments) const
}
}
os << "MFEM mesh v1.0\n";
const bool set_names = attribute_sets.SetsExist() ||
bdr_attribute_sets.SetsExist();
os << (!set_names ? "MFEM mesh v1.0\n" : "MFEM mesh v1.3\n");
if (!comments.empty()) { os << '\n' << comments << '\n'; }
@@ -4848,6 +4871,12 @@ void ParMesh::Print(std::ostream &os, const std::string &comments) const
PrintElement(elements[i], os);
}
if (set_names)
{
os << "\nattribute_sets\n";
attribute_sets.Print(os);
}
int num_bdr_elems = NumOfBdrElements;
if (print_shared && Dim > 1)
{
@@ -4876,6 +4905,13 @@ void ParMesh::Print(std::ostream &os, const std::string &comments) const
PrintElement(faces[(*s2l_face)[i]], os);
}
}
if (set_names)
{
os << "\nbdr_attribute_sets\n";
bdr_attribute_sets.Print(os);
}
os << "\nvertices\n" << NumOfVertices << '\n';
if (Nodes == NULL)
{
@@ -4896,6 +4932,11 @@ void ParMesh::Print(std::ostream &os, const std::string &comments) const
os << "\nnodes\n";
Nodes->Save(os);
}
if (set_names)
{
os << "mfem_mesh_end\n";
}
}
void ParMesh::Save(const std::string &fname, int precision) const
+1
View File
@@ -17,6 +17,7 @@
#include "general/error.hpp"
#include "general/device.hpp"
#include "general/array.hpp"
#include "general/arrays_by_name.hpp"
#include "general/sets.hpp"
#include "general/hash.hpp"
#include "general/mem_alloc.hpp"
+1
View File
@@ -18,6 +18,7 @@ include_directories(BEFORE ${CMAKE_CURRENT_SOURCE_DIR})
# for d in general linalg mesh fem enzyme; do ls -1 $d/*.cpp; done
set(UNIT_TESTS_SRCS
general/test_array.cpp
general/test_arrays_by_name.cpp
general/test_error.cpp
general/test_mem.cpp
general/test_text.cpp
+43
View File
@@ -25,3 +25,46 @@ TEST_CASE("Array init-list construction", "[Array]")
REQUIRE(a[i] == b[i]);
}
}
TEST_CASE("Array entry sorting", "[Array]")
{
int ContigData[6] = {6, 5, 4, 3, 2, 1};
Array<int> a(ContigData, 6);
Array<int> b({1, 2, 3, 3, 2, 1});
a.Sort();
b.Sort();
for (int i = 1; i < a.Size(); i++)
{
REQUIRE(a[i] >= a[i-1]);
}
for (int i = 1; i < b.Size(); i++)
{
REQUIRE(b[i] >= b[i-1]);
}
}
TEST_CASE("Array entry strict sorting", "[Array]")
{
int ContigData[6] = {6, 1, 4, 1, 2, 1};
Array<int> a(ContigData, 6);
Array<int> b({1, 2, 3, 3, 2, 1});
a.Sort();
b.Sort();
a.Unique();
b.Unique();
for (int i = 1; i < a.Size(); i++)
{
REQUIRE(a[i] > a[i-1]);
}
for (int i = 1; i < b.Size(); i++)
{
REQUIRE(b[i] > b[i-1]);
}
}
+202
View File
@@ -0,0 +1,202 @@
// Copyright (c) 2010-2024, 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.
#include "mfem.hpp"
#include "unit_tests.hpp"
using namespace mfem;
void FillArraysByName(ArraysByName<int> &abn)
{
abn.CreateArray("1 potato");
abn.CreateArray("2 potato");
abn.CreateArray("3 potato");
abn.CreateArray("four");
Array<int> a3({2, 4, 6});
int ContigData[4] = {4, 3, 2, 1};
Array<int> a4(ContigData, 4);
abn["1 potato"].SetSize(1); abn["1 potato"][0] = 100;
abn["2 potato"].Append(5); abn["2 potato"].Append(10);
abn["3 potato"] = a3;
abn["four"] = a4;
}
void FillNonUniqueArraysByName(ArraysByName<int> &abn)
{
abn.CreateArray("1 potato");
abn.CreateArray("2 potato");
abn.CreateArray("3 potato");
abn.CreateArray("four");
Array<int> a3({2, 4, 6, 4, 2});
int ContigData[6] = {4, 3, 3, 2, 1, 1};
Array<int> a4(ContigData, 6);
abn["1 potato"].SetSize(3); abn["1 potato"] = 100;
abn["2 potato"].Append(5); abn["2 potato"].Append(10);
abn["2 potato"].Append(5); abn["2 potato"].Append(10);
abn["3 potato"] = a3;
abn["four"] = a4;
}
TEST_CASE("ArraysByName range-based for loop", "[ArraysByName]")
{
ArraysByName<int> abn;
FillArraysByName(abn);
// Test standard Iterator
int i = 1;
for (auto a : abn)
{
REQUIRE(a.second.Size() == i);
i++;
}
// Test ConstIterator
const ArraysByName<int> &abnc = abn;
i = 1;
for (auto a : abnc)
{
REQUIRE(a.second.Size() == i);
i++;
}
}
TEST_CASE("ArraysByName Copy Methods", "[ArraysByName]")
{
ArraysByName<int> abn;
FillArraysByName(abn);
// Explicit call to copy constructor
ArraysByName<int> abn_copy1(abn);
REQUIRE(abn == abn_copy1);
// Implicit call to copy constructor
ArraysByName<int> abn_copy2 = abn;
REQUIRE(abn == abn_copy2);
// Copy assignment operator
ArraysByName<int> abn_copy3;
abn_copy3 = abn;
REQUIRE(abn == abn_copy3);
// Explicit call to move constructor
ArraysByName<int> abn_copy4a(abn);
ArraysByName<int> abn_copy4b(std::move(abn_copy4a));
REQUIRE(abn == abn_copy4b);
// Implicit call to move constructor
ArraysByName<int> abn_copy5a(abn);
ArraysByName<int> abn_copy5b = std::move(abn_copy5a);
REQUIRE(abn == abn_copy5b);
// Move assignment operator
ArraysByName<int> abn_copy6a(abn);
ArraysByName<int> abn_copy6b;
abn_copy6b = std::move(abn_copy6a);
REQUIRE(abn == abn_copy6b);
}
TEST_CASE("ArraysByName Various Methods", "[ArraysByName]")
{
ArraysByName<int> abn;
FillArraysByName(abn);
// Get set names and verify that they are valid
std::set<std::string> names = abn.GetNames();
REQUIRE(abn.Size() == (int)names.size());
for (auto name : names)
{
REQUIRE(abn.EntryExists(name));
}
// Check for existence (or not) of specific named sets
REQUIRE(abn.EntryExists("1 potato"));
REQUIRE(abn.EntryExists("2 potato"));
REQUIRE(abn.EntryExists("3 potato"));
REQUIRE(abn.EntryExists("four"));
REQUIRE(!abn.EntryExists("5 potato"));
abn.CreateArray("5 potato");
REQUIRE(abn.EntryExists("5 potato"));
abn.DeleteArray("5 potato");
REQUIRE(!abn.EntryExists("5 potato"));
abn.DeleteAll();
REQUIRE(!abn.EntryExists("1 potato"));
REQUIRE(!abn.EntryExists("2 potato"));
REQUIRE(!abn.EntryExists("3 potato"));
REQUIRE(!abn.EntryExists("four"));
REQUIRE(!abn.EntryExists("5 potato"));
REQUIRE(abn.Size() == 0);
}
TEST_CASE("ArraysByName Sort/Unique Methods", "[ArraysByName]")
{
ArraysByName<int> abn;
FillNonUniqueArraysByName(abn);
// Verify sizes
int i = 1;
for (auto a : abn)
{
REQUIRE(a.second.Size() == i + 2);
i++;
}
// Sort entries
abn.SortAll();
// Re-Verify sizes
i = 1;
for (auto a : abn)
{
REQUIRE(a.second.Size() == i + 2);
i++;
}
// Verify sorting
for (auto a : abn)
{
for (int j=1; j<a.second.Size(); j++)
{
REQUIRE(a.second[j] >= a.second[j-1]);
}
}
// Remove duplicates
abn.UniqueAll();
// Verify new sizes
i = 1;
for (auto a : abn)
{
REQUIRE(a.second.Size() == i);
i++;
}
// Verify strict sorting
for (auto a : abn)
{
for (int j=1; j<a.second.Size(); j++)
{
REQUIRE(a.second[j] > a.second[j-1]);
}
}
}
+12 -8
View File
@@ -31,6 +31,7 @@ SOURCE_FILES := $(filter-out \
$(CEED_SOURCE_FILES) $(MINI_SOURCE_FILES), $(SOURCE_FILES))
HEADER_FILES = $(SRC)catch.hpp $(SRC)unit_tests.hpp
OBJECT_FILES = $(SOURCE_FILES:$(SRC)%.cpp=%.o)
LIBTESTS_O = libtests.o
DATA_DIR = data
SEQ_MAIN_OBJ = unit_test_main.o
@@ -105,17 +106,17 @@ all: $(UNIT_TESTS)
.SUFFIXES: .cpp .o
.PHONY: all clean
unit_tests: $(SEQ_MAIN_OBJ) $(OBJECT_FILES) $(MFEM_LIB_FILE) $(CONFIG_MK) $(DATA_DIR)
$(CCC) $(SEQ_MAIN_OBJ) $(OBJECT_FILES) $(MFEM_LINK_FLAGS) $(MFEM_LIBS) -o $(@)
unit_tests: $(SEQ_MAIN_OBJ) $(LIBTESTS_O) $(MFEM_LIB_FILE) $(CONFIG_MK) $(DATA_DIR)
$(CCC) $(SEQ_MAIN_OBJ) $(LIBTESTS_O) $(MFEM_LINK_FLAGS) $(MFEM_LIBS) -o $(@)
punit_tests: $(PAR_MAIN_OBJ) $(OBJECT_FILES) $(MFEM_LIB_FILE) $(CONFIG_MK) $(DATA_DIR)
$(CCC) $(PAR_MAIN_OBJ) $(OBJECT_FILES) $(MFEM_LINK_FLAGS) $(MFEM_LIBS) -o $(@)
punit_tests: $(PAR_MAIN_OBJ) $(LIBTESTS_O) $(MFEM_LIB_FILE) $(CONFIG_MK) $(DATA_DIR)
$(CCC) $(PAR_MAIN_OBJ) $(LIBTESTS_O) $(MFEM_LINK_FLAGS) $(MFEM_LIBS) -o $(@)
cunit_tests: $(CUDA_MAIN_OBJ) $(OBJECT_FILES) $(MFEM_LIB_FILE) $(CONFIG_MK) $(DATA_DIR)
$(CCC) $(CUDA_MAIN_OBJ) $(OBJECT_FILES) $(MFEM_LINK_FLAGS) $(MFEM_LIBS) -o $(@)
cunit_tests: $(CUDA_MAIN_OBJ) $(LIBTESTS_O) $(MFEM_LIB_FILE) $(CONFIG_MK) $(DATA_DIR)
$(CCC) $(CUDA_MAIN_OBJ) $(LIBTESTS_O) $(MFEM_LINK_FLAGS) $(MFEM_LIBS) -o $(@)
pcunit_tests: $(PCUDA_MAIN_OBJ) $(OBJECT_FILES) $(MFEM_LIB_FILE) $(CONFIG_MK) $(DATA_DIR)
$(CCC) $(PCUDA_MAIN_OBJ) $(OBJECT_FILES) $(MFEM_LINK_FLAGS) $(MFEM_LIBS) -o $(@)
pcunit_tests: $(PCUDA_MAIN_OBJ) $(LIBTESTS_O) $(MFEM_LIB_FILE) $(CONFIG_MK) $(DATA_DIR)
$(CCC) $(PCUDA_MAIN_OBJ) $(LIBTESTS_O) $(MFEM_LINK_FLAGS) $(MFEM_LIBS) -o $(@)
ceed_tests: $(CEED_OBJ) $(MFEM_LIB_FILE) $(CONFIG_MK) $(DATA_DIR)
$(CCC) $(CEED_OBJ) $(MFEM_LINK_FLAGS) $(MFEM_LIBS) -o $(@)
@@ -125,6 +126,9 @@ $(DEBUG_DEVICE_TEST): $(DEBUG_DEVICE_OBJ) $(MFEM_LIB_FILE) \
$(CCC) $(DEBUG_DEVICE_OBJ) $(MFEM_LINK_FLAGS) \
$(MFEM_LIBS) -o $(@)
$(LIBTESTS_O): $(OBJECT_FILES)
$(LD) -r $(OBJECT_FILES) -o $(@)
# Note: in this rule, we always use the full path to the source file as a
# workaround for an issue with coveralls.
$(OBJECT_FILES) $(SEQ_MAIN_OBJ) $(PAR_MAIN_OBJ) $(CUDA_MAIN_OBJ) \