perf(allocations): reduced overall allocations by 95%, increaseed jacobian applicatin by 2x
This commit uses global pre allocated work space to dramatically reduce memory usage and allocation time
This commit is contained in:
@@ -167,6 +167,7 @@ namespace mean_field::mapping {
|
||||
|
||||
m_field_value.SetSize(dimension);
|
||||
m_field_jacobian.SetSize(dimension, dimension);
|
||||
m_reference_field_jacobian.SetSize(dimension, dimension);
|
||||
|
||||
m_compactification_point.coordinate = 0.0;
|
||||
m_compactification_point.coordinate_gradient.SetSize(dimension);
|
||||
@@ -538,6 +539,10 @@ namespace mean_field::mapping {
|
||||
return MappingStatus::valid;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Evaluate a displacement field dof matrix at a given integration point and compute what the displacement of that point is and what the gradient of the the displacement is with respect to the computational coordinates / reference frame.
|
||||
* @note There is actually nothing in this function preventing some field other than displacement from being passed through here; this should maybe be tightened.
|
||||
*/
|
||||
void DomainMapper::EvaluateField(
|
||||
const ElementDisplacementData &field,
|
||||
mfem::ElementTransformation &transformation,
|
||||
@@ -551,22 +556,30 @@ namespace mean_field::mapping {
|
||||
const mfem::DenseMatrix &dof_matrix = field.GetDofMatrix();
|
||||
|
||||
workspace.m_shape.SetSize(element.GetDof());
|
||||
workspace.m_mesh_dshape.SetSize(element.GetDof(), m_options.dimension);
|
||||
|
||||
element.CalcShape(integration_point, workspace.m_shape);
|
||||
if (inverse_mesh_jacobian != nullptr) {
|
||||
workspace.m_reference_dshape.SetSize(element.GetDof(), m_options.dimension);
|
||||
element.CalcDShape(integration_point, workspace.m_reference_dshape);
|
||||
mfem::Mult(workspace.m_reference_dshape, *inverse_mesh_jacobian, workspace.m_mesh_dshape);
|
||||
} else {
|
||||
element.CalcPhysDShape(transformation, workspace.m_mesh_dshape);
|
||||
}
|
||||
|
||||
value.SetSize(m_options.dimension);
|
||||
dof_matrix.MultTranspose(workspace.m_shape, value);
|
||||
|
||||
jacobian.SetSize(m_options.dimension, m_options.dimension);
|
||||
mfem::MultAtB(dof_matrix, workspace.m_mesh_dshape, jacobian);
|
||||
|
||||
if (inverse_mesh_jacobian != nullptr || element.GetMapType() == mfem::FiniteElement::VALUE) {
|
||||
workspace.m_reference_dshape.SetSize(element.GetDof(), m_options.dimension);
|
||||
element.CalcDShape(integration_point, workspace.m_reference_dshape);
|
||||
// Contract DOFs before applying fixed-mesh geometry. This is the
|
||||
// same DOF^T * (Dshape * J_mesh^-1), without transforming every
|
||||
// basis gradient. The scratch matrix must not alias the cached
|
||||
// inverse supplied by EvaluateVolumeVariation.
|
||||
mfem::MultAtB(dof_matrix, workspace.m_reference_dshape, workspace.m_reference_field_jacobian);
|
||||
const mfem::DenseMatrix &inverseMeshJacobian =
|
||||
inverse_mesh_jacobian != nullptr ? *inverse_mesh_jacobian : transformation.InverseJacobian();
|
||||
mfem::Mult(workspace.m_reference_field_jacobian, inverseMeshJacobian, jacobian);
|
||||
} else {
|
||||
// Retain the original finite-element-specific physical-gradient
|
||||
// path for mapping types without the ordinary VALUE pullback.
|
||||
workspace.m_mesh_dshape.SetSize(element.GetDof(), m_options.dimension);
|
||||
element.CalcPhysDShape(transformation, workspace.m_mesh_dshape);
|
||||
mfem::MultAtB(dof_matrix, workspace.m_mesh_dshape, jacobian);
|
||||
}
|
||||
}
|
||||
|
||||
MappingStatus DomainMapper::EvaluatePoint(
|
||||
@@ -594,6 +607,7 @@ namespace mean_field::mapping {
|
||||
context.reference_position.SetSize(m_options.dimension);
|
||||
transformation.Transform(integration_point, context.reference_position);
|
||||
|
||||
// Get the displacement field value and its Jacobian at the integration point. Note these are in the workspace to avoid repeated allocations.
|
||||
EvaluateField(
|
||||
element_data.displacement, transformation, integration_point, workspace, workspace.m_field_value,
|
||||
workspace.m_field_jacobian, nullptr
|
||||
@@ -605,17 +619,32 @@ namespace mean_field::mapping {
|
||||
}
|
||||
|
||||
context.displaced_position.SetSize(m_options.dimension);
|
||||
|
||||
// Get the position of the point in physical space by adding the displacement to the reference position. Note MFEM really dislikes raw arithmetic operators
|
||||
// so we need to first assign the reference position then use the in place += operator.
|
||||
context.displaced_position = context.reference_position;
|
||||
context.displaced_position += workspace.m_field_value;
|
||||
|
||||
context.displacement_jacobian.SetSize(m_options.dimension, m_options.dimension);
|
||||
context.displacement_jacobian = workspace.m_field_jacobian;
|
||||
|
||||
// Ensure that the diagonal of the displacement Jacobian is incremented by 1.0 to account for the identity mapping from reference to physical space.
|
||||
// recall that r = x + d (where d is the workspace.m_field_value and x is context.reference_position) then we can differentiate this
|
||||
// component wise to find the gradient of the displaced position wrt. the mesh coordinate (reference position). E.g as you move along
|
||||
// the mesh coordinate how much does the physical coordinate change and in what direction. Lets call this F
|
||||
// F = \frac{\partial r_i}{\partial x_j} where r is the displaced position and x is the mesh position.
|
||||
// We then have F = \frac{\partial x_i}{\partial x_j} + \frac{\partial d_i}{x_j} where d is the displacement (recall r = x + d)
|
||||
// By definition the first term is the identity matrix. The second term we get out of EvaluateField. Thus why we need to add the identity matrix here
|
||||
for (int i = 0; i < m_options.dimension; ++i)
|
||||
context.displacement_jacobian(i, i) += 1.0;
|
||||
|
||||
context.compactified = IsCompactifiedElement(transformation);
|
||||
|
||||
// This branch only runs for vacuum elements
|
||||
if (context.compactified) {
|
||||
// There are two things that we need to the mapping. First is a reference coordinate which stroid embeds into the mesh at mesh generation time, this is
|
||||
// parameterized from 0 - 1 where 0 is the model surface and 1 is the mesh exterior (what will becomes the compactified infinity, note also we never actually evaluate at s=1; rather we define some arbitrary small tolerance to approach s=1). Lets call this s. We also need
|
||||
// the gradient of s as we move along the mesh coordinates. All of this is stashes within workspace.m_compactification_point.
|
||||
const MappingStatus coordinate_status = EvaluateCompactificationCoordinate(
|
||||
element_data.compactification, transformation, integration_point, workspace,
|
||||
workspace.m_compactification_point, nullptr
|
||||
@@ -632,6 +661,8 @@ namespace mean_field::mapping {
|
||||
.compactification_coordinate_gradient = workspace.m_compactification_point.coordinate_gradient
|
||||
};
|
||||
|
||||
// This apply whatever the exterior map is to generate the new physical exterior coordinate and jacobian between physical and reference space.
|
||||
// In general we have only implemented a kelvin mapping; however, in future additional mappings may be implemented.
|
||||
const MappingStatus exterior_status = m_exterior_map->Evaluate(exterior_input, workspace.m_exterior_result);
|
||||
if (exterior_status != MappingStatus::valid)
|
||||
return exterior_status;
|
||||
@@ -643,6 +674,7 @@ namespace mean_field::mapping {
|
||||
context.mapping_jacobian = context.displacement_jacobian;
|
||||
}
|
||||
|
||||
// Validation work
|
||||
if (!vector_is_finite(context.physical_position) || !matrix_is_finite(context.mapping_jacobian))
|
||||
return MappingStatus::non_finite_result;
|
||||
|
||||
@@ -650,9 +682,12 @@ namespace mean_field::mapping {
|
||||
if (!std::isfinite(context.mapping_determinant))
|
||||
return MappingStatus::non_finite_result;
|
||||
if (context.mapping_determinant <= 0.0)
|
||||
// This is the most common error we see come out of this function, specifically it is common when we try to deform the mesh too much in one step.
|
||||
return MappingStatus::non_positive_determinant;
|
||||
|
||||
context.inverse_mapping_jacobian.SetSize(m_options.dimension, m_options.dimension);
|
||||
|
||||
// It can be useful to have the inverse jacobian, here we just use MFEM's build in inverse tooling.
|
||||
mfem::CalcInverse(context.mapping_jacobian, context.inverse_mapping_jacobian);
|
||||
|
||||
if (!matrix_is_finite(context.inverse_mapping_jacobian))
|
||||
|
||||
Reference in New Issue
Block a user