Skip to content

Commit 95f7f30

Browse files
Merge pull request #275 from arcaneframework/dev/mab-hex-quad-support-electrostatics
[feat] hexa/quad elemnt support
2 parents 4df1fad + 8baa61c commit 95f7f30

File tree

12 files changed

+421
-22
lines changed

12 files changed

+421
-22
lines changed

meshes/msh/box-rods.quad.msh

22.5 KB
Binary file not shown.

modules/electrostatics/CMakeLists.txt

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,11 @@ file(COPY "inputs" DESTINATION ${CMAKE_CURRENT_BINARY_DIR})
2424
file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/meshes)
2525
set(MESH_FILES
2626
box-rods.msh
27+
box-rods.quad.msh
2728
box-rod-circle.msh
2829
interdigital_capacitor.msh
2930
truncated_cube.msh
31+
truncated_cube.hexa.msh
3032
spheres_in_sphere.msh
3133
)
3234
foreach(MESH_FILE IN LISTS MESH_FILES)
@@ -37,9 +39,13 @@ enable_testing()
3739

3840
if(FEMUTILS_HAS_SOLVER_BACKEND_PETSC)
3941
add_test(NAME [electrostatics]box-rods COMMAND Electrostatics inputs/box-rods.arc)
42+
add_test(NAME [electrostatics]box-rods_neumann COMMAND Electrostatics inputs/box-rods.neumann.arc)
43+
add_test(NAME [electrostatics]box-rods_quad COMMAND Electrostatics inputs/box-rods.quad.arc)
44+
add_test(NAME [electrostatics]box-rods_neumann_quad COMMAND Electrostatics inputs/box-rods.neumann.quad.arc)
4045
add_test(NAME [electrostatics]capacitor COMMAND Electrostatics inputs/Capacitor.arc)
4146
add_test(NAME [electrostatics]rod-circle COMMAND Electrostatics inputs/rod-circle.arc)
4247
add_test(NAME [electrostatics]spheres COMMAND Electrostatics inputs/spheres.arc)
48+
add_test(NAME [electrostatics]truncated-cube_hexa COMMAND Electrostatics inputs/truncated_cube.hexa.arc)
4349
add_test(NAME [electrostatics]box-rods_petsc_monitor COMMAND Electrostatics
4450
-A,//fem/petsc-flags=-ksp_monitor
4551
inputs/box-rods.arc)
@@ -67,6 +73,8 @@ if(FEMUTILS_HAS_SOLVER_BACKEND_PETSC)
6773

6874
if(FEMUTILS_HAS_PARALLEL_SOLVER AND MPIEXEC_EXECUTABLE)
6975
add_test(NAME [electrostatics]box-rods_2pu COMMAND ${MPIEXEC_EXECUTABLE} -n 2 ./Electrostatics inputs/box-rods.arc)
76+
add_test(NAME [electrostatics]box-rods_quad_2pu COMMAND ${MPIEXEC_EXECUTABLE} -n 2 ./Electrostatics inputs/box-rods.quad.arc)
77+
add_test(NAME [electrostatics]truncated-cube_hexa_2pu COMMAND ${MPIEXEC_EXECUTABLE} -n 2 ./Electrostatics inputs/truncated_cube.hexa.arc)
7078
endif()
7179
endif()
7280

Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
// -*- tab-width: 2; indent-tabs-mode: nil; coding: utf-8-with-signature -*-
2+
//-----------------------------------------------------------------------------
3+
// Copyright 2000-2025 CEA (www.cea.fr) IFPEN (www.ifpenergiesnouvelles.com)
4+
// See the top-level COPYRIGHT file for details.
5+
// SPDX-License-Identifier: Apache-2.0
6+
//-----------------------------------------------------------------------------
7+
/*---------------------------------------------------------------------------*/
8+
/* ElementMatrixHexQuad.h (C) 2022-2025 */
9+
/* */
10+
/* Contains functions to compute the FEM element matrices for electrostatics */
11+
/*---------------------------------------------------------------------------*/
12+
/*---------------------------------------------------------------------------*/
13+
14+
/*---------------------------------------------------------------------------*/
15+
/**
16+
* @brief Computes the element matrix for a quadrilateral element (QUAD4, ℙ1 FE).
17+
*
18+
* This function calculates the integral of:
19+
* 𝑎(𝑢,𝑣) = ∫∫ (∂𝑢/∂𝑥 ∂𝑣/∂𝑥 + ∂𝑢/∂𝑦 ∂𝑣/∂𝑦)dΩ
20+
*
21+
* Steps involved:
22+
* 1. Define Gauss points (2x2) and weights.
23+
* 2. Loop over Gauss points to compute the gradients in physical space
24+
* and the determinant of the Jacobian, via computeGradientsAndJacobianQuad4.
25+
* 3. Compute the integration weight.
26+
* 4. Assemble the element matrix using the computed gradients.
27+
*
28+
* @param cell The cell for which the element matrix is computed.
29+
* @return The computed element matrix.
30+
*/
31+
/*---------------------------------------------------------------------------*/
32+
33+
RealMatrix<4, 4> FemModule::_computeElementMatrixQuad4(Cell cell)
34+
{
35+
// Gauss points and weights for 2x2 quadrature
36+
constexpr Real gp[2] = { -M_SQRT1_3, M_SQRT1_3 }; // [-1/sqrt(3) , 1/sqrt(3)]
37+
constexpr Real w = 1.0;
38+
39+
// Initialize the element matrix
40+
RealMatrix<4, 4> ae;
41+
ae.fill(0.0);
42+
43+
// Loop over Gauss points
44+
for (Int8 ixi = 0; ixi < 2; ++ixi) {
45+
for (Int8 ieta = 0; ieta < 2; ++ieta) {
46+
47+
// Get the coordinates of the Gauss point in natural coordinates (ξ,η,ζ)
48+
const Real xi = gp[ixi];
49+
const Real eta = gp[ieta];
50+
51+
// Get shape function gradients w.r.t (𝑥,𝑦) and determinant of Jacobian
52+
const auto gp_info = ArcaneFemFunctions::FeOperation2D::computeGradientsAndJacobianQuad4(cell, m_node_coord, xi, eta);
53+
const RealVector<4>& dxU = gp_info.dN_dx;
54+
const RealVector<4>& dyU = gp_info.dN_dy;
55+
const Real detJ = gp_info.det_j;
56+
57+
// Integration weight
58+
const Real integration_weight = detJ * w * w;
59+
60+
// stiffness matrix assembly
61+
ae += (dxU ^ dxU) * integration_weight + (dyU ^ dyU) * integration_weight;
62+
}
63+
}
64+
return ae;
65+
}
66+
67+
/*---------------------------------------------------------------------------*/
68+
/**
69+
* @brief Computes the element matrix for a hexahedral element (HEXA8, ℙ1 FE).
70+
*
71+
* This function calculates the integral of:
72+
* 𝑎(𝑢,𝑣) = ∫∫∫ (∂𝑢/∂𝑥 ∂𝑣/∂𝑥 + ∂𝑢/∂𝑦 ∂𝑣/∂𝑦 + ∂𝑢/∂𝑧 ∂𝑣/∂𝑧)dΩ
73+
*
74+
* Steps involved:
75+
* 1. Define Gauss points (2x2x2) and weights.
76+
* 2. Loop over Gauss points to compute the gradients in physical space (𝑥,𝑦,𝑧)
77+
* and the determinant of the Jacobian via computeGradientsAndJacobianHexa8.
78+
* 3. Compute the integration weight.
79+
* 4. Assemble the element matrix using the computed gradients.
80+
*
81+
* @param cell The cell for which the element matrix is computed.
82+
* @return The computed element matrix.
83+
*/
84+
/*---------------------------------------------------------------------------*/
85+
86+
RealMatrix<8, 8> FemModule::_computeElementMatrixHexa8(Cell cell)
87+
{
88+
// 2x2x2 Gauss points and weights for [-1,1]^3
89+
constexpr Real gp[2] = { -M_SQRT1_3, M_SQRT1_3 }; // -1/sqrt(3), 1/sqrt(3)
90+
constexpr Real w = 1.0;
91+
92+
// Initialize the element matrix
93+
RealMatrix<8, 8> ae;
94+
ae.fill(0.0);
95+
96+
// Loop over Gauss points
97+
for (Int8 ixi = 0; ixi < 2; ++ixi) {
98+
for (Int8 ieta = 0; ieta < 2; ++ieta) {
99+
for (Int8 izeta = 0; izeta < 2; ++izeta) {
100+
101+
// Get the coordinates of Gauss points in natural coordinates (ξ,η,ζ)
102+
const Real xi = gp[ixi];
103+
const Real eta = gp[ieta];
104+
const Real zeta = gp[izeta];
105+
106+
// Get shape function gradients w.r.t (𝑥,𝑦,𝑧) and determinant of Jacobian
107+
const auto gp_info = ArcaneFemFunctions::FeOperation3D::computeGradientsAndJacobianHexa8(cell, m_node_coord, xi, eta, zeta);
108+
const RealVector<8>& dxU = gp_info.dN_dx;
109+
const RealVector<8>& dyU = gp_info.dN_dy;
110+
const RealVector<8>& dzU = gp_info.dN_dz;
111+
const Real detJ = gp_info.det_j;
112+
113+
// Integration weight
114+
const Real integration_weight = detJ * w * w;
115+
116+
// Assemble element matrix (variational form)
117+
ae += (dxU ^ dxU) * integration_weight + (dyU ^ dyU) * integration_weight + (dzU ^ dzU) * integration_weight;
118+
}
119+
}
120+
}
121+
return ae;
122+
}

modules/electrostatics/Fem.axl

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@
3333
<description>Which matrix format to use DOK|BSR|AF-BSR.</description>
3434
</simple>
3535

36-
<simple name="petsc-flags" type="string" default="" optional="true">
36+
<simple name="petsc-flags" type="string" default="-mat_type baij" optional="true">
3737
<description>Flags for PETSc from commandline.</description>
3838
</simple>
3939

@@ -49,6 +49,10 @@
4949
<description>Boolean to enable cross validation.</description>
5050
</simple>
5151

52+
<simple name="hex-quad-mesh" type="bool" default="false" optional="true">
53+
<description>Boolean to use hexa or quad element mesh.</description>
54+
</simple>
55+
5256
<!-- Linear system service instance -->
5357
<service-instance name="linear-system" type="Arcane::FemUtils::IDoFLinearSystemFactory" default="AlephLinearSystem" />
5458

modules/electrostatics/FemModule.cc

Lines changed: 61 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
/*---------------------------------------------------------------------------*/
1313
#include "FemModule.h"
1414
#include "ElementMatrix.h"
15+
#include "ElementMatrixHexQuad.h"
1516

1617
/*---------------------------------------------------------------------------*/
1718
/**
@@ -36,6 +37,7 @@ startInit()
3637
m_solve_linear_system = options()->solveLinearSystem();
3738
m_cross_validation = options()->crossValidation();
3839
m_petsc_flags = options()->petscFlags();
40+
m_hex_quad_mesh = options()->hexQuadMesh();
3941

4042
elapsedTime = platform::getRealTime() - elapsedTime;
4143
ArcaneFemFunctions::GeneralFunctions::printArcaneFemTime(traceMng(),"initialize", elapsedTime);
@@ -188,15 +190,36 @@ _assembleLinearOperatorCpu()
188190
auto node_dof(m_dofs_on_nodes.nodeDoFConnectivityView());
189191

190192
if (options()->rho.isPresent()) {
191-
Real qdot = -rho / epsilon;
192-
ArcaneFemFunctions::BoundaryConditions2D::applyConstantSourceToRhs(qdot, mesh(), node_dof, m_node_coord, rhs_values);
193+
Real f = -rho / epsilon;
194+
if (mesh()->dimension() == 2) {
195+
if (m_hex_quad_mesh)
196+
ArcaneFemFunctions::BoundaryConditions2D::applyConstantSourceToRhsQuad4(f, mesh(), node_dof, m_node_coord, rhs_values);
197+
else
198+
ArcaneFemFunctions::BoundaryConditions2D::applyConstantSourceToRhs(f, mesh(), node_dof, m_node_coord, rhs_values);
199+
}
200+
201+
if (mesh()->dimension() == 3) {
202+
if (m_hex_quad_mesh)
203+
ArcaneFemFunctions::BoundaryConditions3D::applyConstantSourceToRhsHexa8(f, mesh(), node_dof, m_node_coord, rhs_values);
204+
else
205+
ArcaneFemFunctions::BoundaryConditions3D::applyConstantSourceToRhs(f, mesh(), node_dof, m_node_coord, rhs_values);
206+
}
193207
}
194208

195209
auto applyBoundaryConditions = [&](auto BCFunctions) {
196210
BC::IArcaneFemBC* bc = options()->boundaryConditions();
197211
if (bc) {
198212
for (BC::INeumannBoundaryCondition* bs : bc->neumannBoundaryConditions())
199-
BCFunctions.applyNeumannToRhs(bs, node_dof, m_node_coord, rhs_values);
213+
if (mesh()->dimension() == 2)
214+
if (m_hex_quad_mesh)
215+
ArcaneFemFunctions::BoundaryConditions2D::applyNeumannToRhsQuad4(bs, node_dof, m_node_coord, rhs_values);
216+
else
217+
ArcaneFemFunctions::BoundaryConditions2D::applyNeumannToRhs(bs, node_dof, m_node_coord, rhs_values);
218+
else
219+
if (m_hex_quad_mesh)
220+
ArcaneFemFunctions::BoundaryConditions3D::applyNeumannToRhsHexa8(bs, node_dof, m_node_coord, rhs_values);
221+
else
222+
ArcaneFemFunctions::BoundaryConditions3D::applyNeumannToRhs(bs, node_dof, m_node_coord, rhs_values);
200223

201224
for (BC::IDirichletBoundaryCondition* bs : bc->dirichletBoundaryConditions())
202225
BCFunctions.applyDirichletToLhsAndRhs(bs, node_dof, m_node_coord, m_linear_system, rhs_values);
@@ -318,13 +341,15 @@ _assembleBilinearOperator()
318341

319342
if (m_matrix_format == "DOK") {
320343
if (mesh()->dimension() == 3)
321-
_assembleBilinear<4>([this](const Cell& cell) {
322-
return _computeElementMatrixTetra4(cell);
323-
});
344+
if(m_hex_quad_mesh)
345+
_assembleBilinear<8>([this](const Cell& cell) { return _computeElementMatrixHexa8(cell); });
346+
else
347+
_assembleBilinear<4>([this](const Cell& cell) { return _computeElementMatrixTetra4(cell); });
324348
else
325-
_assembleBilinear<3>([this](const Cell& cell) {
326-
return _computeElementMatrixTria3(cell);
327-
});
349+
if(m_hex_quad_mesh)
350+
_assembleBilinear<4>([this](const Cell& cell) { return _computeElementMatrixQuad4(cell); });
351+
else
352+
_assembleBilinear<3>([this](const Cell& cell) { return _computeElementMatrixTria3(cell); });
328353
}
329354

330355
elapsedTime = platform::getRealTime() - elapsedTime;
@@ -422,15 +447,32 @@ _updateVariables()
422447
m_phi.synchronize();
423448

424449
if (mesh()->dimension() == 2)
425-
ENUMERATE_ (Cell, icell, allCells()) {
426-
Cell cell = *icell;
427-
m_E[cell] = ArcaneFemFunctions::FeOperation2D::computeGradientTria3(cell, m_node_coord, m_phi);
428-
}
429-
else
430-
ENUMERATE_ (Cell, icell, allCells()) {
431-
Cell cell = *icell;
432-
m_E[cell] = ArcaneFemFunctions::FeOperation3D::computeGradientTetra4(cell, m_node_coord, m_phi);
433-
}
450+
if (m_hex_quad_mesh)
451+
ENUMERATE_ (Cell, icell, allCells()) {
452+
Cell cell = *icell;
453+
Real3 grad = ArcaneFemFunctions::FeOperation2D::computeGradientQuad4(cell, m_node_coord, m_phi);
454+
m_E[cell] = -grad.x * grad.x - grad.y * grad.y;
455+
}
456+
else
457+
ENUMERATE_ (Cell, icell, allCells()) {
458+
Cell cell = *icell;
459+
Real3 grad = ArcaneFemFunctions::FeOperation2D::computeGradientTria3(cell, m_node_coord, m_phi);
460+
m_E[cell] = -grad.x * grad.x - grad.y * grad.y;
461+
}
462+
463+
if (mesh()->dimension() == 3)
464+
if (m_hex_quad_mesh)
465+
ENUMERATE_ (Cell, icell, allCells()) {
466+
Cell cell = *icell;
467+
Real3 grad = ArcaneFemFunctions::FeOperation3D::computeGradientHexa8(cell, m_node_coord, m_phi);
468+
m_E[cell] = -grad.x * grad.x - grad.y * grad.y - grad.z * grad.z;
469+
}
470+
else
471+
ENUMERATE_ (Cell, icell, allCells()) {
472+
Cell cell = *icell;
473+
Real3 grad = ArcaneFemFunctions::FeOperation3D::computeGradientTetra4(cell, m_node_coord, m_phi);
474+
m_E[cell] = -grad.x * grad.x - grad.y * grad.y - grad.z * grad.z;
475+
}
434476

435477
m_E.synchronize();
436478

@@ -460,11 +502,10 @@ _validateResults()
460502
if (allNodes().size() < 200)
461503
ENUMERATE_ (Node, inode, allNodes()) {
462504
Node node = *inode;
463-
info() << "phi[" << node.localId() << "][" << node.uniqueId() << "] = " << m_phi[node];
505+
info() << "phi[" << node.uniqueId() << "] = " << m_phi[node];
464506
}
465507

466508
String filename = options()->resultFile();
467-
info() << "ValidateResultFile filename=" << filename;
468509

469510
if (!filename.empty())
470511
checkNodeResultFile(traceMng(), filename, m_phi, 1.0e-4, 1.0e-16);

modules/electrostatics/FemModule.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@ class FemModule
8989
bool m_assemble_linear_system = true;
9090
bool m_solve_linear_system = true;
9191
bool m_cross_validation = true;
92+
bool m_hex_quad_mesh = false;
9293

9394
void _doStationarySolve();
9495
void _getMaterialParameters();
@@ -100,7 +101,8 @@ class FemModule
100101

101102
RealMatrix<3, 3> _computeElementMatrixTria3(Cell cell);
102103
RealMatrix<4, 4> _computeElementMatrixTetra4(Cell cell);
103-
104+
RealMatrix<4, 4> _computeElementMatrixQuad4(Cell cell);
105+
RealMatrix<8, 8> _computeElementMatrixHexa8(Cell cell);
104106
template <int N>
105107
void _assembleBilinear(const std::function<RealMatrix<N, N>(const Cell&)>& compute_element_matrix);
106108
};
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
260 0.20717053527226
2+
261 0.383610894594714
3+
262 0.387911285847584
4+
263 -0.407050243122318
5+
264 -0.425425596473062
6+
265 -0.705231441654697
7+
266 0.326249166788217
8+
267 0.349280160680684
9+
268 -0.364613077656847
10+
269 -0.0201922027407905
11+
270 -0.697845286522503
12+
271 -0.115572177922491
13+
272 -0.630863639596694
14+
273 0.302314253921705
15+
274 -0.162425200359167
16+
275 -0.114418588238129
17+
276 -0.2948195045131
18+
277 -0.375718403629807
19+
278 -0.77074423851435
20+
279 -0.17490104704671
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
330 -0.454092321745147
2+
337 -0.567484524008041
3+
338 -0.596074907200828
4+
339 -0.602676785764029
5+
340 -0.630921162792379
6+
341 -0.702324315388119
7+
342 -0.635109745204455
8+
343 -0.666487677467807
9+
344 -0.758449272836748
10+
345 -0.601902087988295
11+
346 -0.623165072062167
12+
347 -0.674979546518178
13+
348 -0.643542875728247
14+
349 -0.679525088730678
15+
350 -0.775568041352925
16+
351 -0.687504081612848
17+
352 -0.401359711200829
18+
353 -0.697643007268312
19+
354 -0.842631723211244
20+
355 -0.490300612287456
21+
356 -0.885986586154468
22+
359 -0.749894190756057
23+
360 -0.903598044401985

0 commit comments

Comments
 (0)