def create_fields_displacement_gradients(coordinates: Field, reference_coordinates: Field, mesh: Mesh): """ :return: 1st and 2nd displacement gradients of (coordinates - referenceCoordinates) w.r.t. referenceCoordinates. """ assert (coordinates.getNumberOfComponents() == 3) and (reference_coordinates.getNumberOfComponents() == 3) fieldmodule = mesh.getFieldmodule() dimension = mesh.getDimension() with ChangeManager(fieldmodule): if dimension == 3: u = coordinates - reference_coordinates displacement_gradient = fieldmodule.createFieldGradient( u, reference_coordinates) displacement_gradient2 = fieldmodule.createFieldGradient( displacement_gradient, reference_coordinates) elif dimension == 2: # Note this needs improvement as missing cross terms # assume xi directions are approximately normal; # effect is to penalise elements where this is not so, which is also desired dX_dxi1 = fieldmodule.createFieldDerivative( reference_coordinates, 1) dX_dxi2 = fieldmodule.createFieldDerivative( reference_coordinates, 2) dx_dxi1 = fieldmodule.createFieldDerivative(coordinates, 1) dx_dxi2 = fieldmodule.createFieldDerivative(coordinates, 2) dS1_dxi1 = fieldmodule.createFieldMagnitude(dX_dxi1) dS2_dxi2 = fieldmodule.createFieldMagnitude(dX_dxi2) du_dS1 = (dx_dxi1 - dX_dxi1) / dS1_dxi1 du_dS2 = (dx_dxi2 - dX_dxi2) / dS2_dxi2 displacement_gradient = fieldmodule.createFieldConcatenate( [du_dS1, du_dS2]) # curvature: d2u_dSdxi1 = fieldmodule.createFieldDerivative( displacement_gradient, 1) d2u_dSdxi2 = fieldmodule.createFieldDerivative( displacement_gradient, 2) displacement_gradient2 = fieldmodule.createFieldConcatenate( [d2u_dSdxi1 / dS1_dxi1, d2u_dSdxi2 / dS2_dxi2]) else: # dimension == 1 dX_dxi1 = fieldmodule.createFieldDerivative( reference_coordinates, 1) dx_dxi1 = fieldmodule.createFieldDerivative(coordinates, 1) dS1_dxi1 = fieldmodule.createFieldMagnitude(dX_dxi1) displacement_gradient = (dx_dxi1 - dX_dxi1) / dS1_dxi1 # curvature: displacement_gradient2 = fieldmodule.createFieldDerivative( displacement_gradient, 1) / dS1_dxi1 return displacement_gradient, displacement_gradient2
def field_is_managed_real_1_to_3_components(field_in: Field): """ Conditional function returning True if the field is real-valued with up to 3 components, and is managed. """ return (field_in.getValueType() == Field.VALUE_TYPE_REAL) and \ (field_in.getNumberOfComponents() <= 3) and field_in.isManaged()
def create_fields_transformations(coordinates: Field, rotation_angles=None, scale_value=1.0, translation_offsets=None): """ Create constant fields for rotation, scale and translation containing the supplied values, plus the transformed coordinates applying them in the supplied order. :param coordinates: The coordinate field to scale, 3 components. :param rotation_angles: List of euler angles, length = number of components. See create_field_euler_angles_rotation_matrix. :param scale_value: Scalar to multiply all components of coordinates. :param translation_offsets: List of offsets, length = number of components. :return: 4 fields: transformedCoordinates, rotation, scale, translation """ if rotation_angles is None: rotation_angles = [0.0, 0.0, 0.0] if translation_offsets is None: translation_offsets = [0.0, 0.0, 0.0] components_count = coordinates.getNumberOfComponents() assert (components_count == 3) and (len(rotation_angles) == components_count) and isinstance(scale_value, float) \ and (len(translation_offsets) == components_count), "createTransformationFields. Invalid arguments" fieldmodule = coordinates.getFieldmodule() with ChangeManager(fieldmodule): # scale, translate and rotate model, in that order rotation = fieldmodule.createFieldConstant(rotation_angles) scale = fieldmodule.createFieldConstant(scale_value) translation = fieldmodule.createFieldConstant(translation_offsets) rotation_matrix = create_field_euler_angles_rotation_matrix( fieldmodule, rotation) rotated_coordinates = fieldmodule.createFieldMatrixMultiply( components_count, rotation_matrix, coordinates) transformed_coordinates = rotated_coordinates * scale + translation assert transformed_coordinates.isValid() return transformed_coordinates, rotation, scale, translation
def createFieldsTransformations(coordinates: Field, rotation_angles=None, scale_value=1.0, \ translation_offsets=None, translation_scale_factor=1.0): """ Create constant fields for rotation, scale and translation containing the supplied values, plus the transformed coordinates applying them in the supplied order. :param coordinates: The coordinate field to scale, 3 components. :param rotation_angles: List of euler angles, length = number of components. See create_field_euler_angles_rotation_matrix. :param scale_value: Scalar to multiply all components of coordinates. :param translation_offsets: List of offsets, length = number of components. :param translation_scale_factor: Scaling to multiply translation by so it's magnitude can remain close to other parameters for rotation (radians) and scale (assumed close to unit). :return: 4 fields: transformedCoordinates, rotation, scale, translation """ if rotation_angles is None: rotation_angles = [0.0, 0.0, 0.0] if translation_offsets is None: translation_offsets = [0.0, 0.0, 0.0] components_count = coordinates.getNumberOfComponents() assert (components_count == 3) and (len(rotation_angles) == components_count) and isinstance(scale_value, float) \ and (len(translation_offsets) == components_count), "createFieldsTransformations. Invalid arguments" fieldmodule = coordinates.getFieldmodule() with ChangeManager(fieldmodule): # Rotate, scale, and translate model, in that order rotation = fieldmodule.createFieldConstant(rotation_angles) scale = fieldmodule.createFieldConstant(scale_value) translation = fieldmodule.createFieldConstant(translation_offsets) rotation_matrix = create_field_euler_angles_rotation_matrix( fieldmodule, rotation) rotated_coordinates = fieldmodule.createFieldMatrixMultiply( components_count, rotation_matrix, coordinates) transformed_coordinates = rotated_coordinates*scale + (translation if (translation_scale_factor == 1.0) else \ translation*fieldmodule.createFieldConstant([ translation_scale_factor ]*components_count)) assert transformed_coordinates.isValid() return transformed_coordinates, rotation, scale, translation
def field_is_managed_coordinates(field_in: Field): """ Conditional function returning True if the field is Finite Element type with 3 components, and is managed. """ return field_in.castFiniteElement().isValid() and ( field_in.getNumberOfComponents() == 3) and field_in.isManaged()
def field_is_managed_coordinates(field_in: Field): """ Conditional function returning True if the field is Finite Element type, with coordinate type attribute, up to 3 components, and is managed. """ return (field_in is not None) and field_in.isManaged() and\ (field_in.getNumberOfComponents() <= 3) and\ field_in.castFiniteElement().isValid() and field_in.isTypeCoordinate()
def evaluate_field_nodeset_mean(field: Field, nodeset: Nodeset): """ :return: Mean of field over nodeset. """ fieldmodule = nodeset.getFieldmodule() components_count = field.getNumberOfComponents() with ChangeManager(fieldmodule): mean_field = fieldmodule.createFieldNodesetMean(field, nodeset) fieldcache = fieldmodule.createFieldcache() result, mean_values = mean_field.evaluateReal(fieldcache, components_count) assert result == RESULT_OK del mean_field del fieldcache return mean_values
def getNodesetConditionalSize(nodeset: Nodeset, conditionalField: Field): """ :return: Number of objects in nodeset for which conditionalField is True. """ assert conditionalField.getNumberOfComponents() == 1 fieldmodule = conditionalField.getFieldmodule() fieldcache = fieldmodule.createFieldcache() nodeiterator = nodeset.createNodeiterator() size = 0 node = nodeiterator.next() while node.isValid(): fieldcache.setNode(node) result, value = conditionalField.evaluateReal(fieldcache, 1) if value != 0.0: size += 1 node = nodeiterator.next() return size
def get_node_name_centres(nodeset: Nodeset, coordinates_field: Field, name_field: Field): """ Find mean locations of node coordinate with the same names. :param nodeset: Zinc Nodeset or NodesetGroup to search. :param coordinates_field: The coordinate field to evaluate. :param name_field: The name field to match. :return: Dict of names -> coordinates. """ components_count = coordinates_field.getNumberOfComponents() fieldmodule = nodeset.getFieldmodule() fieldcache = fieldmodule.createFieldcache() name_records = {} # name -> (coordinates, count) nodeiter = nodeset.createNodeiterator() node = nodeiter.next() while node.isValid(): fieldcache.setNode(node) name = name_field.evaluateString(fieldcache) coordinates_result, coordinates = coordinates_field.evaluateReal( fieldcache, components_count) if name and (coordinates_result == RESULT_OK): name_record = name_records.get(name) if name_record: name_centre = name_record[0] for c in range(components_count): name_centre[c] += coordinates[c] name_record[1] += 1 else: name_records[name] = [coordinates, 1] node = nodeiter.next() # divide centre coordinates by count name_centres = {} for name in name_records: name_record = name_records[name] name_count = name_record[1] name_centre = name_record[0] if name_count > 1: scale = 1.0 / name_count for c in range(components_count): name_centre[c] *= scale name_centres[name] = name_centre return name_centres
def evaluate_field_nodeset_range(field: Field, nodeset: Nodeset): """ :return: min, max range of field over nodes. """ fieldmodule = nodeset.getFieldmodule() components_count = field.getNumberOfComponents() with ChangeManager(fieldmodule): min_field = fieldmodule.createFieldNodesetMinimum(field, nodeset) max_field = fieldmodule.createFieldNodesetMaximum(field, nodeset) fieldcache = fieldmodule.createFieldcache() result, min_values = min_field.evaluateReal(fieldcache, components_count) assert result == RESULT_OK result, max_values = max_field.evaluateReal(fieldcache, components_count) assert result == RESULT_OK del min_field del max_field del fieldcache return min_values, max_values
def evaluate_field_mesh_integral(field: Field, coordinates: Field, mesh: Mesh, number_of_points=4): """ Integrate value of a field over mesh using Gaussian Quadrature. :param field: Field to integrate over mesh. :param coordinates: Field giving spatial coordinates to integrate over. :param mesh: The mesh or mesh group to integrate over. :param number_of_points: Number of integration points in each dimension. :return: Integral value. """ fieldmodule = mesh.getFieldmodule() components_count = field.getNumberOfComponents() with ChangeManager(fieldmodule): integral = fieldmodule.createFieldMeshIntegral(field, coordinates, mesh) integral.setNumbersOfPoints(number_of_points) fieldcache = fieldmodule.createFieldcache() result, value = integral.evaluateReal(fieldcache, components_count) del integral del fieldcache assert result == RESULT_OK return value
def create_field_euler_angles_rotation_matrix(fieldmodule: Fieldmodule, euler_angles: Field) -> Field: """ From OpenCMISS-Zinc graphics_library.cpp, matrix transposed to row major. Matrix is product RzRyRx, giving rotation about x, then y, then z with positive angles rotating by right hand rule about axis. :param fieldmodule: The fieldmodule to create the field in. :param euler_angles: 3-component field of angles in radians, components: 0 = azimuth (about z) 1 = elevation (about y) 2 = roll (about x) :return: 3x3 rotation matrix field suitable for pre-multiplying vector v i.e. v' = Mv """ assert euler_angles.getNumberOfComponents() == 3 with ChangeManager(fieldmodule): azimuth = fieldmodule.createFieldComponent(euler_angles, 1) cos_azimuth = fieldmodule.createFieldCos(azimuth) sin_azimuth = fieldmodule.createFieldSin(azimuth) elevation = fieldmodule.createFieldComponent(euler_angles, 2) cos_elevation = fieldmodule.createFieldCos(elevation) sin_elevation = fieldmodule.createFieldSin(elevation) roll = fieldmodule.createFieldComponent(euler_angles, 3) cos_roll = fieldmodule.createFieldCos(roll) sin_roll = fieldmodule.createFieldSin(roll) minus_one = fieldmodule.createFieldConstant([-1.0]) cos_azimuth_sin_elevation = cos_azimuth * sin_elevation sin_azimuth_sin_elevation = sin_azimuth * sin_elevation matrix_components = [ cos_azimuth * cos_elevation, cos_azimuth_sin_elevation * sin_roll - sin_azimuth * cos_roll, cos_azimuth_sin_elevation * cos_roll + sin_azimuth * sin_roll, sin_azimuth * cos_elevation, sin_azimuth_sin_elevation * sin_roll + cos_azimuth * cos_roll, sin_azimuth_sin_elevation * cos_roll - cos_azimuth * sin_roll, minus_one * sin_elevation, cos_elevation * sin_roll, cos_elevation * cos_roll ] rotation_matrix = fieldmodule.createFieldConcatenate(matrix_components) return rotation_matrix
def create_field_euler_angles_rotation_matrix(fieldmodule: Fieldmodule, euler_angles: Field) -> Field: """ From OpenCMISS-Zinc graphics_library.cpp, transposed. :param fieldmodule: :param euler_angles: 3-component field of angles in radians, components: 1 = azimuth (about z) 2 = elevation (about rotated y) 3 = roll (about rotated x) :return: 3x3 rotation matrix field suitable for pre-multiplying [x, y, z]. """ assert euler_angles.getNumberOfComponents() == 3 with ChangeManager(fieldmodule): azimuth = fieldmodule.createFieldComponent(euler_angles, 1) cos_azimuth = fieldmodule.createFieldCos(azimuth) sin_azimuth = fieldmodule.createFieldSin(azimuth) elevation = fieldmodule.createFieldComponent(euler_angles, 2) cos_elevation = fieldmodule.createFieldCos(elevation) sin_elevation = fieldmodule.createFieldSin(elevation) roll = fieldmodule.createFieldComponent(euler_angles, 3) cos_roll = fieldmodule.createFieldCos(roll) sin_roll = fieldmodule.createFieldSin(roll) minus_one = fieldmodule.createFieldConstant([-1.0]) cos_azimuth_sin_elevation = cos_azimuth*sin_elevation sin_azimuth_sin_elevation = sin_azimuth*sin_elevation matrix_components = [ cos_azimuth*cos_elevation, cos_azimuth_sin_elevation*sin_roll - sin_azimuth*cos_roll, cos_azimuth_sin_elevation*cos_roll + sin_azimuth*sin_roll, sin_azimuth*cos_elevation, sin_azimuth_sin_elevation*sin_roll + cos_azimuth*cos_roll, sin_azimuth_sin_elevation*cos_roll - cos_azimuth*sin_roll, minus_one*sin_elevation, cos_elevation*sin_roll, cos_elevation*cos_roll] rotation_matrix = fieldmodule.createFieldConcatenate(matrix_components) return rotation_matrix
def transform_coordinates(field: Field, rotation_scale, offset, time=0.0) -> bool: """ Transform finite element field coordinates by matrix and offset, handling nodal derivatives and versions. Limited to nodal parameters, rectangular cartesian coordinates :param field: the coordinate field to transform :param rotation_scale: square transformation matrix 2-D array with as many rows and columns as field components. :param offset: coordinates offset. :param time: time value. :return: True on success, otherwise false. """ ncomp = field.getNumberOfComponents() if (ncomp != 2) and (ncomp != 3): print( 'zinc.transformCoordinates: field has invalid number of components' ) return False if (len(rotation_scale) != ncomp) or (len(offset) != ncomp): print( 'zinc.transformCoordinates: invalid matrix number of columns or offset size' ) return False for matRow in rotation_scale: if len(matRow) != ncomp: print( 'zinc.transformCoordinates: invalid matrix number of columns') return False if field.getCoordinateSystemType( ) != Field.COORDINATE_SYSTEM_TYPE_RECTANGULAR_CARTESIAN: print('zinc.transformCoordinates: field is not rectangular cartesian') return False fe_field = field.castFiniteElement() if not fe_field.isValid(): print( 'zinc.transformCoordinates: field is not finite element field type' ) return False success = True fm = field.getFieldmodule() fm.beginChange() cache = fm.createFieldcache() cache.setTime(time) nodes = fm.findNodesetByFieldDomainType(Field.DOMAIN_TYPE_NODES) node_template = nodes.createNodetemplate() node_iter = nodes.createNodeiterator() node = node_iter.next() while node.isValid(): node_template.defineFieldFromNode(fe_field, node) cache.setNode(node) for derivative in [ Node.VALUE_LABEL_VALUE, Node.VALUE_LABEL_D_DS1, Node.VALUE_LABEL_D_DS2, Node.VALUE_LABEL_D2_DS1DS2, Node.VALUE_LABEL_D_DS3, Node.VALUE_LABEL_D2_DS1DS3, Node.VALUE_LABEL_D2_DS2DS3, Node.VALUE_LABEL_D3_DS1DS2DS3 ]: versions = node_template.getValueNumberOfVersions( fe_field, -1, derivative) for v in range(versions): result, values = fe_field.getNodeParameters( cache, -1, derivative, v + 1, ncomp) if result != RESULT_OK: success = False else: new_values = vectorops.matrixvectormult( rotation_scale, values) if derivative == Node.VALUE_LABEL_VALUE: new_values = vectorops.add(new_values, offset) result = fe_field.setNodeParameters( cache, -1, derivative, v + 1, new_values) if result != RESULT_OK: success = False node = node_iter.next() fm.endChange() if not success: print('zinc.transformCoordinates: failed to get/set some values') return success