Skip to content

Commit 06562dd

Browse files
authored
Merge pull request #50 from hsorby/main
Add calculate_jacobian and report_on_lowest_value functions.
2 parents 584c47c + 90296c5 commit 06562dd

File tree

5 files changed

+537
-1
lines changed

5 files changed

+537
-1
lines changed

src/cmlibs/utils/zinc/field.py

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -723,6 +723,51 @@ def find_coordinate_fields(region):
723723
return field_list
724724

725725

726+
def _is_3_component_real_valued_field(field):
727+
return field is not None and field.isValid() and (field.getNumberOfComponents() == 3) and (field.getValueType() == Field.VALUE_TYPE_REAL)
728+
729+
730+
def create_jacobian_determinant_field(coordinates, reference_coordinates, name=None):
731+
"""
732+
Create the Jacobian determinant of a 3-component coordinate field
733+
w.r.t. 3-D element reference coordinates.
734+
This value should always be positive for valid right-handed elements, negative if the
735+
element volume becomes negative or is left-handed w.r.t. the reference coordinates.
736+
If the coordinate field or reference field is not suitable for calculating
737+
the Jacobian determinant, the function returns None.
738+
:param coordinates: Geometric coordinate field.
739+
:param reference_coordinates: Reference geometric coordinate field.
740+
:param name: String to set as the name of the field.
741+
:return: Jacobian determinant field.
742+
"""
743+
jacobian_determinant = None
744+
if _is_3_component_real_valued_field(coordinates) and _is_3_component_real_valued_field(reference_coordinates):
745+
fm = coordinates.getFieldmodule()
746+
with ChangeManager(fm):
747+
jacobian_determinant = fm.createFieldDeterminant(fm.createFieldGradient(coordinates, reference_coordinates))
748+
if name is not None:
749+
jacobian_determinant.setName(name)
750+
751+
return jacobian_determinant
752+
753+
754+
def create_xi_reference_jacobian_determinant_field(coordinates, name=None):
755+
"""
756+
Calculate the Jacobian determinant w.r.t the 'xi' field.
757+
Returns None if the coordinates field is not a valid field.
758+
See also :func:create_jacobian_determinant_field.
759+
:param coordinates: Geometric coordinate field.
760+
:param name: String to set as the name of the field, optional defaults to 'Jacobian_determinant_wrt_xi'.
761+
:return: Jacobian determinant field.
762+
"""
763+
jacobian_determinant = None
764+
if isinstance(coordinates, Field) and coordinates.isValid():
765+
fm = coordinates.getFieldmodule()
766+
jacobian_determinant = create_jacobian_determinant_field(coordinates, fm.findFieldByName("xi"), "Jacobian_determinant_wrt_xi" if name is None else name)
767+
768+
return jacobian_determinant
769+
770+
726771
# Create C++ style aliases for names of functions.
727772
createFieldsDisplacementGradients = create_fields_displacement_gradients
728773
createFieldsTransformations = create_fields_transformations

src/cmlibs/utils/zinc/finiteelement.py

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
"""
22
Utilities for creating and working with Zinc Finite Elements.
33
"""
4+
import math
5+
46
from cmlibs.maths import vectorops
57
from cmlibs.utils.zinc.general import ChangeManager
68
from cmlibs.zinc.element import Element, Elementbasis, Elementfieldtemplate, Mesh
@@ -617,6 +619,47 @@ def interpolate_cubic_hermite_derivative(v1, d1, v2, d2, xi):
617619
return [(f1 * v1[i] + f2 * d1[i] + f3 * v2[i] + f4 * d2[i]) for i in range(len(v1))]
618620

619621

622+
def _is_real_scalar_field(field):
623+
return field is not None and field.isValid() and (field.getValueType() == Field.VALUE_TYPE_REAL) and (field.getNumberOfComponents() == 1)
624+
625+
626+
def get_scalar_field_minimum_in_mesh(real_field, mesh=None):
627+
"""
628+
Evaluate the 3D mesh associated with the given (real) field and
629+
report back on the element with the lowest field value. If the optional
630+
mesh (or mesh_group) is not given then the highest dimension mesh available will be used.
631+
Returns (-1, inf) if the evaluation is invalid.
632+
:param real_field: A real scalar value field to find the lowest value of.
633+
:param mesh: A mesh or mesh_group (optional, default None).
634+
:return: A tuple of element identifier and minimum value within that element.
635+
"""
636+
minimum_element_id = -1
637+
minimum_element_value = math.inf
638+
if _is_real_scalar_field(real_field):
639+
fm = real_field.getFieldmodule()
640+
fc = fm.createFieldcache()
641+
fr = fc.createFieldrange()
642+
643+
in_use_mesh = mesh if mesh else get_highest_dimension_mesh(fm)
644+
if in_use_mesh is None or not hasattr(in_use_mesh, "createElementiterator"):
645+
return minimum_element_id, minimum_element_value
646+
647+
element_iter = in_use_mesh.createElementiterator()
648+
element = element_iter.next()
649+
while element.isValid():
650+
fc.setElement(element)
651+
result = real_field.evaluateFieldrange(fc, fr)
652+
if result == RESULT_OK:
653+
result, min_value, max_value = fr.getRangeReal(1)
654+
if result == RESULT_OK and min_value < minimum_element_value:
655+
minimum_element_id = element.getIdentifier()
656+
minimum_element_value = min_value
657+
658+
element = element_iter.next()
659+
660+
return minimum_element_id, minimum_element_value
661+
662+
620663
createCubeElement = create_cube_element
621664
createSquareElement = create_square_element
622665
createLineElement = create_line_element

src/cmlibs/utils/zinc/mesh.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -181,7 +181,6 @@ def _find_connected(element_nodes, seed_index=None, ignore_element_indices=None,
181181

182182
update_indexes = {}
183183
if progress_callback is not None:
184-
185184
update_interval = max(1, int(num_elements * 0.01))
186185
update_indexes = set([i for i in range(update_interval)] + [i for i in range(update_interval, num_elements, update_interval)])
187186

0 commit comments

Comments
 (0)