Skip to content

Commit e4d8556

Browse files
authored
Merge pull request #3445 from roystgnr/embedding_updates
Refinement capability for Prism21, fixes for Tri7, test coverage
2 parents 16f6ded + 2500494 commit e4d8556

11 files changed

Lines changed: 419 additions & 388 deletions

File tree

examples/adaptivity/adaptivity_ex3/adaptivity_ex3.C

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,9 @@ int main(int argc, char ** argv)
170170
dim = input_file("dimension", 2);
171171
const std::string indicator_type = input_file("indicator_type", "kelly");
172172
singularity = input_file("singularity", true);
173+
const bool extrusion = input_file("extrusion",false);
174+
const bool complete = input_file("complete",false);
175+
libmesh_error_msg_if(extrusion && dim < 3, "Extrusion option is only for 3D meshes");
173176

174177
// Skip higher-dimensional examples on a lower-dimensional libMesh build
175178
libmesh_example_requires(dim <= LIBMESH_DIM, "2D/3D support");
@@ -224,7 +227,7 @@ int main(int argc, char ** argv)
224227
// Read in the mesh
225228
if (dim == 1)
226229
MeshTools::Generation::build_line(mesh, 1, -1., 0.);
227-
else if (dim == 2)
230+
else if (dim == 2 || extrusion == true)
228231
mesh.read("lshaped.xda");
229232
else
230233
mesh.read("lshaped3D.xda");
@@ -233,10 +236,20 @@ int main(int argc, char ** argv)
233236
if (element_type == "simplex")
234237
MeshTools::Modification::all_tri(mesh);
235238

236-
// We used first order elements to describe the geometry,
239+
if (extrusion)
240+
{
241+
Mesh flat_mesh = mesh;
242+
mesh.clear();
243+
MeshTools::Generation::build_extrusion(mesh, flat_mesh, 1, {0,0,1});
244+
}
245+
246+
// Use highest-order elements if the config file says so.
247+
if (complete)
248+
mesh.all_complete_order();
249+
// Otherwise, we used first order elements to describe the geometry,
237250
// but we may need second order elements to hold the degrees
238251
// of freedom
239-
if (approx_order > 1 || refine_type != "h")
252+
else if (approx_order > 1 || refine_type != "h")
240253
mesh.all_second_order();
241254

242255
// Mesh Refinement object

examples/adaptivity/adaptivity_ex3/run.sh

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,15 @@ run_example "$example_name" dimension=1 approx_type=HERMITE approx_order=3 refin
2626
run_example "$example_name" dimension=1 approx_type=HERMITE approx_order=3 refinement_type=hp max_r_steps=8
2727
run_example "$example_name" dimension=1 approx_type=HERMITE approx_order=3 refinement_type=matchedhp max_r_steps=4
2828

29+
# Examples to use for test coverage
30+
run_example "$example_name" dimension=2 element_type=simplex approx_type=LAGRANGE approx_order=2 refinement_type=h max_r_steps=3
31+
run_example "$example_name" dimension=2 element_type=simplex complete=true approx_type=LAGRANGE approx_order=3 refinement_type=h max_r_steps=3
32+
run_example "$example_name" dimension=2 element_type=simplex approx_type=HIERARCHIC approx_order=3 refinement_type=h max_r_steps=3
33+
run_example "$example_name" dimension=2 element_type=simplex approx_type=CLOUGH singularity=false approx_order=3 refinement_type=h max_r_steps=3
34+
run_example "$example_name" dimension=3 element_type=simplex extrusion=true approx_type=LAGRANGE approx_order=1 refinement_type=h max_r_steps=3
35+
run_example "$example_name" dimension=3 element_type=simplex extrusion=true approx_type=LAGRANGE approx_order=2 refinement_type=h max_r_steps=3
36+
run_example "$example_name" dimension=3 element_type=simplex extrusion=true complete=true approx_type=LAGRANGE approx_order=3 refinement_type=h max_r_steps=3
37+
2938
# Examples to use for benchmarking
3039
benchmark_example 1 "$example_name" dimension=3 approx_type=LAGRANGE approx_order=2 refinement_type=h max_r_steps=12
3140
benchmark_example 1 "$example_name" dimension=3 approx_type=HIERARCHIC approx_order=3 refinement_type=h

include/geom/cell_prism20.h

Lines changed: 23 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -258,6 +258,19 @@ class Prism20 final : public Prism
258258

259259
virtual void flip(BoundaryInfo *) override final;
260260

261+
#ifdef LIBMESH_ENABLE_AMR
262+
virtual
263+
const std::vector<std::pair<unsigned char, unsigned char>> &
264+
parent_bracketing_nodes(unsigned int,
265+
unsigned int) const override
266+
{
267+
// We can't get any good bracketing nodes for child tri-center
268+
// nodes on the parent prism midplane. No refining Prism20 for
269+
// us.
270+
libmesh_not_implemented_msg("Prism20 cannot be refined; use Prism21 instead.");
271+
}
272+
#endif
273+
261274
unsigned int center_node_on_side(const unsigned short side) const override final;
262275

263276
ElemType side_type (const unsigned int s) const override final;
@@ -276,16 +289,16 @@ class Prism20 final : public Prism
276289
/**
277290
* Matrix used to create the elements children.
278291
*/
279-
virtual Real embedding_matrix (const unsigned int i,
280-
const unsigned int j,
281-
const unsigned int k) const override
282-
{ return _embedding_matrix[i][j][k]; }
283-
284-
/**
285-
* Matrix that computes new nodal locations/solution values
286-
* from current nodes/solution.
287-
*/
288-
static const Real _embedding_matrix[num_children][num_nodes][num_nodes];
292+
virtual Real embedding_matrix (const unsigned int,
293+
const unsigned int,
294+
const unsigned int) const override
295+
{
296+
// We can't get any good bracketing nodes for child tri-center
297+
// nodes on the parent prism midplane, so we can't auto-calculate
298+
// embedding matrices and couldn't use them if we did.
299+
libmesh_not_implemented_msg("Prism20 cannot be refined; use Prism21 instead.");
300+
return 0;
301+
}
289302

290303
LIBMESH_ENABLE_TOPOLOGY_CACHES;
291304

include/geom/cell_prism21.h

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -261,6 +261,14 @@ class Prism21 final : public Prism
261261

262262
virtual void flip(BoundaryInfo *) override final;
263263

264+
#ifdef LIBMESH_ENABLE_AMR
265+
virtual
266+
const std::vector<std::pair<unsigned char, unsigned char>> &
267+
parent_bracketing_nodes(unsigned int c,
268+
unsigned int n) const override
269+
{ return _parent_bracketing_nodes[c][n]; }
270+
#endif
271+
264272
unsigned int center_node_on_side(const unsigned short side) const override final;
265273

266274
ElemType side_type (const unsigned int s) const override final;
@@ -290,6 +298,13 @@ class Prism21 final : public Prism
290298
*/
291299
static const Real _embedding_matrix[num_children][num_nodes][num_nodes];
292300

301+
/**
302+
* Pairs of nodes that bracket child nodes when doing mesh
303+
* refinement.
304+
*/
305+
static const std::vector<std::pair<unsigned char, unsigned char>>
306+
_parent_bracketing_nodes[num_children][num_nodes];
307+
293308
LIBMESH_ENABLE_TOPOLOGY_CACHES;
294309

295310
#endif // LIBMESH_ENABLE_AMR

src/apps/embedding.C

Lines changed: 85 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@
3232
#include "libmesh/point.h"
3333

3434
// C++ includes
35-
35+
#include <numeric> // gcd
3636

3737
using namespace libMesh;
3838

@@ -45,6 +45,7 @@ void usage_error(const char * progname)
4545
<< " --elem type elem type (e.g. TET14)\n"
4646
<< " --childnum num child number\n"
4747
<< " --denominator num denominator to use\n"
48+
<< " --diff only output diffs from existing\n"
4849
<< std::endl;
4950

5051
exit(1);
@@ -83,7 +84,9 @@ int main(int argc, char ** argv)
8384
assert_argument(cl, "--childnum", argv[0], 0);
8485

8586
const int denominator =
86-
assert_argument(cl, "--denominator", argv[0], 0);
87+
assert_argument(cl, "--denominator", argv[0], 1);
88+
89+
const bool diff = cl.search("--diff");
8790

8891
const ElemType elem_type =
8992
Utility::string_to_enum<ElemType>(elem_type_string);
@@ -131,26 +134,97 @@ int main(int argc, char ** argv)
131134
elem->set_node(n) = &nodes[n];
132135
}
133136

134-
for (auto i : elem->node_index_range())
137+
const unsigned int denomdigits = std::ceil(std::log10(denominator));
138+
const unsigned int spacing = denomdigits*2+3;
139+
140+
std::cout.precision(17);
141+
142+
std::cout << " // embedding matrix for child " << childnum << '\n';
143+
std::cout << " {\n";
144+
std::cout << " //";
145+
for (auto i : make_range(n_nodes))
146+
{
147+
const unsigned int indexdigits =
148+
std::ceil(std::log10(i));
149+
const int padding = spacing-indexdigits-2*(i==0);
150+
for (int k=0; k < padding; ++k)
151+
std::cout << ' ';
152+
std::cout << i;
153+
if (i+1 == n_nodes)
154+
std::cout << '\n';
155+
else
156+
std::cout << ',';
157+
}
158+
159+
for (auto i : make_range(n_nodes))
135160
{
136161
const Point & pt = elem->point(i);
137-
std::cout << '{';
162+
std::cout << " {";
138163
for (auto j : make_range(n_nodes))
139164
{
140165
Real shape = FEInterface::shape(dim, fe_type, elem_type, j, pt);
166+
167+
// Don't print -0 or 1e-17; we don't tolerate FP error at 0
168+
if (std::abs(shape) < TOLERANCE*std::sqrt(TOLERANCE))
169+
shape = 0;
170+
141171
const Real embed = ref.embedding_matrix(childnum, i, j);
142-
if (std::abs(shape - embed) < TOLERANCE*TOLERANCE)
143-
std::cout << "++++++,";
172+
if (diff &&
173+
std::abs(shape - embed) < TOLERANCE*std::sqrt(TOLERANCE))
174+
{
175+
for (unsigned int k=0; k != spacing; ++k)
176+
std::cout << '+';
177+
if (j+1 != n_nodes)
178+
std::cout << ',';
179+
}
144180
else
145181
{
146-
std::cout << (shape*denominator);
147-
if (shape != 0.0)
148-
std::cout << "/r" << denominator;
149-
std::cout << ", ";
182+
int oldnumerator = std::round(shape*denominator);
183+
int newnumerator = oldnumerator;
184+
int newdenominator = denominator;
185+
186+
if (std::abs(oldnumerator-shape*denominator) < TOLERANCE*sqrt(TOLERANCE))
187+
{
188+
int the_gcd = std::gcd(newnumerator, newdenominator);
189+
newnumerator /= the_gcd;
190+
newdenominator /= the_gcd;
191+
}
192+
193+
const unsigned int newdenomdigits =
194+
std::ceil(std::log10(newdenominator));
195+
std::ostringstream ostr;
196+
ostr << (shape*newdenominator);
197+
const int padding =
198+
(shape != 0.0 && newdenominator != 1) ?
199+
int(spacing)-newdenomdigits-2-ostr.str().size() :
200+
int(spacing)-ostr.str().size();
201+
for (int k=0; k < padding; ++k)
202+
std::cout << ' ';
203+
std::cout << ostr.str();
204+
if (shape != 0.0 && newdenominator != 1)
205+
{
206+
if (1 << (int)std::round(std::log2(newdenominator)) ==
207+
newdenominator)
208+
std::cout << "/" << newdenominator << '.';
209+
// If we don't have a power of 2 we need to make
210+
// sure we're dividing at might-exceed-double Real
211+
// precision
212+
else
213+
std::cout << "/r" << newdenominator;
214+
}
215+
if (j+1 != n_nodes)
216+
std::cout << ',';
150217
}
151218
}
152-
std::cout << '}' << std::endl;
219+
220+
if (i+1 == n_nodes)
221+
std::cout << "} ";
222+
else
223+
std::cout << "}, ";
224+
225+
std::cout << "// " << i << '\n';
153226
}
227+
std::cout << " },\n";
154228

155229
return 0;
156230
}

src/fe/fe_abstract.C

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -865,8 +865,24 @@ void FEAbstract::compute_node_constraints (NodeConstraints & constraints,
865865
my_side_n < n_side_nodes;
866866
my_side_n++)
867867
{
868+
// We can have an FE type that supports an order
869+
// partially, such that sides do not support the same
870+
// order. E.g. we say that a LAGRANGE PRISM21 supports
871+
// "third" order to distinguish its shape functions from
872+
// a PRISM18, but the QUAD9 sides will still only
873+
// support second order.
874+
FEType side_fe_type = fe_type;
875+
const int side_max_order =
876+
FEInterface::max_order(fe_type, my_side->type());
877+
878+
if ((int)fe_type.order > side_max_order)
879+
side_fe_type.order = side_max_order;
880+
868881
// Do not use the p_level(), if any, that is inherited by the side.
869-
libmesh_assert_less (my_side_n, FEInterface::n_dofs(fe_type, /*extra_order=*/0, my_side.get()));
882+
libmesh_assert_less
883+
(my_side_n,
884+
FEInterface::n_dofs(side_fe_type, /*extra_order=*/0,
885+
my_side.get()));
870886

871887
const Node * my_node = my_nodes[my_side_n];
872888

@@ -884,13 +900,17 @@ void FEAbstract::compute_node_constraints (NodeConstraints & constraints,
884900
their_side_n++)
885901
{
886902
// Do not use the p_level(), if any, that is inherited by the side.
887-
libmesh_assert_less (their_side_n, FEInterface::n_dofs(fe_type, /*extra_order=*/0, parent_side.get()));
903+
libmesh_assert_less
904+
(their_side_n,
905+
FEInterface::n_dofs(side_fe_type,
906+
/*extra_order=*/0,
907+
parent_side.get()));
888908

889909
const Node * their_node = parent_nodes[their_side_n];
890910
libmesh_assert(their_node);
891911

892912
// Do not use the p_level(), if any, that is inherited by the side.
893-
const Real their_value = FEInterface::shape(fe_type,
913+
const Real their_value = FEInterface::shape(side_fe_type,
894914
/*extra_order=*/0,
895915
parent_side.get(),
896916
their_side_n,

src/fe/fe_lagrange.C

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -836,6 +836,18 @@ void lagrange_compute_constraints (DofConstraints & constraints,
836836
elem->build_side_ptr(my_side, s);
837837
parent->build_side_ptr(parent_side, s);
838838

839+
// We have some elements with interior bubble function bases
840+
// and lower-order sides. We need to only ask for the lower
841+
// order in those cases.
842+
FEType side_fe_type = fe_type;
843+
int extra_order = 0;
844+
if (my_side->default_order() < fe_type.order)
845+
{
846+
side_fe_type.order = my_side->default_order();
847+
extra_order = (int)(side_fe_type.order) -
848+
(int)(fe_type.order);
849+
}
850+
839851
// This function gets called element-by-element, so there
840852
// will be a lot of memory allocation going on. We can
841853
// at least minimize this for the case of the dof indices
@@ -844,14 +856,14 @@ void lagrange_compute_constraints (DofConstraints & constraints,
844856
parent_dof_indices.reserve (parent_side->n_nodes());
845857

846858
dof_map.dof_indices (my_side.get(), my_dof_indices,
847-
variable_number);
859+
variable_number, extra_order);
848860
dof_map.dof_indices (parent_side.get(), parent_dof_indices,
849-
variable_number);
861+
variable_number, extra_order);
850862

851863
const unsigned int n_side_dofs =
852-
FEInterface::n_dofs(fe_type, my_side.get());
864+
FEInterface::n_dofs(side_fe_type, my_side.get());
853865
const unsigned int n_parent_side_dofs =
854-
FEInterface::n_dofs(fe_type, parent_side.get());
866+
FEInterface::n_dofs(side_fe_type, parent_side.get());
855867
for (unsigned int my_dof=0; my_dof != n_side_dofs; my_dof++)
856868
{
857869
libmesh_assert_less (my_dof, my_side->n_nodes());
@@ -915,7 +927,7 @@ void lagrange_compute_constraints (DofConstraints & constraints,
915927
parent_dof_indices[their_dof];
916928

917929
const Real their_dof_value = FEInterface::shape(Dim-1,
918-
fe_type,
930+
side_fe_type,
919931
parent_side.get(),
920932
their_dof,
921933
mapped_point);

0 commit comments

Comments
 (0)