diff --git a/src/cmlibs/utils/zinc/field.py b/src/cmlibs/utils/zinc/field.py index 8e88638..360bf35 100644 --- a/src/cmlibs/utils/zinc/field.py +++ b/src/cmlibs/utils/zinc/field.py @@ -554,7 +554,7 @@ def create_field_stored_mesh_location(fieldmodule: Fieldmodule, mesh: Mesh, name :param fieldmodule: Zinc fieldmodule to find or create field in. :param mesh: Mesh to store locations in, from same fieldmodule. - :param name: Name of new field. If not defined, defaults to "location\_" + mesh.getName(). + :param name: Name of new field. If not defined, defaults to "location_" + mesh.getName(). :param managed: Managed state of field. :return: Zinc FieldStoredMeshLocation """ @@ -576,7 +576,7 @@ def find_or_create_field_stored_mesh_location(fieldmodule: Fieldmodule, mesh: Me :param fieldmodule: Zinc fieldmodule to find or create field in. :param mesh: Mesh to store locations in, from same fieldmodule. - :param name: Name of new field. If not defined, defaults to "location\_" + mesh.getName(). + :param name: Name of new field. If not defined, defaults to "location_" + mesh.getName(). :param managed: Managed state of field if created here. """ if not name: diff --git a/src/cmlibs/utils/zinc/general.py b/src/cmlibs/utils/zinc/general.py index 6da5de1..a8971d5 100644 --- a/src/cmlibs/utils/zinc/general.py +++ b/src/cmlibs/utils/zinc/general.py @@ -124,7 +124,7 @@ def create_node(field_module, data_object, identifier=-1, node_set_name='nodes', """ Create a Node in the field_module using the data_object. The data object must supply a 'get_field_names' method and a 'get_time_sequence' method. Derive a node data object from the 'AbstractNodeDataObject' class to ensure - that the data object class meets it's requirements. + that the data object class meets its requirements. Optionally use the identifier to set the identifier of the Node created, the time parameter to set the time value in the cache, or the node_set_name to specify which node set to use the default node set @@ -162,6 +162,7 @@ def create_node(field_module, data_object, identifier=-1, node_set_name='nodes', for i, field in enumerate(fields): field_name = field_names[i] field_value = getattr(data_object, field_name)() + # print(field_name, type(field_value), field_value, isinstance(field_value, ("".__class__, u"".__class__))) if isinstance(field_value, ("".__class__, u"".__class__)): field.assignString(field_cache, field_value) else: diff --git a/src/cmlibs/utils/zinc/group.py b/src/cmlibs/utils/zinc/group.py index f7e8cfb..43ac3dd 100644 --- a/src/cmlibs/utils/zinc/group.py +++ b/src/cmlibs/utils/zinc/group.py @@ -503,20 +503,25 @@ def match_fitting_group_names(data_fieldmodule, model_fieldmodule, log_diagnosti :param data_fieldmodule: Data Fieldmodule whose group names may be modified. :param model_fieldmodule: Model Fieldmodule containing preferred group names. :param log_diagnostics: Set to True to write diagonstic messages about name matches and changes to logging. + :return: A dictionary of matched group names. """ # future: match with annotation terms model_names = [group.getName() for group in get_group_list(model_fieldmodule)] + matched_names = {} for data_group in get_group_list(data_fieldmodule): data_name = data_group.getName() compare_name = data_name.strip().casefold() for model_name in model_names: + # print('comparing "%s" to "%s" or "%s"' % (data_name, model_name, compare_name)) if model_name == data_name: + matched_names[model_name] = (data_name, None) if log_diagnostics: logger.info("Data group '" + data_name + "' found in model") break elif model_name.strip().casefold() == compare_name: result = data_group.setName(model_name) if result == RESULT_OK: + matched_names[model_name] = (data_name, compare_name) if log_diagnostics: logger.info("Data group '" + data_name + "' found in model as '" + model_name + "'. Renaming to match.") @@ -527,5 +532,8 @@ def match_fitting_group_names(data_fieldmodule, model_fieldmodule, log_diagnosti logger.error(" Reason: field of that name already exists.") break else: + # print('Data group "' + data_name + '" not found in model') if log_diagnostics: logger.info("Data group '" + data_name + "' not found in model") + + return matched_names diff --git a/src/cmlibs/utils/zinc/region.py b/src/cmlibs/utils/zinc/region.py index 429217f..98c9e8f 100644 --- a/src/cmlibs/utils/zinc/region.py +++ b/src/cmlibs/utils/zinc/region.py @@ -11,18 +11,19 @@ def _find_missing(lst): def convert_nodes_to_datapoints(target_region, source_region, source_nodeset_type=Field.DOMAIN_TYPE_NODES, - destroy_after_conversion=True): + destroy_after_conversion=True, field_names=None): """ + When the source nodeset type is Field.DOMAIN_TYPE_DATAPOINTS, then datapoints are transferred from the Converts nodes in the source region to datapoints in the target region, renumbering any existing datapoints in target region to not clash. - When the source nodeset type is Field.DOMAIN_TYPE_DATAPOINTS, then datapoints are transferred from the source region to the target region. :param target_region: Zinc Region to read data into. Existing data points are renumbered to avoid nodes. :param source_region: Zinc Region containing nodes to transfer. - :param source_nodeset_type: Set to Field.DOMAIN_TYPE_DATAPOINTS or Field.DOMAIN_TYPE_NODES to transfer datapoints + :param source_nodeset_type: Set to Field.DOMAIN_TYPE_DATAPOINTS or Field.DOMAIN_TYPE_NODES to transfer datapoints or convert nodes. Datapoint transfer should only be to different regions [default: Field.DOMAIN_TYPE_NODES]. - :param destroy_after_conversion: Set to True to destroy nodes that have been successfully converted, or False + :param destroy_after_conversion: Set to True to destroy nodes that have been successfully converted, or False to leave intact in source region [default: True]. + :param field_names: A list of field names to output. """ source_fieldmodule = source_region.getFieldmodule() target_fieldmodule = target_region.getFieldmodule() @@ -46,11 +47,14 @@ def convert_nodes_to_datapoints(target_region, source_region, source_nodeset_typ datapoint_identifier = datapoint.getIdentifier() if datapoint_identifier in existing_nodes_identifiers_set and len(available_identifiers): next_identifier = available_identifiers.pop(0) + elif datapoint_identifier not in existing_nodes_identifiers_set: + next_identifier = datapoint_identifier else: max_identifier += 1 next_identifier = max_identifier - identifier_map[datapoint_identifier] = next_identifier + if next_identifier != datapoint_identifier: + identifier_map[datapoint_identifier] = next_identifier datapoint = datapoint_iterator.next() for current_identifier, new_identifier in identifier_map.items(): @@ -58,7 +62,7 @@ def convert_nodes_to_datapoints(target_region, source_region, source_nodeset_typ datapoint.setIdentifier(new_identifier) # transfer nodes as datapoints to target_region - buffer = write_to_buffer(source_region, resource_domain_type=source_nodeset_type) + buffer = write_to_buffer(source_region, resource_domain_type=source_nodeset_type, field_names=field_names) if source_nodeset_type == Field.DOMAIN_TYPE_NODES: buffer = buffer.replace(bytes("!#nodeset nodes", "utf-8"), bytes("!#nodeset datapoints", "utf-8")) result = read_from_buffer(target_region, buffer) @@ -68,17 +72,18 @@ def convert_nodes_to_datapoints(target_region, source_region, source_nodeset_typ nodes.destroyAllNodes() -def copy_fitting_data(target_region, source_region): +def copy_fitting_data(target_region, source_region, field_names=None): """ Copy nodes and data points from source_region to target_region, converting nodes to data points and offsetting data point identifiers to not clash. All groups and fields in use are transferred. This is used for setting up fitting problems where data needs to be in datapoints only. :param target_region: Zinc Region to read nodes/data into. :param source_region: Zinc Region containing nodes/data to transfer. Unmodified. + :param field_names: A list of field names to output. """ for domain_type in [Field.DOMAIN_TYPE_DATAPOINTS, Field.DOMAIN_TYPE_NODES]: convert_nodes_to_datapoints(target_region, source_region, source_nodeset_type=domain_type, - destroy_after_conversion=False) + destroy_after_conversion=False, field_names=field_names) def copy_nodeset(region, nodeset): diff --git a/tests/test_zinc_group.py b/tests/test_zinc_group.py index 9ca13ce..00e19d1 100644 --- a/tests/test_zinc_group.py +++ b/tests/test_zinc_group.py @@ -1,4 +1,3 @@ -import os import unittest from cmlibs.utils.zinc.field import find_or_create_field_group from cmlibs.utils.zinc.group import ( @@ -8,6 +7,7 @@ from cmlibs.zinc.element import Element from cmlibs.zinc.field import Field from cmlibs.zinc.result import RESULT_OK + from utilities import assert_almost_equal_list, get_test_resource_name @@ -136,7 +136,6 @@ def test_group_add_compare_group_local_contents(self): self.assertEqual(0, group1.getMeshGroup(mesh1d).getSize()) self.assertEqual(0, group1.getNodesetGroup(nodes).getSize()) - def test_match_fitting_group_names(self): """ Test utility functions for adding and comparing group local contents. @@ -154,8 +153,36 @@ def test_match_fitting_group_names(self): data_group_fred = find_or_create_field_group(data_fieldmodule, " fRed\t") data_group_two_names = find_or_create_field_group(data_fieldmodule, "\t two NAMES ") - match_fitting_group_names(data_fieldmodule, model_fieldmodule, log_diagnostics=True) - + names = match_fitting_group_names(data_fieldmodule, model_fieldmodule, log_diagnostics=True) self.assertEqual(data_group_bob.getName(), "bob") self.assertEqual(data_group_fred.getName(), "fred") self.assertEqual(data_group_two_names.getName(), "two names") + self.assertIn("two names", names) + self.assertEqual(names["two names"], ('\t two NAMES ', 'two names')) + self.assertIn("bob", names) + self.assertEqual(names["bob"], (' Bob', 'bob')) + + def test_match_fitting_group_names_cap(self): + """ + Test utility functions for adding and comparing group local contents. + """ + context = Context("test") + model_region = context.createRegion() + model_fieldmodule = model_region.getFieldmodule() + find_or_create_field_group(model_fieldmodule, "Bob", managed=True) + find_or_create_field_group(model_fieldmodule, "fRed", managed=True) + find_or_create_field_group(model_fieldmodule, "james", managed=True) + + data_region = context.createRegion() + data_fieldmodule = data_region.getFieldmodule() + data_group_bob = find_or_create_field_group(data_fieldmodule, "bob") + data_group_fred = find_or_create_field_group(data_fieldmodule, "fred") + find_or_create_field_group(data_fieldmodule, "james") + + names = match_fitting_group_names(data_fieldmodule, model_fieldmodule, log_diagnostics=True) + self.assertEqual(data_group_bob.getName(), "Bob") + self.assertEqual(data_group_fred.getName(), "fRed") + self.assertIn("Bob", names) + self.assertEqual(names["Bob"], ('bob', 'bob')) + self.assertIn("james", names) + self.assertEqual(names["james"], ('james', None)) diff --git a/tests/test_zinc_region.py b/tests/test_zinc_region.py index de63154..95026e6 100644 --- a/tests/test_zinc_region.py +++ b/tests/test_zinc_region.py @@ -139,6 +139,226 @@ def test_copy_dataset_II(self): self.assertEqual(4, datapoints.getSize()) self.assertEqual(4, target_datapoints.getSize()) + def test_transfer_nodes_IV(self): + """ + Test zinc region transferring nodes over datapoints. + """ + context = Context("test") + source_region = context.createRegion() + target_region = context.createRegion() + source_fieldmodule = source_region.getFieldmodule() + target_fieldmodule = target_region.getFieldmodule() + source_coordinates = find_or_create_field_coordinates(source_fieldmodule) + target_coordinates = find_or_create_field_coordinates(target_fieldmodule) + + nodes = source_fieldmodule.findNodesetByFieldDomainType(Field.DOMAIN_TYPE_NODES) + target_datapoints = target_fieldmodule.findNodesetByFieldDomainType(Field.DOMAIN_TYPE_DATAPOINTS) + + node_coordinates = [[1.0] * 3, [2.0] * 3, [3.0] * 3] + datapoint_coordinates = [[-1.0] * 3, [-2.0] * 3, [-3.0] * 3] + reidentify_nodes = {3: 4} + + create_nodes(source_coordinates, node_coordinates, node_set=nodes) + create_nodes(target_coordinates, datapoint_coordinates, node_set=target_datapoints) + + for node_identifier in reidentify_nodes: + node = nodes.findNodeByIdentifier(node_identifier) + node.setIdentifier(reidentify_nodes[node_identifier]) + + nodeset_group = source_fieldmodule.createFieldGroup() + nodeset_group.setName('nodes_group') + nodeset = nodeset_group.getOrCreateNodesetGroup(nodes) + nodeset.addNode(nodes.findNodeByIdentifier(2)) + nodeset.addNode(nodes.findNodeByIdentifier(4)) + + self.assertEqual(2, nodeset.getSize()) + + datapointsset_group = target_fieldmodule.createFieldGroup() + datapointsset_group.setName('datapoints_group') + datapointsset = datapointsset_group.getOrCreateNodesetGroup(target_datapoints) + datapointsset.addNode(target_datapoints.findNodeByIdentifier(2)) + datapointsset.addNode(target_datapoints.findNodeByIdentifier(3)) + + self.assertEqual(2, datapointsset.getSize()) + + self.assertEqual(3, nodes.getSize()) + self.assertEqual(3, target_datapoints.getSize()) + + convert_nodes_to_datapoints(target_region, source_region) + + self.assertEqual(0, nodes.getSize()) + self.assertEqual(6, target_datapoints.getSize()) + + ni = target_datapoints.createNodeiterator() + datapoint = ni.next() + fc = target_fieldmodule.createFieldcache() + while datapoint.isValid(): + current_identifier = datapoint.getIdentifier() + fc.setNode(datapoint) + _, values = target_coordinates.evaluateReal(fc, 3) + if current_identifier == 1: + self.assertEqual(1, values[0]) + elif current_identifier == 2: + self.assertEqual(2, values[0]) + elif current_identifier == 3: + self.assertEqual(-3, values[0]) + elif current_identifier == 4: + self.assertEqual(3, values[0]) + elif current_identifier == 5: + self.assertEqual(-1, values[0]) + elif current_identifier == 6: + self.assertEqual(-2, values[0]) + datapoint = ni.next() + + ni = datapointsset.createNodeiterator() + datapoint = ni.next() + while datapoint.isValid(): + identifier = datapoint.getIdentifier() + self.assertIn(identifier, [3, 6]) + datapoint = ni.next() + + nodeset_group = target_fieldmodule.findFieldByName("nodes_group").castGroup() + nodeset = nodeset_group.getNodesetGroup(target_datapoints) + ni = nodeset.createNodeiterator() + datapoint = ni.next() + while datapoint.isValid(): + identifier = datapoint.getIdentifier() + self.assertIn(identifier, [2, 4]) + datapoint = ni.next() + + def test_transfer_nodes_V(self): + """ + Test zinc region transferring nodes over datapoints. + """ + context = Context("test") + source_region = context.createRegion() + target_region = context.createRegion() + source_fieldmodule = source_region.getFieldmodule() + target_fieldmodule = target_region.getFieldmodule() + source_coordinates = find_or_create_field_coordinates(source_fieldmodule) + target_coordinates = find_or_create_field_coordinates(target_fieldmodule) + + nodes = source_fieldmodule.findNodesetByFieldDomainType(Field.DOMAIN_TYPE_NODES) + target_datapoints = target_fieldmodule.findNodesetByFieldDomainType(Field.DOMAIN_TYPE_DATAPOINTS) + + node_coordinates = [[1.0] * 3, [2.0] * 3, [3.0] * 3, [4.0] * 3, [5.0] * 3] + datapoint_coordinates = [[-1.0] * 3, [-2.0] * 3, [-3.0] * 3, [-4.0] * 3, [-5.0] * 3, [-6.0] * 3, [-7.0] * 3] + + create_nodes(source_coordinates, node_coordinates, node_set=nodes) + create_nodes(target_coordinates, datapoint_coordinates, node_set=target_datapoints) + + reidentify_nodes = {1: 6, 2: 7, 4: 8} + for node_identifier in reidentify_nodes: + node = nodes.findNodeByIdentifier(node_identifier) + node.setIdentifier(reidentify_nodes[node_identifier]) + + reidentify_nodes = {1: 8, 2: 9, 4: 10} + for node_identifier in reidentify_nodes: + node = target_datapoints.findNodeByIdentifier(node_identifier) + node.setIdentifier(reidentify_nodes[node_identifier]) + + self.assertEqual(5, nodes.getSize()) + self.assertEqual(7, target_datapoints.getSize()) + + convert_nodes_to_datapoints(target_region, source_region) + + self.assertEqual(0, nodes.getSize()) + self.assertEqual(12, target_datapoints.getSize()) + + expected_values = { + 1: [-3.0, -3.0, -3.0], + 2: [-5.0, -5.0, -5.0], + 3: [3.0, 3.0, 3.0], + 4: [-6.0, -6.0, -6.0], + 5: [5.0, 5.0, 5.0], + 6: [1.0, 1.0, 1.0], + 7: [2.0, 2.0, 2.0], + 8: [4.0, 4.0, 4.0], + 9: [-2.0, -2.0, -2.0], + 10: [-4.0, -4.0, -4.0], + 11: [-7.0, -7.0, -7.0], + 12: [-1.0, -1.0, -1.0], + } + ni = target_datapoints.createNodeiterator() + fc = target_fieldmodule.createFieldcache() + datapoint = ni.next() + while datapoint.isValid(): + fc.setNode(datapoint) + identifier = datapoint.getIdentifier() + _, value = target_coordinates.evaluateReal(fc, 3) + self.assertEqual(expected_values[identifier], value) + datapoint = ni.next() + + def test_transfer_nodes_VI(self): + """ + Test zinc region transferring nodes over datapoints filtering fields. + """ + context = Context("test") + source_region = context.createRegion() + target_region = context.createRegion() + source_fieldmodule = source_region.getFieldmodule() + target_fieldmodule = target_region.getFieldmodule() + source_coordinates = find_or_create_field_coordinates(source_fieldmodule) + alt_coordinates = find_or_create_field_coordinates(source_fieldmodule, name='alt_coordinates') + + source_datapoints = source_fieldmodule.findNodesetByFieldDomainType(Field.DOMAIN_TYPE_DATAPOINTS) + source_nodes = source_fieldmodule.findNodesetByFieldDomainType(Field.DOMAIN_TYPE_NODES) + + node_coordinates = [[1.0] * 3, [2.0] * 3, [3.0] * 3, [4.0] * 3, [5.0] * 3] + datapoint_coordinates = [[-1.0] * 3, [-2.0] * 3, [-3.0] * 3, [-4.0] * 3, [-5.0] * 3, [-6.0] * 3, [-7.0] * 3] + + group_1 = source_fieldmodule.createFieldGroup() + group_2 = source_fieldmodule.createFieldGroup() + group_3 = source_fieldmodule.createFieldGroup() + + group_1.setName("group_1") + group_2.setName("group_2") + group_3.setName("group_3") + + create_nodes(source_coordinates, datapoint_coordinates, node_set=source_datapoints) + create_nodes(alt_coordinates, node_coordinates, node_set=source_nodes) + + dataset_group = group_1.getOrCreateNodesetGroup(source_datapoints) + dataset_group.addNode(source_datapoints.findNodeByIdentifier(1)) + dataset_group.addNode(source_datapoints.findNodeByIdentifier(2)) + + dataset_group = group_2.getOrCreateNodesetGroup(source_datapoints) + dataset_group.addNode(source_datapoints.findNodeByIdentifier(5)) + dataset_group.addNode(source_datapoints.findNodeByIdentifier(6)) + + dataset_group = group_3.getOrCreateNodesetGroup(source_nodes) + dataset_group.addNode(source_datapoints.findNodeByIdentifier(2)) + dataset_group.addNode(source_datapoints.findNodeByIdentifier(3)) + + fi = target_fieldmodule.createFielditerator() + field = fi.next() + field_count = 0 + while field.isValid(): + field_count += 1 + print('b4:', field.getName()) + field = fi.next() + + fi = source_fieldmodule.createFielditerator() + field = fi.next() + while field.isValid(): + print('src:', field.getName()) + field = fi.next() + + self.assertEqual(0, field_count) + + convert_nodes_to_datapoints(target_region, source_region, source_nodeset_type=Field.DOMAIN_TYPE_DATAPOINTS, field_names=['group_1', 'coordinates']) + + fi = target_fieldmodule.createFielditerator() + field = fi.next() + field_count = 0 + while field.isValid(): + field_count += 1 + print('a4:', field.getName()) + field = fi.next() + + self.assertEqual(2, field_count) + + if __name__ == "__main__": unittest.main()