feat(surface): major work on implementing surface constraints in a presciption agnostic manner
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
module;
|
||||
|
||||
#include <array>
|
||||
#include <cmath>
|
||||
#include <concepts>
|
||||
#include <cstddef>
|
||||
#include <memory>
|
||||
@@ -888,6 +889,282 @@ export namespace mean_field::field {
|
||||
mfem::Array<int> m_trueToReduced;
|
||||
};
|
||||
|
||||
/*
|
||||
* Boundary rows expressed in a field's reduced solver ordering.
|
||||
*
|
||||
* This object is deliberately independent of any particular physical
|
||||
* surface condition. Its template constructor below combines a field,
|
||||
* a semantic boundary, and a domain schema. Consequently the same
|
||||
* topology machinery can be used by any compiled surface formulation;
|
||||
* it is not tied to enthalpy or pressure.
|
||||
*/
|
||||
class FieldBoundaryDofMap final {
|
||||
public:
|
||||
FieldBoundaryDofMap() = default;
|
||||
|
||||
FieldBoundaryDofMap(
|
||||
const int fieldReducedSize,
|
||||
const mfem::Array<int> &boundaryReducedDofs
|
||||
)
|
||||
: m_fieldReducedSize(fieldReducedSize),
|
||||
m_boundaryReducedDofs(boundaryReducedDofs) {
|
||||
if (m_fieldReducedSize < 0) {
|
||||
throw std::invalid_argument("FieldBoundaryDofMap requires a non-negative field size.");
|
||||
}
|
||||
|
||||
m_boundaryReducedDofMarker.SetSize(m_fieldReducedSize);
|
||||
m_boundaryReducedDofMarker = 0;
|
||||
|
||||
int previousReducedDof = -1;
|
||||
for (const int reducedDof : m_boundaryReducedDofs) {
|
||||
if (reducedDof < 0 || reducedDof >= m_fieldReducedSize) {
|
||||
throw std::invalid_argument("FieldBoundaryDofMap contains a DOF outside the reduced field vector.");
|
||||
}
|
||||
if (reducedDof <= previousReducedDof) {
|
||||
throw std::invalid_argument("FieldBoundaryDofMap indices must be strictly increasing and unique.");
|
||||
}
|
||||
|
||||
m_boundaryReducedDofMarker[reducedDof] = 1;
|
||||
previousReducedDof = reducedDof;
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] int field_size() const noexcept {
|
||||
return m_fieldReducedSize;
|
||||
}
|
||||
|
||||
[[nodiscard]] int size() const noexcept {
|
||||
return m_boundaryReducedDofs.Size();
|
||||
}
|
||||
|
||||
[[nodiscard]] bool empty() const noexcept {
|
||||
return size() == 0;
|
||||
}
|
||||
|
||||
[[nodiscard]] const mfem::Array<int> &reduced_dofs() const noexcept {
|
||||
return m_boundaryReducedDofs;
|
||||
}
|
||||
|
||||
[[nodiscard]] const mfem::Array<int> &reduced_dof_marker() const noexcept {
|
||||
return m_boundaryReducedDofMarker;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool contains(const int reducedDof) const {
|
||||
if (reducedDof < 0 || reducedDof >= m_fieldReducedSize) {
|
||||
throw std::out_of_range("Reduced DOF index is outside FieldBoundaryDofMap.");
|
||||
}
|
||||
return m_boundaryReducedDofMarker[reducedDof] != 0;
|
||||
}
|
||||
|
||||
private:
|
||||
int m_fieldReducedSize{0};
|
||||
mfem::Array<int> m_boundaryReducedDofs;
|
||||
mfem::Array<int> m_boundaryReducedDofMarker;
|
||||
};
|
||||
|
||||
/* Point-supported rows in a field's reduced solver ordering. */
|
||||
class FieldPointDofMap final {
|
||||
public:
|
||||
FieldPointDofMap() = default;
|
||||
|
||||
FieldPointDofMap(
|
||||
const int fieldReducedSize,
|
||||
const mfem::Array<int> &pointReducedDofs
|
||||
)
|
||||
: m_selectedDofs(
|
||||
fieldReducedSize,
|
||||
pointReducedDofs
|
||||
) {
|
||||
}
|
||||
|
||||
[[nodiscard]] int field_size() const noexcept {
|
||||
return m_selectedDofs.field_size();
|
||||
}
|
||||
|
||||
[[nodiscard]] int size() const noexcept {
|
||||
return m_selectedDofs.size();
|
||||
}
|
||||
|
||||
[[nodiscard]] bool empty() const noexcept {
|
||||
return m_selectedDofs.empty();
|
||||
}
|
||||
|
||||
[[nodiscard]] const mfem::Array<int> &reduced_dofs() const noexcept {
|
||||
return m_selectedDofs.reduced_dofs();
|
||||
}
|
||||
|
||||
[[nodiscard]] const mfem::Array<int> &reduced_dof_marker() const noexcept {
|
||||
return m_selectedDofs.reduced_dof_marker();
|
||||
}
|
||||
|
||||
[[nodiscard]] bool contains(const int reducedDof) const {
|
||||
return m_selectedDofs.contains(reducedDof);
|
||||
}
|
||||
|
||||
private:
|
||||
FieldBoundaryDofMap m_selectedDofs;
|
||||
};
|
||||
|
||||
template <
|
||||
MfemDomainField FieldT,
|
||||
utils::domain::IsBoundary BoundaryT,
|
||||
utils::domain::IsSchema SchemaT>
|
||||
[[nodiscard]] FieldBoundaryDofMap make_field_boundary_dof_map(
|
||||
const mfem::ParFiniteElementSpace &finiteElementSpace,
|
||||
const FieldDofMap &fieldDofMap
|
||||
) {
|
||||
static_assert(
|
||||
SchemaT::template contains_boundary<BoundaryT>(),
|
||||
"The requested boundary is not registered in the supplied DomainSchema."
|
||||
);
|
||||
|
||||
MFEM_VERIFY(
|
||||
!finiteElementSpace.Nonconforming(),
|
||||
"Field boundary true-DOF resolution currently requires a conforming mfem::ParFiniteElementSpace."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
fieldDofMap.full_size() == finiteElementSpace.GetTrueVSize(),
|
||||
"The field map and finite-element space have incompatible true-DOF sizes."
|
||||
);
|
||||
|
||||
const mfem::Mesh *mesh = finiteElementSpace.GetMesh();
|
||||
MFEM_VERIFY(mesh != nullptr, "Field boundary DOF resolution requires an MFEM mesh.");
|
||||
|
||||
mfem::Array<int> boundaryVDofMarker(finiteElementSpace.GetVSize());
|
||||
boundaryVDofMarker = 0;
|
||||
|
||||
mfem::Array<int> boundaryElementVDofs;
|
||||
for (int boundaryElement = 0; boundaryElement < mesh->GetNBE(); ++boundaryElement) {
|
||||
if (!SchemaT::template boundary_attribute_matches<BoundaryT>(mesh->GetBdrAttribute(boundaryElement))) {
|
||||
continue;
|
||||
}
|
||||
|
||||
finiteElementSpace.GetBdrElementVDofs(boundaryElement, boundaryElementVDofs);
|
||||
for (const int encodedVDof : boundaryElementVDofs) {
|
||||
const int vdof = mfem::FiniteElementSpace::DecodeDof(encodedVDof);
|
||||
MFEM_VERIFY(
|
||||
vdof >= 0 && vdof < finiteElementSpace.GetVSize(), "MFEM returned an invalid boundary vector DOF."
|
||||
);
|
||||
boundaryVDofMarker[vdof] = 1;
|
||||
}
|
||||
}
|
||||
|
||||
finiteElementSpace.Synchronize(boundaryVDofMarker);
|
||||
|
||||
mfem::Array<int> boundaryReducedDofMarker(fieldDofMap.reduced_size());
|
||||
boundaryReducedDofMarker = 0;
|
||||
|
||||
for (int vdof = 0; vdof < boundaryVDofMarker.Size(); ++vdof) {
|
||||
if (boundaryVDofMarker[vdof] == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const int trueDof = finiteElementSpace.GetLocalTDofNumber(vdof);
|
||||
if (trueDof < 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const std::optional<int> reducedDof = fieldDofMap.reduced_dof(trueDof);
|
||||
MFEM_VERIFY(
|
||||
reducedDof.has_value(),
|
||||
"A boundary DOF selected for the field is absent from that field's reduced solver map."
|
||||
);
|
||||
boundaryReducedDofMarker[*reducedDof] = 1;
|
||||
}
|
||||
|
||||
mfem::Array<int> boundaryReducedDofs;
|
||||
mfem::FiniteElementSpace::MarkerToList(boundaryReducedDofMarker, boundaryReducedDofs);
|
||||
return FieldBoundaryDofMap(fieldDofMap.reduced_size(), boundaryReducedDofs);
|
||||
}
|
||||
|
||||
template <MfemDomainField FieldT>
|
||||
[[nodiscard]] FieldPointDofMap make_field_point_dof_map(
|
||||
const mfem::ParFiniteElementSpace &finiteElementSpace,
|
||||
const FieldDofMap &fieldDofMap,
|
||||
const mfem::Vector &point,
|
||||
const double tolerance
|
||||
) {
|
||||
MFEM_VERIFY(
|
||||
!finiteElementSpace.Nonconforming(),
|
||||
"Field point true-DOF resolution currently requires a conforming mfem::ParFiniteElementSpace."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
fieldDofMap.full_size() == finiteElementSpace.GetTrueVSize(),
|
||||
"The field map and finite-element space have incompatible true-DOF sizes."
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
std::isfinite(tolerance) && tolerance >= 0.0, "The field point tolerance must be finite and non-negative."
|
||||
);
|
||||
|
||||
const mfem::Mesh *mesh = finiteElementSpace.GetMesh();
|
||||
MFEM_VERIFY(mesh != nullptr, "Field point DOF resolution requires an MFEM mesh.");
|
||||
MFEM_VERIFY(
|
||||
point.Size() == mesh->SpaceDimension(), "The requested field point has the wrong coordinate dimension."
|
||||
);
|
||||
|
||||
mfem::Array<int> pointVDofMarker(finiteElementSpace.GetVSize());
|
||||
pointVDofMarker = 0;
|
||||
|
||||
mfem::Array<int> vertexVDofs;
|
||||
for (int vertex = 0; vertex < mesh->GetNV(); ++vertex) {
|
||||
const mfem::real_t *coordinates = mesh->GetVertex(vertex);
|
||||
double distanceSquared = 0.0;
|
||||
for (int component = 0; component < point.Size(); ++component) {
|
||||
const double difference = coordinates[component] - point(component);
|
||||
distanceSquared += difference * difference;
|
||||
}
|
||||
if (std::sqrt(distanceSquared) > tolerance) {
|
||||
continue;
|
||||
}
|
||||
|
||||
finiteElementSpace.GetVertexVDofs(vertex, vertexVDofs);
|
||||
for (const int encodedVDof : vertexVDofs) {
|
||||
const int vdof = mfem::FiniteElementSpace::DecodeDof(encodedVDof);
|
||||
MFEM_VERIFY(
|
||||
vdof >= 0 && vdof < finiteElementSpace.GetVSize(), "MFEM returned an invalid point vector DOF."
|
||||
);
|
||||
pointVDofMarker[vdof] = 1;
|
||||
}
|
||||
}
|
||||
|
||||
finiteElementSpace.Synchronize(pointVDofMarker);
|
||||
|
||||
mfem::Array<int> pointReducedDofMarker(fieldDofMap.reduced_size());
|
||||
pointReducedDofMarker = 0;
|
||||
for (int vdof = 0; vdof < pointVDofMarker.Size(); ++vdof) {
|
||||
if (pointVDofMarker[vdof] == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const int trueDof = finiteElementSpace.GetLocalTDofNumber(vdof);
|
||||
if (trueDof < 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const std::optional<int> reducedDof = fieldDofMap.reduced_dof(trueDof);
|
||||
MFEM_VERIFY(
|
||||
reducedDof.has_value(),
|
||||
"A point DOF selected for the field is absent from that field's reduced solver map."
|
||||
);
|
||||
pointReducedDofMarker[*reducedDof] = 1;
|
||||
}
|
||||
|
||||
mfem::Array<int> pointReducedDofs;
|
||||
mfem::FiniteElementSpace::MarkerToList(pointReducedDofMarker, pointReducedDofs);
|
||||
|
||||
const long long localPointDofCount = pointReducedDofs.Size();
|
||||
long long globalPointDofCount = 0;
|
||||
MPI_Allreduce(
|
||||
&localPointDofCount, &globalPointDofCount, 1, MPI_LONG_LONG, MPI_SUM, finiteElementSpace.GetComm()
|
||||
);
|
||||
MFEM_VERIFY(
|
||||
globalPointDofCount == finiteElementSpace.GetVDim(),
|
||||
"The requested geometric point must identify exactly one field vertex globally."
|
||||
);
|
||||
|
||||
return FieldPointDofMap(fieldDofMap.reduced_size(), pointReducedDofs);
|
||||
}
|
||||
|
||||
/*
|
||||
* Canonical adapter between an MFEM GridFunction and a reduced field
|
||||
* vector.
|
||||
@@ -1015,9 +1292,6 @@ export namespace mean_field::field {
|
||||
[[nodiscard]]
|
||||
FieldDofGridFunctionAdapter
|
||||
make_field_dof_grid_function_adapter(const mfem::ParFiniteElementSpace &finiteElementSpace) {
|
||||
return FieldDofGridFunctionAdapter(
|
||||
make_field_dof_map<FieldT, SchemaT>(finiteElementSpace),
|
||||
finiteElementSpace
|
||||
);
|
||||
return FieldDofGridFunctionAdapter(make_field_dof_map<FieldT, SchemaT>(finiteElementSpace), finiteElementSpace);
|
||||
}
|
||||
} // namespace mean_field::field
|
||||
|
||||
Reference in New Issue
Block a user