From 7be30a631c5713ea11c14f6a6ec20ca082db9341 Mon Sep 17 00:00:00 2001 From: arti-sukasem <51779010+arti-sukasem@users.noreply.github.com> Date: Tue, 17 May 2022 15:02:39 +1200 Subject: [PATCH 01/10] Create a generic lung scaffold and its parameters for species --- src/scaffoldmaker/annotation/lung_terms.py | 17 + .../meshtypes/meshtype_3d_lung2.py | 2143 +++++++++++++++++ src/scaffoldmaker/scaffolds.py | 2 + tests/test_lung.py | 164 ++ 4 files changed, 2326 insertions(+) create mode 100644 src/scaffoldmaker/meshtypes/meshtype_3d_lung2.py diff --git a/src/scaffoldmaker/annotation/lung_terms.py b/src/scaffoldmaker/annotation/lung_terms.py index 05ef1621..dced2b77 100644 --- a/src/scaffoldmaker/annotation/lung_terms.py +++ b/src/scaffoldmaker/annotation/lung_terms.py @@ -4,27 +4,44 @@ # convention: preferred name, preferred id, followed by any other ids and alternative names lung_terms = [ + ("anterior border of left lung", "ILX:0793130"), + ("anterior border of right lung", "ILX:0793129"), + ("anterior mediastinum of left lung", "ILX:0793183"), + ("anterior mediastinum of right lung", "ILX:0793184"), ("apex of right lung accessory lobe", "ILX:0778119"), ("apex of left lung", "ILX:0778112"), ("apex of right lung", "ILX:0778113"), + ("base of left lung surface", "ILX:0793187"), + ("base of right lung accessory lobe surface", "ILX:0793189"), + ("base of right lung surface", "ILX:0793188"), ("dorsal base of right lung accessory lobe", "ILX:0778125"), ("dorsal base of left lung", "ILX:0778126"), ("dorsal base of right lung", "ILX:0778127"), ("horizontal fissure of right lung", "ILX:0746327"), ("laterodorsal tip of middle lobe of right lung", "ILX:0778124"), ("left lung", "UBERON:0002168", "ILX:0733450"), + ("left lung surface", "ILX:0793186"), ("lower lobe of left lung", "UBERON:0008953", "ILX:0735534"), + ("lower lobe of left lung surface", "ILX:0793192"), ("lower lobe of right lung", "UBERON:0002171", "ILX:0725712"), + ("lower lobe of right lung surface", "ILX:0793191"), ("lung", "UBERON:0002048", "ILX:0726937"), ("middle lobe of right lung", "UBERON:0002174", "ILX:0733737"), + ("middle lobe of right lung surface", "ILX:0793193"), ("medial base of left lung", "ILX:0778120"), ("medial base of right lung", "ILX:0778121"), + ("anterior mediastinum of left lung", "UBERON:0008820", "ILX:0725455"), + ("anterior mediastinum of right lung", "UBERON:0008820", "ILX:0725455"), ("oblique fissure of left lung", "ILX:0778115"), ("oblique fissure of right lung", "ILX:0778114"), ("right lung", "UBERON:0002167", "ILX:0729582"), + ("right lung surface", "ILX:0793185"), ("right lung accessory lobe", "UBERON:0004890", "ILX:0728696"), + ("right lung accessory lobe surface", "ILX:0793190"), ("upper lobe of left lung", "UBERON:0008952", "ILX:0735339"), + ("upper lobe of left lung surface", "ILX:0793194"), ("upper lobe of right lung", "UBERON:0002170", "ILX:0728821"), + ("upper lobe of right lung surface", "ILX:0793195"), ("ventral base of right lung accessory lobe", "ILX:0778123"), ("ventral base of left lung", "ILX:0778118"), ("ventral base of right lung", "ILX:0778122") diff --git a/src/scaffoldmaker/meshtypes/meshtype_3d_lung2.py b/src/scaffoldmaker/meshtypes/meshtype_3d_lung2.py new file mode 100644 index 00000000..bc8f1fd3 --- /dev/null +++ b/src/scaffoldmaker/meshtypes/meshtype_3d_lung2.py @@ -0,0 +1,2143 @@ +''' +Generates a 3D generic lung mesh. +''' + +import copy +import math +from scaffoldmaker.utils.interpolation import sampleCubicHermiteCurves, interpolateSampleCubicHermite, \ + smoothCubicHermiteDerivativesLine, interpolateSampleLinear +from scaffoldmaker.annotation.annotationgroup import AnnotationGroup, findOrCreateAnnotationGroupForTerm, getAnnotationGroupForTerm +from scaffoldmaker.annotation.lung_terms import get_lung_term +from scaffoldmaker.meshtypes.scaffold_base import Scaffold_base +from scaffoldmaker.utils.eft_utils import remapEftLocalNodes, remapEftNodeValueLabel, remapEftNodeValueLabelsVersion, setEftScaleFactorIds +from scaffoldmaker.utils.cylindermesh import createEllipsePerimeter +from scaffoldmaker.utils.eftfactory_tricubichermite import eftfactory_tricubichermite +from opencmiss.utils.zinc.general import ChangeManager +from scaffoldmaker.utils.geometry import createEllipsoidPoints, getEllipseRadiansToX, getEllipseArcLength, getApproximateEllipsePerimeter, \ + updateEllipseAngleByArcLength +from scaffoldmaker.utils.interpolation import DerivativeScalingMode +from scaffoldmaker.utils.derivativemoothing import DerivativeSmoothing +from scaffoldmaker.utils.meshrefinement import MeshRefinement +from scaffoldmaker.utils.vector import magnitude, setMagnitude, crossproduct3, normalise +from opencmiss.utils.zinc.field import Field, findOrCreateFieldCoordinates, findOrCreateFieldGroup, \ + findOrCreateFieldNodeGroup, findOrCreateFieldStoredMeshLocation, findOrCreateFieldStoredString, createFieldEulerAnglesRotationMatrix +from opencmiss.utils.zinc.finiteelement import get_element_node_identifiers +from opencmiss.zinc.element import Element +from opencmiss.zinc.node import Node + +class MeshType_3d_lung2(Scaffold_base): + ''' + Generic 3D lung scaffold. + ''' + + """ + Generates an ellipsoid with a tear-shaped base for the lung mathematically, + with x, y, z length and the position, angle of the oblique fissure. + Regions and markers of the lung are annotated. + """ + + @staticmethod + def getName(): + return '3D Lung 2' + + @staticmethod + def getParameterSetNames(): + return [ + 'Default', + 'Human 1', + 'Mouse 1', + 'Rat 1', + 'Pig 1', + 'Material' + ] + + @classmethod + def getDefaultOptions(cls, parameterSetName='Default'): + if parameterSetName == 'Default': + parameterSetName = 'Human 1' + options = { + 'Base parameter set': parameterSetName, + 'Number of left lung lobes': 2, + 'Left-right lung spacing': 1.0, + 'Left-right apex medial shear displacement': 0.0, + 'Left-right apex ventral shear displacement': 0.0, + 'Left lung width': 0.5, + 'Left lung depth': 1.0, + 'Left lung height': 1.0, + 'Left lung ventral edge sharpness factor': 0.0, + 'Left lung medial curvature factor': 0.0, + 'Left lung medial protrusion factor': 0.0, + 'Right lung width': 0.5, + 'Right lung depth': 1.0, + 'Right lung height': 1.0, + 'Right lung ventral edge sharpness factor': 0.0, + 'Right lung medial curvature factor': 0.0, + 'Right lung medial protrusion factor': 0.0, + 'Open fissures': False, + 'Accessory lobe': True, + 'Accessory lobe medial curvature': 0.0, + 'Accessory lobe length': 0.5, + 'Accessory lobe dorsal centre [x,y]': [0.0, 0.25], + 'Accessory lobe dorsal height': 0.5, + 'Accessory lobe dorsal width': 0.5, + 'Accessory lobe ventral height': 0.5, + 'Accessory lobe ventral width': 0.5, + 'Diaphragm centre x': 0.0, + 'Diaphragm centre y': 0.0, + 'Diaphragm curvature x': 0.0, + 'Diaphragm curvature y': 0.0, + 'Left lung rotation in degree about z-axis': 0.0, + 'Right lung rotation in degree about z-axis': 0.0, + 'Accessory lobe rotation in degree about z-axis': 0.0, + 'Refine': False, + 'Refine number of elements': 4, + 'Material Parameters': None + } + materialOptions = copy.deepcopy(options) + if 'Human 1' in parameterSetName: + options['Left-right lung spacing'] = 0.75 + options['Left-right apex medial shear displacement'] = 0.2 + options['Left-right apex ventral shear displacement'] = -0.2 + options['Left lung width'] = 0.8 + options['Left lung depth'] = 1.0 + options['Left lung height'] = 1.0 + options['Left lung ventral edge sharpness factor'] = 0.8 + options['Left lung medial curvature factor'] = 1.0 + options['Left lung medial protrusion factor'] = 0.1 + options['Left lung rotation in degree about z-axis'] = -15.0 + options['Right lung width'] = 0.8 + options['Right lung depth'] = 1.0 + options['Right lung height'] = 1.0 + options['Right lung ventral edge sharpness factor'] = 0.8 + options['Right lung medial curvature factor'] = 1.0 + options['Right lung medial protrusion factor'] = 0.1 + options['Right lung rotation in degree about z-axis'] = 15.0 + options['Accessory lobe'] = False + options['Diaphragm curvature x'] = 1.0 + options['Diaphragm curvature y'] = 1.0 + elif 'Mouse 1' in parameterSetName: + options['Number of left lung lobes'] = 1 + options['Left-right lung spacing'] = 0.9 + options['Left-right apex medial shear displacement'] = 0.35 + options['Left-right apex ventral shear displacement'] = -0.2 + options['Left lung width'] = 0.5 + options['Left lung depth'] = 1.0 + options['Left lung height'] = 0.7 + options['Left lung ventral edge sharpness factor'] = 0.5 + options['Left lung medial curvature factor'] = 1.0 + options['Left lung medial protrusion factor'] = 0.4 + options['Left lung rotation in degree about z-axis'] = -10.0 + options['Right lung width'] = 0.8 + options['Right lung depth'] = 1.2 + options['Right lung height'] = 0.85 + options['Right lung ventral edge sharpness factor'] = 0.8 + options['Right lung medial curvature factor'] = 0.8 + options['Right lung medial protrusion factor'] = 0.2 + options['Right lung rotation in degree about z-axis'] = 15.0 + options['Accessory lobe dorsal centre [x,y]'] = [0.0, 0.1] + options['Accessory lobe length'] = 0.6 + options['Accessory lobe dorsal height'] = 0.5 + options['Accessory lobe dorsal width'] = 0.5 + options['Accessory lobe ventral height'] = 0.15 + options['Accessory lobe ventral width'] = 0.2 + options['Accessory lobe medial curvature'] = 0.3 + options['Accessory lobe rotation in degree about z-axis'] = 0 + options['Diaphragm centre y'] = 0.5 + options['Diaphragm curvature x'] = 0.8 + options['Diaphragm curvature y'] = 1.0 + elif 'Rat 1' in parameterSetName: + options['Number of left lung lobes'] = 1 + options['Left-right lung spacing'] = 1.1 + options['Left-right apex medial shear displacement'] = 0.35 + options['Left-right apex ventral shear displacement'] = 0.0 + options['Left lung width'] = 0.5 + options['Left lung depth'] = 1.4 + options['Left lung height'] = 1.0 + options['Left lung ventral edge sharpness factor'] = 0.6 + options['Left lung medial curvature factor'] = 0.8 + options['Left lung medial protrusion factor'] = 0.5 + options['Left lung rotation in degree about z-axis'] = 0.0 + options['Right lung width'] = 0.5 + options['Right lung depth'] = 1.8 + options['Right lung height'] = 1.0 + options['Right lung ventral edge sharpness factor'] = 0.0 + options['Right lung medial curvature factor'] = 0.8 + options['Right lung medial protrusion factor'] = 0.0 + options['Right lung rotation in degree about z-axis'] = 0.0 + options['Accessory lobe'] = True + options['Accessory lobe dorsal centre [x,y]'] = [0.075, 0.1] + options['Accessory lobe length'] = 1.0 + options['Accessory lobe dorsal height'] = 0.5 + options['Accessory lobe dorsal width'] = 0.5 + options['Accessory lobe ventral width'] = 0.2 + options['Accessory lobe ventral height'] = 0.2 + options['Accessory lobe medial curvature'] = 0.5 + options['Accessory lobe rotation in degree about z-axis'] = 10.0 + options['Diaphragm curvature x'] = 1.0 + options['Diaphragm curvature y'] = 1.0 + elif 'Pig 1' in parameterSetName: + options['Number of left lung lobes'] = 1 + options['Left-right lung spacing'] = 1.2 + options['Left-right apex medial shear displacement'] = 0.5 + options['Left-right apex ventral shear displacement'] = -0.4 + options['Left lung width'] = 0.8 + options['Left lung depth'] = 1.6 + options['Left lung height'] = 1.1 + options['Left lung ventral edge sharpness factor'] = 0.7 + options['Left lung medial curvature factor'] = 0.7 + options['Left lung medial protrusion factor'] = 0.3 + options['Left lung rotation in degree about z-axis'] = -10.0 + options['Right lung width'] = 0.8 + options['Right lung depth'] = 1.5 + options['Right lung height'] = 1.1 + options['Right lung ventral edge sharpness factor'] = 0.7 + options['Right lung medial curvature factor'] = 0.7 + options['Right lung medial protrusion factor'] = 0.0 + options['Right lung rotation in degree about z-axis'] = 10.0 + options['Accessory lobe'] = True + options['Accessory lobe dorsal centre [x,y]'] = [0.1, 0.15] + options['Accessory lobe length'] = 0.8 + options['Accessory lobe dorsal height'] = 0.5 + options['Accessory lobe dorsal width'] = 0.6 + options['Accessory lobe ventral height'] = 0.1 + options['Accessory lobe ventral width'] = 0.2 + options['Accessory lobe medial curvature'] = 0.7 + options['Accessory lobe rotation in degree about z-axis'] = 0.0 + options['Diaphragm centre y'] = 0.1 + options['Diaphragm curvature x'] = 1.0 + options['Diaphragm curvature y'] = 1.0 + options['Material Parameters'] = materialOptions + options['Material Parameters']['Base parameter set'] = 'Material' + options['Material Parameters']['Accessory lobe'] = options['Accessory lobe'] + return options + + @staticmethod + def getOrderedOptionNames(): + optionNames = [ + 'Number of left lung lobes', + 'Left-right lung spacing', + 'Left-right apex medial shear displacement', + 'Left-right apex ventral shear displacement', + 'Left lung width', + 'Left lung depth', + 'Left lung height', + 'Left lung ventral edge sharpness factor', + 'Left lung medial curvature factor', + 'Left lung medial protrusion factor', + 'Left lung rotation in degree about z-axis', + 'Right lung width', + 'Right lung depth', + 'Right lung height', + 'Right lung ventral edge sharpness factor', + 'Right lung medial curvature factor', + 'Right lung medial protrusion factor', + 'Right lung rotation in degree about z-axis', + 'Open fissures', + 'Accessory lobe', + 'Accessory lobe dorsal centre [x,y]', + 'Accessory lobe length', + 'Accessory lobe dorsal height', + 'Accessory lobe dorsal width', + 'Accessory lobe ventral height', + 'Accessory lobe ventral width', + 'Accessory lobe medial curvature', + 'Accessory lobe rotation in degree about z-axis', + 'Diaphragm centre x', + 'Diaphragm centre y', + 'Diaphragm curvature x', + 'Diaphragm curvature y', + 'Refine', + 'Refine number of elements' + ] + return optionNames + + @classmethod + def checkOptions(cls, options): + ''' + :return: True if dependent options changed, otherwise False. + ''' + dependentChanges = False + if options['Refine number of elements'] < 1: + options['Refine number of elements'] = 1 + + if options['Number of left lung lobes'] > 2: + options['Number of left lung lobes'] = 2 + + return dependentChanges + + @classmethod + def generateBaseMesh(cls, region, options): + ''' + Generate the base tricubic Hermite mesh. See also generateMesh(). + :param region: Zinc region to define model in. Must be empty. + :param options: Dict containing options. See getDefaultOptions(). + :return: annotationGroups + ''' + # Generate two meshes: geometric[0] and lung[1] coordinates + for coordinate in range(2): + if coordinate == 0: + fm_0 = region.getFieldmodule() + coordinates_0 = findOrCreateFieldCoordinates(fm_0) + # point reference + fm = fm_0 + coordinates = coordinates_0 + else: + region_1 = region.createRegion() + fm_1 = region_1.getFieldmodule() + coordinates_1 = findOrCreateFieldCoordinates(fm_1, name="lung coordinates") + # point reference + fm = fm_1 + coordinates = coordinates_1 + # update material coordinates according to geometric parameters + options['Material Parameters']['Number of left lung lobes'] = options['Number of left lung lobes'] + options['Material Parameters']['Open fissures'] = options['Open fissures'] + options['Material Parameters']['Accessory lobe'] = options['Accessory lobe'] + options = options['Material Parameters'] + + numberOfLeftLung = options['Number of left lung lobes'] + spacingBetweenLeftRight = options['Left-right lung spacing'] / 2.0 + lungsApexDisplacement = options['Left-right apex medial shear displacement'] + forwardLeftRightApex = options['Left-right apex ventral shear displacement'] + leftWidth = options['Left lung width'] / 2.0 + leftDepth = options['Left lung depth'] / 2.0 + leftHeight = options['Left lung height'] + leftEdgeSharpFactor = options['Left lung ventral edge sharpness factor'] + leftLungMedialCurvature = options['Left lung medial curvature factor'] * 2.0 + leftLungMedialProtrusion = options['Left lung medial protrusion factor'] + rightWidth = options['Right lung width'] / 2.0 + rightDepth = options['Right lung depth'] / 2.0 + rightHeight = options['Right lung height'] + rightEdgeSharpFactor = options['Right lung ventral edge sharpness factor'] + rightLungMedialCurvature = options['Right lung medial curvature factor'] * 2.0 + rightLungMedialProtrusion = options['Right lung medial protrusion factor'] + isOpenfissure = options['Open fissures'] + hasAccessoryLobe = options['Accessory lobe'] + accessoryLobeDorsalCentre = options['Accessory lobe dorsal centre [x,y]'] + accessoryLobeLength = options['Accessory lobe length'] + accessoryLobeDorsalHeight = options['Accessory lobe dorsal height'] + accessoryLobeDorsalWidth = options['Accessory lobe dorsal width'] + accessoryLobeVentralHeight = options['Accessory lobe ventral height'] + accessoryLobeVentralWidth = options['Accessory lobe ventral width'] + accessoryLobeMedialCurve = options['Accessory lobe medial curvature'] * 2.0 + diaphragmCentreX = options['Diaphragm centre x'] + diaphragmCentreY = options['Diaphragm centre y'] + diaphragmCurvatureX = options['Diaphragm curvature x'] + diaphragmCurvatureY = options['Diaphragm curvature y'] + rotateLeftLung = options['Left lung rotation in degree about z-axis'] + rotateRightLung = options['Right lung rotation in degree about z-axis'] + rotateAccessoryLobe = options['Accessory lobe rotation in degree about z-axis'] + + nodes = fm.findNodesetByFieldDomainType(Field.DOMAIN_TYPE_NODES) + nodetemplate = nodes.createNodetemplate() + nodetemplate.defineField(coordinates) + nodetemplate.setValueNumberOfVersions(coordinates, -1, Node.VALUE_LABEL_VALUE, 1) + nodetemplate.setValueNumberOfVersions(coordinates, -1, Node.VALUE_LABEL_D_DS1, 1) + nodetemplate.setValueNumberOfVersions(coordinates, -1, Node.VALUE_LABEL_D_DS2, 1) + nodetemplate.setValueNumberOfVersions(coordinates, -1, Node.VALUE_LABEL_D_DS3, 1) + + mesh = fm.findMeshByDimension(3) + + eftfactory = eftfactory_tricubichermite(mesh, None) + eftRegular = eftfactory.createEftBasic() + + elementtemplateRegular = mesh.createElementtemplate() + elementtemplateRegular.setElementShapeType(Element.SHAPE_TYPE_CUBE) + elementtemplateRegular.defineField(coordinates, -1, eftRegular) + + elementtemplateCustom = mesh.createElementtemplate() + elementtemplateCustom.setElementShapeType(Element.SHAPE_TYPE_CUBE) + + #################### + # Annotation groups + #################### + lungGroup = AnnotationGroup(region, get_lung_term("lung")) + leftLungGroup = AnnotationGroup(region, get_lung_term("left lung")) + annotationGroups = [leftLungGroup, lungGroup] + lungMeshGroup = lungGroup.getMeshGroup(mesh) + leftLungMeshGroup = leftLungGroup.getMeshGroup(mesh) + rightLungGroup = AnnotationGroup(region, get_lung_term("right lung")) + rightLungMeshGroup = rightLungGroup.getMeshGroup(mesh) + annotationGroups.append(rightLungGroup) + lowerRightLungGroup = AnnotationGroup(region, get_lung_term("lower lobe of right lung")) + lowerRightLungMeshGroup = lowerRightLungGroup.getMeshGroup(mesh) + annotationGroups.append(lowerRightLungGroup) + upperRightLungGroup = AnnotationGroup(region, get_lung_term("upper lobe of right lung")) + upperRightLungMeshGroup = upperRightLungGroup.getMeshGroup(mesh) + annotationGroups.append(upperRightLungGroup) + middleRightLungGroup = AnnotationGroup(region, get_lung_term("middle lobe of right lung")) + middleRightLungMeshGroup = middleRightLungGroup.getMeshGroup(mesh) + annotationGroups.append(middleRightLungGroup) + mediastinumLeftGroup = AnnotationGroup(region, get_lung_term("anterior mediastinum of left lung")) + mediastinumLeftGroupMeshGroup = mediastinumLeftGroup.getMeshGroup(mesh) + annotationGroups.append(mediastinumLeftGroup) + mediastinumRightGroup = AnnotationGroup(region, get_lung_term("anterior mediastinum of right lung")) + mediastinumRightGroupMeshGroup = mediastinumRightGroup.getMeshGroup(mesh) + annotationGroups.append(mediastinumRightGroup) + + if numberOfLeftLung == 2: + lowerLeftLungGroup = AnnotationGroup(region, get_lung_term("lower lobe of left lung")) + lowerLeftLungMeshGroup = lowerLeftLungGroup.getMeshGroup(mesh) + annotationGroups.append(lowerLeftLungGroup) + upperLeftLungGroup = AnnotationGroup(region, get_lung_term("upper lobe of left lung")) + upperLeftLungMeshGroup = upperLeftLungGroup.getMeshGroup(mesh) + annotationGroups.append(upperLeftLungGroup) + + # Marker points/groups + leftApexGroup = findOrCreateAnnotationGroupForTerm(annotationGroups, region, + get_lung_term("apex of left lung")) + rightApexGroup = findOrCreateAnnotationGroupForTerm(annotationGroups, region, + get_lung_term("apex of right lung")) + leftVentralGroup = findOrCreateAnnotationGroupForTerm(annotationGroups, region, + get_lung_term("ventral base of left lung")) + rightVentralGroup = findOrCreateAnnotationGroupForTerm(annotationGroups, region, + get_lung_term("ventral base of right lung")) + rightLateralGroup = findOrCreateAnnotationGroupForTerm(annotationGroups, region, + get_lung_term("laterodorsal tip of middle lobe of right lung")) + leftMedialGroup = findOrCreateAnnotationGroupForTerm(annotationGroups, region, + get_lung_term("medial base of left lung")) + rightMedialGroup = findOrCreateAnnotationGroupForTerm(annotationGroups, region, + get_lung_term("medial base of right lung")) + + if hasAccessoryLobe: + # Annotation groups + rightLungAccessoryLobeGroup = AnnotationGroup(region, get_lung_term("right lung accessory lobe")) + rightLungAccessoryLobeMeshGroup = rightLungAccessoryLobeGroup.getMeshGroup(mesh) + annotationGroups.append(rightLungAccessoryLobeGroup) + rightLungAccessoryLobeNodesetGroup = rightLungAccessoryLobeGroup.getNodesetGroup(nodes) + # Marker points + accessoryApexGroup = findOrCreateAnnotationGroupForTerm(annotationGroups, region, + get_lung_term("apex of right lung accessory lobe")) + accessoryVentralGroup = findOrCreateAnnotationGroupForTerm(annotationGroups, region, + get_lung_term("ventral base of right lung accessory lobe")) + accessoryDorsalGroup = findOrCreateAnnotationGroupForTerm(annotationGroups, region, + get_lung_term("dorsal base of right lung accessory lobe")) + + # Nodeset group + leftLungNodesetGroup = leftLungGroup.getNodesetGroup(nodes) + rightLungNodesetGroup = rightLungGroup.getNodesetGroup(nodes) + lungNodesetGroup = lungGroup.getNodesetGroup(nodes) + mediastinumLeftNodesetGroup = mediastinumLeftGroup.getNodesetGroup(nodes) + mediastinumRightNodesetGroup = mediastinumRightGroup.getNodesetGroup(nodes) + + # Annotation fiducial point + markerGroup = findOrCreateFieldGroup(fm, "marker") + markerName = findOrCreateFieldStoredString(fm, name="marker_name") + markerLocation = findOrCreateFieldStoredMeshLocation(fm, mesh, name="marker_location") + markerPoints = findOrCreateFieldNodeGroup(markerGroup, nodes).getNodesetGroup() + markerTemplateInternal = nodes.createNodetemplate() + markerTemplateInternal.defineField(markerName) + markerTemplateInternal.defineField(markerLocation) + + cache = fm.createFieldcache() + + # The fixed number of the elements in the generic lungs + leftLung = 0 + rightLung = 1 + # Number of elements in lower lobe + lElementsCount1 = 2 + lElementsCount2 = 4 + lElementsCount3 = 3 + # Number of elements in upper lobe + uElementsCount1 = 2 + uElementsCount2 = 4 + uElementsCount3 = 4 + # Fissure angle and element distribution + leftFissureAngle = math.atan(leftHeight/(leftDepth * 2.0)) * 180 / math.pi + leftObliqueProportion = 0.8 + rightFissureAngle = math.atan(rightHeight/(rightDepth * 2.0)) * 180 / math.pi + rightObliqueProportion = 0.8 + + ############### + # Create nodes + ############### + nodeIdentifier = 1 + lowerLeftNodeIds = [] + upperLeftNodeIds = [] + lowerRightNodeIds = [] + upperRightNodeIds = [] + + # Left lung nodes + nodeIdentifier = createLungNodes(spacingBetweenLeftRight, + leftDepth, leftWidth, leftHeight, leftFissureAngle, leftObliqueProportion, + leftLung, cache, coordinates, nodes, nodetemplate, + mediastinumLeftNodesetGroup, leftLungNodesetGroup, lungNodesetGroup, + lElementsCount1, lElementsCount2, lElementsCount3, + uElementsCount1, uElementsCount2, uElementsCount3, + lowerLeftNodeIds, upperLeftNodeIds, nodeIdentifier) + + # Right lung nodes + nodeIdentifier = createLungNodes(spacingBetweenLeftRight, + rightDepth, rightWidth, rightHeight, rightFissureAngle, rightObliqueProportion, + rightLung, cache, coordinates, nodes, nodetemplate, + mediastinumRightNodesetGroup, rightLungNodesetGroup, lungNodesetGroup, + lElementsCount1, lElementsCount2, lElementsCount3, + uElementsCount1, uElementsCount2, uElementsCount3, + lowerRightNodeIds, upperRightNodeIds, nodeIdentifier) + + if hasAccessoryLobe: + # The number of the elements in the accessory lobe right lung + accesssoryLobeElementsCount1 = 2 + accesssoryLobeElementsCount2 = 5 + accesssoryLobeElementsCount3 = 2 + accessoryLobeNodeIds = [] + + # Accessory lobe right lung nodes + nodeIdentifier = createAccessorylobeLungNodes(accessoryLobeDorsalCentre, cache, coordinates, nodes, nodetemplate, + rightLungAccessoryLobeNodesetGroup, lungNodesetGroup, + accesssoryLobeElementsCount1, accesssoryLobeElementsCount2, accesssoryLobeElementsCount3, + accessoryLobeLength, accessoryLobeDorsalWidth, accessoryLobeDorsalHeight, accessoryLobeVentralWidth, + accessoryLobeVentralHeight, accessoryLobeNodeIds, nodeIdentifier) + + ################## + # Create elements + ################## + elementIdentifier = 1 + if numberOfLeftLung == 2: + # Left lung elements + elementIdentifier, leftUpperLobeElementID, leftLowerLobeElementID = \ + createLungElements(coordinates, eftfactory, eftRegular, elementtemplateRegular, + elementtemplateCustom, mesh, lungMeshGroup, + leftLungMeshGroup, lowerLeftLungMeshGroup, None, + upperLeftLungMeshGroup, mediastinumLeftGroupMeshGroup, + lElementsCount1, lElementsCount2, lElementsCount3, + uElementsCount1, uElementsCount2, uElementsCount3, + lowerLeftNodeIds, upperLeftNodeIds, elementIdentifier) + else: + elementIdentifier, leftUpperLobeElementID, leftLowerLobeElementID = \ + createLungElements(coordinates, eftfactory, eftRegular, elementtemplateRegular, + elementtemplateCustom, mesh, lungMeshGroup, + leftLungMeshGroup, None, None, None, mediastinumLeftGroupMeshGroup, + lElementsCount1, lElementsCount2, lElementsCount3, + uElementsCount1, uElementsCount2, uElementsCount3, + lowerLeftNodeIds, upperLeftNodeIds, elementIdentifier) + + # Right lung elements + elementIdentifier, rightUpperLobeElementID, rightLowerLobeElementID = \ + createLungElements(coordinates, eftfactory, eftRegular, elementtemplateRegular, + elementtemplateCustom, mesh, lungMeshGroup, + rightLungMeshGroup, lowerRightLungMeshGroup, middleRightLungMeshGroup, + upperRightLungMeshGroup, mediastinumRightGroupMeshGroup, + lElementsCount1, lElementsCount2, lElementsCount3, + uElementsCount1, uElementsCount2, uElementsCount3, + lowerRightNodeIds, upperRightNodeIds, elementIdentifier) + + if isOpenfissure: + leftUpperLobeElementID_temp = None if numberOfLeftLung == 1 else leftUpperLobeElementID + # create discontinuity/seperation along the fissures by making lower and upper lobes independently + nodeIdentifier = createDiscontinuity(coordinates, nodes, mesh, cache, nodetemplate, leftUpperLobeElementID_temp, + rightUpperLobeElementID, nodeIdentifier) + + if hasAccessoryLobe: + # Accessory lobe right lung elements + createAccessorylobeLungElements(coordinates, eftfactory, eftRegular, elementtemplateRegular, + elementtemplateCustom, mesh, lungMeshGroup, + rightLungAccessoryLobeMeshGroup, + accesssoryLobeElementsCount1, accesssoryLobeElementsCount2, + accesssoryLobeElementsCount3, + accessoryLobeNodeIds, elementIdentifier) + + ######################## + # Combining two coordinates + ######################## + if coordinate == 1: + sir = region_1.createStreaminformationRegion() + srm = sir.createStreamresourceMemory() + region_1.write(sir) + result, buffer = srm.getBuffer() + + sir = region.createStreaminformationRegion() + srm = sir.createStreamresourceMemoryBuffer(buffer) + region.read(sir) + + del srm + del sir + del fm_1 + del coordinates_1 + del region_1 + + #################################################################### + # Transformation and translation to the left (0) and right lungs (1) + #################################################################### + for i in [leftLung, rightLung]: + lungNodeset = leftLungNodesetGroup if i == 0 else rightLungNodesetGroup + edgeSharpFactor = leftEdgeSharpFactor if i == 0 else rightEdgeSharpFactor + width = leftWidth if i == 0 else -rightWidth + length = leftDepth if i == 0 else rightDepth + height = leftHeight if i == 0 else rightHeight + spacing = spacingBetweenLeftRight if i == 0 else -spacingBetweenLeftRight + apexMedialDisplacement = lungsApexDisplacement if i == 0 else -lungsApexDisplacement + lungMedialcurvature = -leftLungMedialCurvature if i == 0 else rightLungMedialCurvature + rotateLung = rotateLeftLung if i == 0 else rotateRightLung + lungProtrusion = leftLungMedialProtrusion if i == 0 else rightLungMedialProtrusion + + # Transformation of the left and right lungs + if lungProtrusion != 0.0: + medialProtrusion(lungProtrusion, fm, coordinates, lungNodeset, spacing, width, length, height) + + if edgeSharpFactor != 0.0: + sharpeningRidge(edgeSharpFactor, fm, coordinates, lungNodeset, spacing, length) + + if lungMedialcurvature != 0.0: + bendingAroundZAxis(lungMedialcurvature, fm, coordinates, lungNodeset, spacing) + + if apexMedialDisplacement != 0.0: + medialShearRadian = math.atan(apexMedialDisplacement/height) + tiltLungs(medialShearRadian, 0, 0, 0, fm, coordinates, lungNodeset) + + if forwardLeftRightApex != 0.0: + ventralShearRadian = math.atan(forwardLeftRightApex / height) + tiltLungs(0, ventralShearRadian, 0, 0, fm, coordinates, lungNodeset) + + if rotateLung != 0.0: + rotateLungs(rotateLung, fm, coordinates, lungNodeset, spacing) + + if (accessoryLobeMedialCurve != 0.0) and hasAccessoryLobe: + spacing = accessoryLobeDorsalCentre + bendingAroundZAxis(accessoryLobeMedialCurve, fm, coordinates, rightLungAccessoryLobeNodesetGroup, spacing) + + if (rotateAccessoryLobe != 0.0) and hasAccessoryLobe: + spacing = accessoryLobeDorsalCentre + rotateLungs(rotateAccessoryLobe, fm, coordinates, rightLungAccessoryLobeNodesetGroup, spacing) + + if (diaphragmCurvatureX != 0.0) or (diaphragmCurvatureY != 0.0): + concavingDiaphragmaticSurface(diaphragmCurvatureX, diaphragmCurvatureY, fm, coordinates, diaphragmCentreX, + diaphragmCentreY, lungNodesetGroup) + + ############### + # Marker points + ############### + markerList = [] + + lowerLeftElementCount = (lElementsCount1 * (lElementsCount2-1) * lElementsCount3 + lElementsCount1) + + idx = lowerLeftElementCount + (uElementsCount1 * uElementsCount2 * (uElementsCount3//2) + uElementsCount2) + markerList.append({ "group" : leftApexGroup, "elementId" : idx, "xi" : [0.0, 1.0, 1.0]}) + + idx = lElementsCount1 * (lElementsCount2 // 2) + markerList.append({"group": leftMedialGroup, "elementId": idx, "xi": [1.0, 1.0, 0.0]}) + + idx = lowerLeftElementCount + (uElementsCount1 * (uElementsCount2 - 2)) + markerList.append({"group": leftVentralGroup, "elementId": idx, "xi": [0.0, 1.0, 0.0]}) + + upperLeftElementCount = (uElementsCount1 * uElementsCount2 * (uElementsCount3-1)) + leftLungElementCount = lowerLeftElementCount + upperLeftElementCount + lowerRightElementCount = lowerLeftElementCount + + idx = leftLungElementCount + lowerRightElementCount + \ + (uElementsCount1 * uElementsCount2 * (uElementsCount3//2) + uElementsCount2) + markerList.append({"group": rightApexGroup, "elementId": idx, "xi": [0.0, 1.0, 1.0]}) + + idx = leftLungElementCount + lElementsCount1 + 1 + markerList.append({"group": rightMedialGroup, "elementId": idx, "xi": [0.0, 1.0, 0.0]}) + + idx = leftLungElementCount + lowerRightElementCount + (uElementsCount1 * (uElementsCount2 - 2)) + markerList.append({"group": rightVentralGroup, "elementId": idx, "xi": [0.0, 1.0, 0.0]}) + + idx = leftLungElementCount + (lElementsCount1 * lElementsCount2 * lElementsCount3) + lElementsCount1 + markerList.append({"group": rightLateralGroup, "elementId": idx, "xi": [1.0, 0.0, 1.0]}) + + if hasAccessoryLobe: + rightLungElementCount = leftLungElementCount + + idx_temp = accesssoryLobeElementsCount1 * accesssoryLobeElementsCount2 * (accesssoryLobeElementsCount3 - 1) + 1 + idx = rightLungElementCount + leftLungElementCount + idx_temp + markerList.append({"group": accessoryApexGroup, "elementId": idx, "xi": [0.0, 0.0, 1.0]}) + + idx_temp = accesssoryLobeElementsCount1 * accesssoryLobeElementsCount2 * (accesssoryLobeElementsCount3 - 1) + idx = rightLungElementCount + leftLungElementCount + idx_temp + markerList.append({"group": accessoryVentralGroup, "elementId": idx, "xi": [1.0, 1.0, 0.0]}) + + idx = rightLungElementCount + leftLungElementCount + 1 + markerList.append({"group": accessoryDorsalGroup, "elementId": idx, "xi": [0.0, 0.0, 0.0]}) + + for marker in markerList: + annotationGroup = marker["group"] + markerPoint = markerPoints.createNode(nodeIdentifier, markerTemplateInternal) + cache.setNode(markerPoint) + markerLocation.assignMeshLocation(cache, mesh.findElementByIdentifier(marker["elementId"]), + marker["xi"]) + markerName.assignString(cache, annotationGroup.getName()) + annotationGroup.getNodesetGroup(nodes).addNode(markerPoint) + lungNodesetGroup.addNode(markerPoint) + nodeIdentifier += 1 + + return annotationGroups + + @classmethod + def refineMesh(cls, meshrefinement, options): + """ + Refine source mesh into separate region, with change of basis. + :param meshrefinement: MeshRefinement, which knows source and target region. + :param options: Dict containing options. See getDefaultOptions(). + """ + assert isinstance(meshrefinement, MeshRefinement) + refineElementsCount = options['Refine number of elements'] + meshrefinement.refineAllElementsCubeStandard3d(refineElementsCount, refineElementsCount, refineElementsCount) + + @classmethod + def defineFaceAnnotations(cls, region, options, annotationGroups): + """ + Add face annotation groups from the highest dimension mesh. + Must have defined faces and added subelements for highest dimension groups. + :param region: Zinc region containing model. + :param options: Dict containing options. See getDefaultOptions(). + :param annotationGroups: List of annotation groups for top-level elements. + New face annotation groups are appended to this list. + """ + numberOfLeftLung = options['Number of left lung lobes'] + hasAccessoryLobe = options['Accessory lobe'] + + # create fissure groups + fm = region.getFieldmodule() + mesh1d = fm.findMeshByDimension(1) + mesh2d = fm.findMeshByDimension(2) + + # 1D Annotation + is_exterior = fm.createFieldIsExterior() + is_xi1_0 = fm.createFieldIsOnFace(Element.FACE_TYPE_XI1_0) + is_xi1_1 = fm.createFieldIsOnFace(Element.FACE_TYPE_XI1_1) + is_xi2_1 = fm.createFieldIsOnFace(Element.FACE_TYPE_XI2_1) + is_xi3_0 = fm.createFieldIsOnFace(Element.FACE_TYPE_XI3_0) + is_xi3_1 = fm.createFieldIsOnFace(Element.FACE_TYPE_XI3_1) + + # edge markers + mediastanumTerms = ["anterior mediastinum of left lung", "anterior mediastinum of right lung"] + for mediastanumTerm in mediastanumTerms: + mediastanumGroup = findOrCreateAnnotationGroupForTerm(annotationGroups, region, get_lung_term(mediastanumTerm)) + is_anteriorBorderGroup = mediastanumGroup.getFieldElementGroup(mesh1d) + is_mediastanumGroup_exterior = fm.createFieldAnd(is_anteriorBorderGroup, is_exterior) + is_mediastanumGroup_exterior_xi1_0 = fm.createFieldAnd(is_mediastanumGroup_exterior, is_xi1_0) + is_mediastanumGroup_exterior_xi1_01 = fm.createFieldAnd(is_mediastanumGroup_exterior_xi1_0, is_xi1_1) + is_mediastanumGroup_exterior_xi3_0 = fm.createFieldAnd(is_mediastanumGroup_exterior_xi1_01, is_xi3_0) + + is_upperLeftGroup_Diaphragm = fm.createFieldAnd(is_mediastanumGroup_exterior_xi3_0, is_mediastanumGroup_exterior_xi1_01) + + is_ridge = fm.createFieldAnd(is_mediastanumGroup_exterior_xi1_01, fm.createFieldNot(is_upperLeftGroup_Diaphragm)) + borderTerm = "anterior border of left lung" if "left lung" in mediastanumTerm else "anterior border of right lung" + anteriorBorderLeftGroup = findOrCreateAnnotationGroupForTerm(annotationGroups, region, get_lung_term(borderTerm)) + anteriorBorderLeftGroup.getMeshGroup(mesh1d).addElementsConditional(is_ridge) + annotationGroups.remove(mediastanumGroup) + + # exterior surfaces + surfaceTerms = [ + "left lung", + "right lung", + "upper lobe of right lung", + "middle lobe of right lung", + "lower lobe of right lung", + "lower lobe of left lung", + "upper lobe of left lung", + "right lung accessory lobe", + ] + subLeftLungTerms = ["lower lobe of left lung", "upper lobe of left lung"] + baseLungTerms = ["left lung", "right lung", "right lung accessory lobe"] + lobe = {} + + for term in surfaceTerms: + if (numberOfLeftLung == 1) and (term in subLeftLungTerms): + continue + if (hasAccessoryLobe is False) and (term == "right lung accessory lobe"): + continue + + group = getAnnotationGroupForTerm(annotationGroups, get_lung_term(term)) + group2d = group.getFieldElementGroup(mesh2d) + + if "lobe of" in term: + lobe.update({term: group2d}) + + group2d_exterior = fm.createFieldAnd(group2d, is_exterior) + surfaceGroup = findOrCreateAnnotationGroupForTerm(annotationGroups, region, get_lung_term(term + " surface")) + surfaceGroup.getMeshGroup(mesh2d).addElementsConditional(group2d_exterior) + + if term in baseLungTerms: + if term == "right lung accessory lobe": + group2D_exterior_bottom = fm.createFieldAnd(group2d_exterior, is_xi3_0) + else: + group2d_exterior_xi1_0 = fm.createFieldAnd(group2d_exterior, is_xi1_0) + group2d_exterior_xi1_1 = fm.createFieldAnd(group2d_exterior, is_xi1_1) + group2d_exterior_xi1_01 = fm.createFieldOr(group2d_exterior_xi1_0, group2d_exterior_xi1_1) + group2D_exterior_bottom = fm.createFieldXor(group2d_exterior, group2d_exterior_xi1_01) + + baseSurfaceGroup = findOrCreateAnnotationGroupForTerm(annotationGroups, region, get_lung_term("base of " + term + " surface")) + baseSurfaceGroup.getMeshGroup(mesh2d).addElementsConditional(group2D_exterior_bottom) + + # Fissures + fissureTerms = ["oblique fissure of right lung", "horizontal fissure of right lung", "oblique fissure of left lung"] + for fissureTerm in fissureTerms: + if (fissureTerm == "oblique fissure of left lung") and (numberOfLeftLung == 2): + fissureGroup = fm.createFieldAnd(lobe["upper lobe of left lung"], lobe["lower lobe of left lung"]) + elif fissureTerm == "oblique fissure of right lung": + fissureGroup = fm.createFieldAnd(fm.createFieldOr(lobe["middle lobe of right lung"], lobe["upper lobe of right lung"]), + lobe["lower lobe of right lung"]) + elif fissureTerm == "horizontal fissure of right lung": + fissureGroup = fm.createFieldAnd(lobe["upper lobe of right lung"], lobe["middle lobe of right lung"]) + fissureSurfaceGroup = findOrCreateAnnotationGroupForTerm(annotationGroups, region, get_lung_term(fissureTerm)) + fissureSurfaceGroup.getMeshGroup(mesh2d).addElementsConditional(fissureGroup) + +def createLungNodes(spaceFromCentre, lengthUnit, widthUnit, heightUnit, fissureAngle, obliqueProportion, + lungSide, cache, coordinates, nodes, nodetemplate, + mediastinumNodesetGroup, lungSideNodesetGroup, lungNodesetGroup, + lElementsCount1, lElementsCount2, lElementsCount3, + uElementsCount1, uElementsCount2, uElementsCount3, + lowerNodeIds, upperNodeIds, nodeIdentifier): + """ + :param spaceFromCentre: + :param lengthUnit: + :param widthUnit: + :param heightUnit: + :param fissureAngle: + :param obliqueProportion: + :param lungSide: + :param cache: + :param coordinates: + :param nodes: + :param nodetemplate: + :param mediastinumNodesetGroup: + :param lungSideNodesetGroup: + :param lungNodesetGroup: + :param lElementsCount1: + :param lElementsCount2: + :param lElementsCount3: + :param uElementsCount1: + :param uElementsCount2: + :param uElementsCount3: + :param lowerNodeIds: + :param upperNodeIds: + :param nodeIdentifier: + :return: nodeIdentifier + """ + + # Initialise parameters + leftLung = 0 + d1 = [1.0, 0.0, 0.0] + d2 = [0.0, 1.0, 0.0] + d3 = [0.0, 0.0, 1.0] + + centre = [0.0, 0.0, 0.0] + xAxis = [1.0, 0.0, 0.0] + yAxis = [0.0, 1.0, 0.0] + zAxis = [0.0, 0.0, 1.0] + + majorXAxis = [xAxis[i] * widthUnit for i in range(3)] + minorYAxis = [yAxis[i] * -lengthUnit for i in range(3)] + minorZAxis = [zAxis[i] * heightUnit for i in range(3)] + + fissureRadian = fissureAngle/180 * math.pi + tanFissureRadian = math.tan(fissureRadian) + + # Create 5 points at the centre of 2D Ellipse base + p1 = [yAxis[i] * -lengthUnit for i in range(3)] + p2 = [yAxis[i] * lengthUnit for i in range(3)] + p3 = [[p1[i] - (p1[i] - p2[i]) * obliqueProportion for i in range(3)]] # point of oblique fissure on diaphragm + + # Simplified simultaneous (linear and ellipsoid) equations + C = tanFissureRadian * p3[0][1] # (y-offset) - y = mx + c + + # Quadratic equation - ax^2 + bx + c = 0 + a = (heightUnit ** 2) + ((lengthUnit ** 2) * (tanFissureRadian ** 2)) + b = (lengthUnit ** 2) * 2 * tanFissureRadian * C + c = (lengthUnit ** 2) * (C ** 2) - ((lengthUnit ** 2) * (heightUnit ** 2)) + det = (b ** 2) - (4 * a * c) # determinant b^2 - 4ac + assert det > 0, "No real solution" + y = (-b + math.sqrt(det)) / (2 * a) + z = tanFissureRadian*y + C + p4 = [0.0, -y, z] # point at the oblique-ellispe intersection + + # points on 2D Ellipse base (two on the curve and one centre) + x = p3[0][1] + y = math.sqrt(1 - (x/lengthUnit) ** 2) * widthUnit + p3.append([y, p3[0][1], p3[0][2]]) + p3.insert(0, [-y, p3[0][1], p3[0][2]]) + + # points around on 2D Ellipse base + # -------------------------------------------------- Lower lobe ------------------------------------------------ + lower_row1 = [] + lower_row1_d2 = [] + lower_row2 = [] + lower_row2_d2 = [] + obl = [] + obl_d2 = [] + lower_edge = [] + lower_edge_d2 = [] + lowerObl = [] + lowerObl_d2 = [] + + for n2 in range(lElementsCount2 + 1): + if n2 == 0: + # row 1 + dx = p3[2][1] + vec1 = minorYAxis + vec2 = majorXAxis + planeCentre = centre + elementsCountAround = 4 + nodesCountAround = elementsCountAround + 1 + x = lower_row1 + nd2 = lower_row1_d2 + elif n2 == 1: + # oblique + vec1_1 = ([(p4[i] - p3[1][i]) / obliqueProportion for i in range(3)]) + vec1 = [vec1_1[i]/2 for i in range(3)] + vec2 = majorXAxis + planeCentre = [p4[i] - vec1[i] for i in range(3)] + dx = magnitude([p3[1][i] - planeCentre[i] for i in range(3)]) + elementsCountAround = 4 + nodesCountAround = elementsCountAround + 1 + x = obl + nd2 = obl_d2 + elif n2 == 2: + # lower lobe + dx = obl[0][1] + elementsCountAround = 3 + vec1 = minorYAxis + vec2 = [minorZAxis[i] * -1 for i in range(3)] + planeCentre = centre + nodesCountAround = elementsCountAround + 1 + x = lower_edge + nd2 = lower_edge_d2 + elif n2 == 3: + # 2nd oblique + obliqueProportion_1 = (lengthUnit + lower_row1[3][1])/(lengthUnit*2) + row1_temp = [0.0, lower_row1[3][1], lower_row1[3][2]] + vec1_1 = ([(lower_edge[2][i] - row1_temp[i]) / obliqueProportion_1 for i in range(3)]) + vec1 = ([vec1_1[i]/2 for i in range(3)]) + vec2 = majorXAxis + planeCentre = [lower_edge[2][i] - vec1[i] for i in range(3)] + dx = magnitude([planeCentre[i] - row1_temp[i] for i in range(3)]) + elementsCountAround = 4 + nodesCountAround = elementsCountAround + x = lowerObl + nd2 = lowerObl_d2 + elif n2 == 4: + # row 2 + row1_temp = [0.0, 0.0, lowerObl[3][2]] + y = math.sqrt(1 - (0.0 / lengthUnit) ** 2 - (lowerObl[3][2] / heightUnit) ** 2) * widthUnit + row1_ellipse = [-y, 0.0, lowerObl[3][2]] + vec1 = ([lower_edge[1][i] - row1_temp[i] for i in range(3)]) + vec2 = [-row1_ellipse[i] + row1_temp[i] for i in range(3)] + planeCentre = row1_temp + dx = lowerObl[3][1] + elementsCountAround = 3 + nodesCountAround = elementsCountAround + 1 + x = lower_row2 + nd2 = lower_row2_d2 + + magMajorAxis = magnitude(vec1) + magMinorAxis = magnitude(vec2) + unitMajorAxis = normalise(vec1) + unitMinorAxis = normalise(vec2) + + radians = 0.0 + totalRadiansAround = getEllipseRadiansToX(magnitude(vec1), 0.0, -dx, 0.5 * math.pi) + arclengthAround = getEllipseArcLength(magnitude(vec1), magnitude(vec2), radians, totalRadiansAround) + elementArcLength = arclengthAround / elementsCountAround + radians = updateEllipseAngleByArcLength(magnitude(vec1), magnitude(vec2), totalRadiansAround, -arclengthAround) + + x_temp = [] + nd2_temp = [] + for n1 in range(nodesCountAround): + cosRadiansAround = math.cos(radians) + sinRadiansAround = math.sin(radians) + + temp = ([(planeCentre[c] + cosRadiansAround * vec1[c] + sinRadiansAround * vec2[c]) for c in range(3)]) + x_temp.append(temp) + + ndab = setMagnitude([-sinRadiansAround * magMajorAxis, cosRadiansAround * magMinorAxis], -elementArcLength) + nd2_temp.append( + [(ndab[0] * unitMajorAxis[c] + ndab[1] * unitMinorAxis[c]) for c in range(3)]) + radians = updateEllipseAngleByArcLength(magnitude(vec1), magnitude(vec2), radians, -elementArcLength) + + # Gradual changes in node spacing + if n2 == 0: + # row 1 + px, pd1, pe, pxi, psf = sampleCubicHermiteCurves(x_temp, nd2_temp, len(x_temp)-1, elementLengthStartEndRatio = 1, arcLengthDerivatives = True) + elif n2 == 1: + # oblique + px, pd1, pe, pxi, psf = sampleCubicHermiteCurves(x_temp, nd2_temp, len(x_temp)-1, elementLengthStartEndRatio = 1, arcLengthDerivatives = True) + elif n2 == 2: + # lower lobe + px, pd1, pe, pxi, psf = sampleCubicHermiteCurves(x_temp, nd2_temp, len(x_temp)-1, elementLengthStartEndRatio = 2, lengthFractionStart = 0.3, + lengthFractionEnd = 1, arcLengthDerivatives = True) + elif n2 == 3: + # 2nd oblique + px, pd1, pe, pxi, psf = sampleCubicHermiteCurves(x_temp, nd2_temp, len(x_temp)-1, elementLengthStartEndRatio = 2, arcLengthDerivatives = True) + elif n2 == 4: + # rows 2 + px, pd1, pe, pxi, psf = sampleCubicHermiteCurves(x_temp, nd2_temp, len(x_temp)-1, elementLengthStartEndRatio = 2, arcLengthDerivatives = True) + + [x.append(px[i]) for i in range(len(px))] + [nd2.append(pd1[i]) for i in range(len(pd1))] + + # complete lower_row2 and lowerObl list + lower_row2.append(obl[3]) + lower_row2_d2.append(obl_d2[3]) + lowerObl.append(lower_row1[3]) + lowerObl_d2.append(lower_row1[3]) + + # smooth derivatives + tx_d2 = [] + td2 = [] + tx_d3 = [] + td3 = [] + md2 = [] + md3 = [] + + for n3 in range(lElementsCount3 + 1): + tx_d2.append([]) + td2.append([]) + if n3 == 0: + x = lower_row1 + d2 = lower_row1 + elif n3 == 1: + x = lower_row2 + d2 = lower_row2 + elif n3 == 2: + x = lowerObl + d2 = lowerObl + elif n3 == 3: + x = obl + d2 = obl + for n2 in range(lElementsCount2 + 1): + tx_d2[n3].append(x[n2]) + td2[n3].append(d2[n2]) + if n3 == 0: + if n2 == 4: + reverse_obl = [obl[-i] for i in range(1, len(obl)+1)] + tx_d3.append(reverse_obl) + td3.append(reverse_obl) + else: + tx_d3.append([lower_row1[n2], lower_row2[n2], lowerObl[n2], obl[n2]]) + td3.append([lower_row1[n2], lower_row2[n2], lowerObl[n2], obl[n2]]) + md3.append(smoothCubicHermiteDerivativesLine(tx_d3[n2], td3[n2])) + md2.append(smoothCubicHermiteDerivativesLine(tx_d2[n3], td2[n3])) + + # create nodes + for n3 in range(lElementsCount3 + 1): + lowerNodeIds.append([]) + for n2 in range(lElementsCount2 + 1): + lowerNodeIds[n3].append([]) + for n1 in range(lElementsCount1 + 1): + lowerNodeIds[n3][n2].append(None) + d2 = md2[n3][n2] + d3 = md3[n2][n3] + + # each i row + if n3 == 0: + x = copy.deepcopy(lower_row1[n2]) + if n2 < 4: + next_xd2 = copy.deepcopy(lower_row1[n2 + 1]) + elif n3 == 1: + x = copy.deepcopy(lower_row2[n2]) + if n2 < 4: + next_xd2 = copy.deepcopy(lower_row2[n2 + 1]) + elif (n3 == 2) and (n2 < 3): + x = copy.deepcopy(lowerObl[n2]) + if n2 < 4: + next_xd2 = copy.deepcopy(lowerObl[n2 + 1]) + elif (n3 == 3) and (n2 < 3): + x = copy.deepcopy(obl[n2]) + if n2 < 2: + next_xd2 = copy.deepcopy(obl[n2 + 1]) + else: + # 3-way point + d2 = [md3[n2][n3][0], md3[n2][n3][1], md3[n2][n3][2]] + d3 = [-md2[n3][n2][0], -md2[n3][n2][1], -md2[n3][n2][2]] + else: + continue + + # skipping the first and last column and row - edge. + if (n2 == 0) and (n1 != 1): + continue + + # symmetry + if n1 == 0: + next_xd1 = [0.0, x[1], x[2]] + d1 = [next_xd1[i] - x[i] for i in range(3)] + elif n1 == 1: + x = [0.0, x[1], x[2]] + next_x = [0.0, next_xd2[1], next_xd2[2]] + if n2 == 0: + d1 = [widthUnit * math.sin(0.5 * math.pi), 0.0, 0.0] + d2 = [next_x[i] - x[i] for i in range(3)] + # 3-way point + if (n3 == 3) and (n2 == 2): + d3 = [0.0, -md2[n3][n2][1], -md2[n3][n2][2]] + elif (n2 < 4): + next_xd2[0] = 0.0 + d2 = [next_xd2[i] - x[i] for i in range(3)] + previous_d2 = d2 + else: + d2 = previous_d2 + else: + x = [-x[0], x[1], x[2]] + d2 = [-d2[0], d2[1], d2[2]] + d3 = [-d3[0], d3[1], d3[2]] + + # translate right lung to the defined centre of the right lung + if lungSide != leftLung: + x = [x[0] + spaceFromCentre, x[1], x[2]] + else: + x = [x[0] - spaceFromCentre, x[1], x[2]] + + node = nodes.createNode(nodeIdentifier, nodetemplate) + cache.setNode(node) + coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_VALUE, 1, x) + coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS1, 1, d1) + coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS2, 1, d2) + coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS3, 1, d3) + lowerNodeIds[n3][n2][n1] = nodeIdentifier + nodeIdentifier += 1 + + # Annotation + lungSideNodesetGroup.addNode(node) + lungNodesetGroup.addNode(node) + + # ---------------------------------------------------- Upper lobe -------------------------------------------- + upper_row1 = [] + upper_row2 = [] + upper_row3 = [] + upper_row4 = [] + upper_edge = [] + + for n2 in range(uElementsCount1): + if n2 == 0: + # Upper lobe along the edge + dx = 0.0 + vec1 = [minorYAxis[i] * -1 for i in range(3)] + vec2 = minorZAxis + planeCentre = centre + elementsCountAround = 5 + nodesCountAround = elementsCountAround + 1 + x = upper_edge + else: + # Upper lobe along the edge on the lower lobe + dx = obl[0][2] + vec1 = minorZAxis + vec2 = minorYAxis + planeCentre = centre + elementsCountAround = 3 + nodesCountAround = elementsCountAround + 1 + x = upper_edge + + radians = 0.0 + totalRadiansAround = getEllipseRadiansToX(magnitude(vec1), 0.0, dx, 0.5 * math.pi) + arclengthAround = getEllipseArcLength(magnitude(vec1), magnitude(vec2), radians, totalRadiansAround) + elementArcLength = arclengthAround / elementsCountAround + radians = updateEllipseAngleByArcLength(magnitude(vec1), magnitude(vec2), totalRadiansAround, -arclengthAround) + + for n1 in range(nodesCountAround): + cosRadians = math.cos(radians) + sinRadians = math.sin(radians) + x_temp = ([(planeCentre[c] + cosRadians * vec1[c] + sinRadians * vec2[c]) for c in range(3)]) + radians = updateEllipseAngleByArcLength(magnitude(vec1), magnitude(vec2), radians, elementArcLength) + if (n2 == 1) and (n1 == 0): + continue + x.insert(0, x_temp) + + # Interpolate the nodes between the upper lobe edge and fissures + for n2 in range(uElementsCount3): + if n2 == 0: + # row 1 + dx = p3[2][1] + vec1 = [minorYAxis[i] * -1 for i in range(3)] + vec2 = majorXAxis + planeCentre = centre + elementsCountAround = 2 + nodesCountAround = elementsCountAround + 1 + x = upper_row1 + elif n2 == 1: + # row 2 + obl_temp = [0.0, 0.0, obl[-2][2]] + y = math.sqrt(1 - (0.0 / lengthUnit) ** 2 - (obl[-2][2] / heightUnit) ** 2) * widthUnit + obl_ellipse = [-y, 0.0, obl[-2][2]] + vec1 = [upper_edge[-2][i] - obl_temp[i] for i in range(3)] + vec2 = [-obl_ellipse[i] + obl_temp[i] for i in range(3)] + planeCentre = obl_temp + dx = obl[-2][1] + elementsCountAround = 2 + nodesCountAround = elementsCountAround + 1 + x = upper_row2 + elif n2 == 2: + # row 3 + obl_temp = [0.0, 0.0, obl[-3][2]] + y = math.sqrt(1 - (0.0 / lengthUnit) ** 2 - (obl[-3][2] / heightUnit) ** 2) * widthUnit + obl_ellipse = [-y, 0.0, obl[-3][2]] + vec1 = [upper_edge[-3][i] - obl_temp[i] for i in range(3)] + vec2 = [-obl_ellipse[i] + obl_temp[i] for i in range(3)] + planeCentre = obl_temp + dx = obl[-3][1] + elementsCountAround = 2 + nodesCountAround = elementsCountAround + 1 + x = upper_row3 + + radians = 0.0 + totalRadiansAround = getEllipseRadiansToX(magnitude(vec1), 0.0, dx, 0.5 * math.pi) + arclengthAround = getEllipseArcLength(magnitude(vec1), magnitude(vec2), radians, totalRadiansAround) + elementArcLength = arclengthAround / elementsCountAround + radians = updateEllipseAngleByArcLength(magnitude(vec1), magnitude(vec2), totalRadiansAround, -arclengthAround) + + x_temp = [] + nd2_temp = [] + for n1 in range(nodesCountAround): + cosRadians = math.cos(radians) + sinRadians = math.sin(radians) + + temp = ([(planeCentre[c] + cosRadians * vec1[c] + sinRadians * vec2[c]) for c in range(3)]) + x_temp.insert(0, temp) + + ndab = setMagnitude([-sinRadiansAround * magMajorAxis, cosRadiansAround * magMinorAxis], -elementArcLength) + nd2_temp.append( + [(ndab[0] * unitMajorAxis[c] + ndab[1] * unitMinorAxis[c]) for c in range(3)]) + + radians = updateEllipseAngleByArcLength(magnitude(vec1), magnitude(vec2), radians, -elementArcLength) + + # Gradual changes in node spacing + if n2 == 0: + # row 1 + px, pd1, pe, pxi, psf = sampleCubicHermiteCurves(x_temp, nd2_temp, len(x_temp) - 1, + elementLengthStartEndRatio=1.0, arcLengthDerivatives=True) + elif n2 == 1: + # row 2 + px, pd1, pe, pxi, psf = sampleCubicHermiteCurves(x_temp, nd2_temp, len(x_temp) - 1, + elementLengthStartEndRatio=1.0, arcLengthDerivatives=True) + elif n2 == 2: + # row 3 + px, pd1, pe, pxi, psf = sampleCubicHermiteCurves(x_temp, nd2_temp, len(x_temp) - 1, + elementLengthStartEndRatio=1.0, arcLengthDerivatives=True) + elif n2 == 3: + # row 4 - curve + x = upper_row4 + px = [] + for j in range(5): + x_temp_start = upper_edge[j + 1] + if (j > 0) and (j < 4): + x_temp_end = obl[j] if j < 3 else upper_row3[1] + px_1, pd1, pe, pxi, psf = sampleCubicHermiteCurves([x_temp_start, x_temp_end], [x_temp_start, x_temp_end], 2, + elementLengthStartEndRatio=1, arcLengthDerivatives=True) + x_ellipse = math.sqrt(1 - (px_1[1][1] / lengthUnit) ** 2 - (px_1[1][2] / heightUnit) ** 2) * widthUnit + x_temp = [-x_ellipse, px_1[1][1], px_1[1][2]] + px.append(x_temp) + else: + px.append(x_temp_start) + + [x.append(px[i]) for i in range(len(px))] + [nd2.append(pd1[i]) for i in range(len(pd1))] + + # smooth derivatives + tx_d2 = [] + tx_d3 = [] + md2 = [] + md3 = [] + for n3 in range(uElementsCount3 + 1): + if n3 == 0: + tx_d2 = upper_row1 + tx_d3 = [upper_edge[-i] for i in range(1, 6)] + elif n3 == 1: + tx_d2 = upper_row2 + tx_d3 = [upper_row1[-n3-1], upper_row2[-n3-1], upper_row3[-n3-1], upper_row4[-n3-1], upper_edge[-5]] + elif n3 == 2: + tx_d2 = upper_row3 + tx_d3 = [obl[n3], upper_row4[n3], upper_edge[n3 + 1]] + elif n3 == 3: + tx_d2 = upper_row4 + tx_d3 = [obl[n3-2], upper_row4[n3-2], upper_edge[n3 - 1]] + elif n3 == 4: + # Apex row + tx_d2 = [upper_edge[i] for i in range(1, 6)] + tx_d3 = [upper_edge[i] for i in range(3)] + + md2.append(smoothCubicHermiteDerivativesLine(tx_d2, tx_d2)) + md3.append(smoothCubicHermiteDerivativesLine(tx_d3, tx_d3)) + + # create nodes + for i in range(uElementsCount3 + 1): + upperNodeIds.append([]) + for j in range(uElementsCount2 + 1): + upperNodeIds[i].append([]) + for k in range(uElementsCount1 + 1): + upperNodeIds[i][j].append(None) + + # Oblique fissure nodes + if (i < 2) and (j == 2): + upperNodeIds[i][j][k] = lowerNodeIds[i][-1][k] + elif (i == 2) and (j < 4): + upperNodeIds[i][j][k] = lowerNodeIds[-1][j][k] + + # each i row + if (i == 0) and (j > 2): + x = copy.deepcopy(upper_row1[j - 2]) + d2 = md2[i][j-2] + idx = j-(j//2)*2 + d3 = md3[idx][i] + if j < 4: + next_xd2 = copy.deepcopy(upper_row1[j - 1]) + elif (i == 1) and (j > 2): + x = copy.deepcopy(upper_row2[j - 2]) + d2 = md2[i][j-2] + idx = j-(j//2)*2 + d3 = md3[idx][i] + if j < 4: + next_xd2 = copy.deepcopy(upper_row2[j - 1]) + elif (i == 2) and (j > 2): + x = copy.deepcopy(upper_row3[j - 2]) + d2 = md2[i][j-2] + idx = j-(j//2)*2 + d3 = md3[idx][i] + if j < 4: + next_xd2 = copy.deepcopy(upper_row3[j - 1]) + elif (i == 3): + x = copy.deepcopy(upper_row4[j]) + d2 = md2[i][j] + d3 = md3[-j-1][i] if j > 2 else md3[-j-1][i-2] + if j < 4: + next_xd2 = copy.deepcopy(upper_row4[j+1]) + elif (i == 4) and (j > 0) and (j < 4): + x = copy.deepcopy(upper_edge[j+1]) + d2 = md2[i][j] + d3 = md3[-j-1][i] if j == 3 else md3[-j-1][i-3] + if j < 4: + next_xd2 = copy.deepcopy(upper_edge[j+2]) + else: + continue + + # skipping the first and last, k. + if (((j == 0) or (j == 4)) and (k != 1)) or ((i == 4) and (k != 1)): + continue + + # symmetry + if k == 0: + # d2 = md2[i][j] + pass + elif k == 1: + if j == 4: + # Ridges + d2 = previous_d2 + elif i == 4: + # Apex row + d3 = [0.0, d3[1], d3[2]] + else: + x = [0.0, x[1], x[2]] + next_xd2 = [0.0, next_xd2[1], next_xd2[2]] + d2 = [next_xd2[i] - x[i] for i in range(3)] + previous_d2 = d2 + d3 = [0.0, d3[1], d3[2]] + else: + x = [-x[0], x[1], x[2]] + d2 = [-d2[0], d2[1], d2[2]] + d3 = [-d3[0], d3[1], d3[2]] + + # translate right lung to the defined centre of the right lung + if lungSide != leftLung: + x = [x[0] + spaceFromCentre, x[1], x[2]] + else: + x = [x[0] - spaceFromCentre, x[1], x[2]] + + node = nodes.createNode(nodeIdentifier, nodetemplate) + cache.setNode(node) + coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_VALUE, 1, x) + coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS1, 1, d1) + coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS2, 1, d2) + coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS3, 1, d3) + upperNodeIds[i][j][k] = nodeIdentifier + nodeIdentifier += 1 + + # Annotation + if j > 2: + mediastinumNodesetGroup.addNode(node) + lungSideNodesetGroup.addNode(node) + lungNodesetGroup.addNode(node) + + return nodeIdentifier + +def createLungElements(coordinates, eftfactory, eftRegular, elementtemplateRegular, elementtemplateCustom, mesh, + lungMeshGroup, lungSideMeshGroup, lowerLobeMeshGroup, middleLobeMeshGroup, + upperLobeMeshGroup, mediastinumGroupMeshGroup, + lElementsCount1, lElementsCount2, lElementsCount3, + uElementsCount1, uElementsCount2, uElementsCount3, + lowerNodeIds, upperNodeIds, elementIdentifier): + """ + :param lowerNodeIds: Indexing by [lElementsCount3 + 1][lElementsCount2 + 1][lElementsCount1 + 1] + :param upperNodeIds: Indexing by [uElementsCount3 + 1][uElementsCount2 + 1][uElementsCount1 + 1] + :return: elementIdentifier + """ + + eftWedgeCollapseXi1_15 = eftfactory.createEftWedgeCollapseXi1Quadrant([1, 5]) + eftWedgeCollapseXi1_26 = eftfactory.createEftWedgeCollapseXi1Quadrant([2, 6]) + eftWedgeCollapseXi1_57 = eftfactory.createEftWedgeCollapseXi1Quadrant([5, 7]) + eftWedgeCollapseXi1_68 = eftfactory.createEftWedgeCollapseXi1Quadrant([6, 8]) + eftWedgeCollapseXi2_78 = eftfactory.createEftWedgeCollapseXi2Quadrant([7, 8]) + eftTetCollapseXi1Xi2_71 = eftfactory.createEftTetrahedronCollapseXi1Xi2Quadrant(7, 1) + eftTetCollapseXi1Xi2_82 = eftfactory.createEftTetrahedronCollapseXi1Xi2Quadrant(8, 2) + + lowerLobeElementID = [] + upperLobeElementID = [] + # Lower lobe elements + for e3 in range(lElementsCount3): + lowerLobeElementID.append([]) + for e2 in range(lElementsCount2): + lowerLobeElementID[e3].append([]) + for e1 in range(lElementsCount1): + lowerLobeElementID[e3][e2].append(None) + + eft = eftRegular + nodeIdentifiers = [ + lowerNodeIds[e3][e2][e1], lowerNodeIds[e3][e2][e1 + 1], lowerNodeIds[e3][e2 + 1][e1], + lowerNodeIds[e3][e2 + 1][e1 + 1], + lowerNodeIds[e3 + 1][e2][e1], lowerNodeIds[e3 + 1][e2][e1 + 1], + lowerNodeIds[e3 + 1][e2 + 1][e1], lowerNodeIds[e3 + 1][e2 + 1][e1 + 1]] + + if (e2 == 0) and (e1 == 0): + # Back wedge elements + nodeIdentifiers.pop(4) + nodeIdentifiers.pop(0) + eft = eftWedgeCollapseXi1_15 + elif (e2 == 0) and (e1 == (lElementsCount1 - 1)): + # Back wedge elements + nodeIdentifiers.pop(5) + nodeIdentifiers.pop(1) + eft = eftWedgeCollapseXi1_26 + elif (e3 == 1) and (e2 == (lElementsCount2 - 2)): + # Middle wedge + nodeIdentifiers.pop(7) + nodeIdentifiers.pop(6) + eft = eftWedgeCollapseXi2_78 + elif (e3 == (lElementsCount3 - 1)) and (e2 == (lElementsCount2 - 3)): + # Remapped cube element 1 + eft = eftfactory.createEftBasic() + setEftScaleFactorIds(eft, [1], []) + remapEftNodeValueLabel(eft, [7, 8], Node.VALUE_LABEL_D_DS2, [(Node.VALUE_LABEL_D_DS3, [1])]) + remapEftNodeValueLabel(eft, [7, 8], Node.VALUE_LABEL_D_DS3, [(Node.VALUE_LABEL_D_DS2, [])]) + elif (e3 == (lElementsCount3 - 1)) and (e2 == (lElementsCount2 - 2)): + # Remapped cube element 2 + nodeIdentifiers[2] = lowerNodeIds[e3 - 1][e2 + 1][e1] + nodeIdentifiers[3] = lowerNodeIds[e3 - 1][e2 + 1][e1 + 1] + nodeIdentifiers[6] = lowerNodeIds[e3 - 1][e2 + 2][e1] + nodeIdentifiers[7] = lowerNodeIds[e3 - 1][e2 + 2][e1 + 1] + eft = eftfactory.createEftBasic() + setEftScaleFactorIds(eft, [1], []) + remapEftNodeValueLabel(eft, [5, 6], Node.VALUE_LABEL_D_DS2, [(Node.VALUE_LABEL_D_DS3, [1])]) + remapEftNodeValueLabel(eft, [5, 6], Node.VALUE_LABEL_D_DS3, [(Node.VALUE_LABEL_D_DS2, [])]) + remapEftNodeValueLabel(eft, [3, 4, 7, 8], Node.VALUE_LABEL_D_DS2, + [(Node.VALUE_LABEL_D_DS3, [1])]) + remapEftNodeValueLabel(eft, [3, 4, 7, 8], Node.VALUE_LABEL_D_DS3, + [(Node.VALUE_LABEL_D_DS2, [])]) + elif None in nodeIdentifiers: + continue + + if eft is eftRegular: + element = mesh.createElement(elementIdentifier, elementtemplateRegular) + else: + elementtemplateCustom.defineField(coordinates, -1, eft) + element = mesh.createElement(elementIdentifier, elementtemplateCustom) + element.setNodesByIdentifier(eft, nodeIdentifiers) + if eft.getNumberOfLocalScaleFactors() == 1: + element.setScaleFactors(eft, [-1.0]) + lowerLobeElementID[e3][e2][e1] = elementIdentifier + elementIdentifier += 1 + + # Annotation + lungMeshGroup.addElement(element) + lungSideMeshGroup.addElement(element) + if lowerLobeMeshGroup: + lowerLobeMeshGroup.addElement(element) + + # Upper lobe elements + for e3 in range(uElementsCount3): + upperLobeElementID.append([]) + for e2 in range(uElementsCount2): + upperLobeElementID[e3].append([]) + for e1 in range(uElementsCount1): + upperLobeElementID[e3][e2].append(None) + is_mediastanum = False + + eft = eftRegular + nodeIdentifiers = [ + upperNodeIds[e3][e2][e1], upperNodeIds[e3][e2][e1 + 1], upperNodeIds[e3][e2 + 1][e1], + upperNodeIds[e3][e2 + 1][e1 + 1], + upperNodeIds[e3 + 1][e2][e1], upperNodeIds[e3 + 1][e2][e1 + 1], + upperNodeIds[e3 + 1][e2 + 1][e1], upperNodeIds[e3 + 1][e2 + 1][e1 + 1]] + + if (e3 < (uElementsCount3 - 1)) and (e2 == (uElementsCount2 - 1)) and (e1 == 0): + is_mediastanum = True + # Distal-front wedge elements + nodeIdentifiers.pop(6) + nodeIdentifiers.pop(2) + eft = eftfactory.createEftBasic() + nodes = [3, 4, 7, 8] + collapseNodes = [3, 7] + remapEftNodeValueLabel(eft, nodes, Node.VALUE_LABEL_D_DS1, []) + remapEftNodeValueLabel(eft, collapseNodes, Node.VALUE_LABEL_D_DS2, + [(Node.VALUE_LABEL_D_DS1, []), (Node.VALUE_LABEL_D_DS2, [])]) + ln_map = [1, 2, 3, 3, 4, 5, 6, 6] + remapEftLocalNodes(eft, 6, ln_map) + + elif (e3 < (uElementsCount3 - 1)) and (e2 == (uElementsCount2 - 1)) and ( + e1 == (uElementsCount1 - 1)): + is_mediastanum = True + # Distal-back wedge elements + nodeIdentifiers.pop(7) + nodeIdentifiers.pop(3) + eft = eftfactory.createEftBasic() + setEftScaleFactorIds(eft, [1], []) + nodes = [3, 4, 7, 8] + collapseNodes = [4, 8] + remapEftNodeValueLabel(eft, collapseNodes, Node.VALUE_LABEL_D_DS2, + [(Node.VALUE_LABEL_D_DS1, [1]), (Node.VALUE_LABEL_D_DS2, [])]) + remapEftNodeValueLabel(eft, nodes, Node.VALUE_LABEL_D_DS1, []) + ln_map = [1, 2, 3, 3, 4, 5, 6, 6] + remapEftLocalNodes(eft, 6, ln_map) + + elif (e3 == (uElementsCount3 - 2)) and (e2 == 0) and (e1 == 0): + # Medial-front wedge elements + nodeIdentifiers.pop(4) + nodeIdentifiers.pop(0) + eft = eftWedgeCollapseXi1_15 + elif (e3 == (uElementsCount3 - 2)) and (e2 == 0) and (e1 == (uElementsCount1 - 1)): + # Medial-back wedge elements + nodeIdentifiers.pop(5) + nodeIdentifiers.pop(1) + eft = eftWedgeCollapseXi1_26 + elif (e3 == (uElementsCount3 - 1)) and (0 < e2 < (uElementsCount2 - 1)) and (e1 == 0): + is_mediastanum = True if (1 < e2 < (uElementsCount2 - 1)) else False + # Top-front wedge elements + nodeIdentifiers.pop(6) + nodeIdentifiers.pop(4) + eft = eftWedgeCollapseXi1_57 + elif (e3 == (uElementsCount3 - 1)) and (0 < e2 < (uElementsCount2 - 1)) and ( + e1 == (uElementsCount1 - 1)): + is_mediastanum = True if (1 < e2 < (uElementsCount2 - 1)) else False + # Top-back wedge elements + nodeIdentifiers.pop(7) + nodeIdentifiers.pop(5) + eft = eftWedgeCollapseXi1_68 + elif (e3 == (uElementsCount3 - 1)) and (e2 == 0) and (e1 == 0): + # Top-front-medial tetrahedron wedge elements + nodeIdentifiers.pop(6) + nodeIdentifiers.pop(5) + nodeIdentifiers.pop(4) + nodeIdentifiers.pop(0) + eft = eftTetCollapseXi1Xi2_82 + elif (e3 == (uElementsCount3 - 1)) and (e2 == 0) and (e1 == (uElementsCount1 - 1)): + # Top-back-medial tetrahedron wedge elements + nodeIdentifiers.pop(7) + nodeIdentifiers.pop(5) + nodeIdentifiers.pop(4) + nodeIdentifiers.pop(1) + eft = eftTetCollapseXi1Xi2_71 + elif (e3 == (uElementsCount3 - 1)) and (e2 == (uElementsCount2 - 1)) and (e1 == 0): + is_mediastanum = True + # Top-front-distal tetrahedron wedge elements + nodeIdentifiers.pop(7) + nodeIdentifiers.pop(6) + nodeIdentifiers.pop(4) + nodeIdentifiers.pop(2) + eft = eftfactory.createEftBasic() + setEftScaleFactorIds(eft, [1], []) + nodes = [5, 6, 7, 8] + # remap parameters on xi3 = 1 before collapsing nodes + remapEftNodeValueLabel(eft, nodes, Node.VALUE_LABEL_D_DS1, []) + remapEftNodeValueLabel(eft, nodes, Node.VALUE_LABEL_D_DS2, []) + remapEftNodeValueLabel(eft, [7, 8], Node.VALUE_LABEL_D_DS3, [(Node.VALUE_LABEL_D_DS2, [1])]) + remapEftNodeValueLabel(eft, [5], Node.VALUE_LABEL_D_DS3, [(Node.VALUE_LABEL_D_DS1, [])]) + remapEftNodeValueLabel(eft, [3, 4], Node.VALUE_LABEL_D_DS1, []) + remapEftNodeValueLabel(eft, [3], Node.VALUE_LABEL_D_DS2, + [(Node.VALUE_LABEL_D_DS1, []), (Node.VALUE_LABEL_D_DS2, [])]) + ln_map = [1, 2, 3, 3, 4, 4, 4, 4] + remapEftLocalNodes(eft, 4, ln_map) + + elif (e3 == (uElementsCount3 - 1)) and (e2 == (uElementsCount2 - 1)) and ( + e1 == (uElementsCount1 - 1)): + is_mediastanum = True + # Top-front-distal tetrahedron wedge elements + nodeIdentifiers.pop(7) + nodeIdentifiers.pop(6) + nodeIdentifiers.pop(5) + nodeIdentifiers.pop(3) + eft = eftfactory.createEftBasic() + setEftScaleFactorIds(eft, [1], []) + nodes = [5, 6, 7, 8] + # remap parameters on xi3 = 1 before collapsing nodes + remapEftNodeValueLabel(eft, nodes, Node.VALUE_LABEL_D_DS1, []) + remapEftNodeValueLabel(eft, nodes, Node.VALUE_LABEL_D_DS2, []) + remapEftNodeValueLabel(eft, [7, 8], Node.VALUE_LABEL_D_DS3, [(Node.VALUE_LABEL_D_DS2, [1])]) + remapEftNodeValueLabel(eft, [6], Node.VALUE_LABEL_D_DS3, [(Node.VALUE_LABEL_D_DS1, [1])]) + remapEftNodeValueLabel(eft, [3, 4], Node.VALUE_LABEL_D_DS1, []) + remapEftNodeValueLabel(eft, [4], Node.VALUE_LABEL_D_DS2, + [(Node.VALUE_LABEL_D_DS1, [1]), (Node.VALUE_LABEL_D_DS2, [])]) + ln_map = [1, 2, 3, 3, 4, 4, 4, 4] + remapEftLocalNodes(eft, 4, ln_map) + + elif (e3 == (uElementsCount3 - 2)) and (e2 == (uElementsCount2 - 3)): + # Remapped cube element 1 + eft = eftfactory.createEftBasic() + setEftScaleFactorIds(eft, [1], []) + remapEftNodeValueLabel(eft, [3, 4], Node.VALUE_LABEL_D_DS2, [(Node.VALUE_LABEL_D_DS3, [1])]) + remapEftNodeValueLabel(eft, [3, 4], Node.VALUE_LABEL_D_DS3, + [(Node.VALUE_LABEL_D_DS2, []), (Node.VALUE_LABEL_D_DS3, [])]) + elif (e3 == (uElementsCount3 - 2)) and (e2 == (uElementsCount2 - 2)): + # Remapped cube element 2 + eft = eftfactory.createEftBasic() + remapEftNodeValueLabel(eft, [1, 2], Node.VALUE_LABEL_D_DS3, + [(Node.VALUE_LABEL_D_DS2, []), (Node.VALUE_LABEL_D_DS3, [])]) + elif None in nodeIdentifiers: + continue + + if eft is eftRegular: + element = mesh.createElement(elementIdentifier, elementtemplateRegular) + else: + elementtemplateCustom.defineField(coordinates, -1, eft) + element = mesh.createElement(elementIdentifier, elementtemplateCustom) + element.setNodesByIdentifier(eft, nodeIdentifiers) + if eft.getNumberOfLocalScaleFactors() == 1: + element.setScaleFactors(eft, [-1.0]) + upperLobeElementID[e3][e2][e1] = elementIdentifier + elementIdentifier += 1 + + # Annotation + lungMeshGroup.addElement(element) + lungSideMeshGroup.addElement(element) + if middleLobeMeshGroup and (e3 < (uElementsCount3 - 2)): + middleLobeMeshGroup.addElement(element) + elif upperLobeMeshGroup: + upperLobeMeshGroup.addElement(element) + if is_mediastanum is True: + mediastinumGroupMeshGroup.addElement(element) + + return elementIdentifier, upperLobeElementID, lowerLobeElementID + +def createAccessorylobeLungNodes(centre, cache, coordinates, nodes, nodetemplate, lungSideNodesetGroup, lungNodesetGroup, + elementsCount1, elementsCount2, elementsCount3, + length, dorsalWidth, dorsalHeight, ventralWidth, ventralHeight, + nodeIds, nodeIdentifier): + """ + Create a 3D triangular mesh from getAccessorylobeLungNodes + :parameter: elementsCount1 - x, elementsCount2 - y, elementsCount3 - z + :return: nodeIdentifier + """ + + # Initialise parameters + lengthUnit = length/elementsCount2 + + # Determine a triangular face at the ventral and dorsal ends + dorsalHeightUnit = dorsalHeight/elementsCount3 + dorsalWidthUnit = dorsalWidth/elementsCount1 + ventralHeightUnit = ventralHeight/elementsCount3 + ventralWidthUnit = ventralWidth/elementsCount1 + + x_dorsal = [] + for n3 in range(elementsCount3 + 1): + x_dorsal.append([]) + for n2 in range(3): + x_dorsal[n3].append([]) + width = dorsalWidthUnit if n2 == 0 else ventralWidthUnit + height = dorsalHeightUnit if n2 == 0 else ventralHeightUnit + for n1 in range(elementsCount1 + 1): + x_dorsal[n3][n2].append(None) + if (n1 != 1) and (n3 == elementsCount3): + continue + unitRatio = 0.5 if n3 == 1 else 1.0 + x_dorsal[n3][n2][n1] = [width * unitRatio * (n1 - 1), lengthUnit * (n2*elementsCount2) - length/2.0, height * n3] + + # Interpolate triangular faces between two ends + px = [] + pd2 = [] + for n3 in range(elementsCount3 + 1): + px.append([]) + pd2.append([]) + for n1 in range(elementsCount1 + 1): + px[n3].append(None) + pd2[n3].append(None) + x_temp = [x_dorsal[n3][0][n1], x_dorsal[n3][1][n1]] + if None in x_temp: + continue + d2_temp = [(x_dorsal[n3][1][n1][i] - x_dorsal[n3][0][n1][i])/elementsCount2 for i in range(3)] + px[n3][n1], pd2[n3][n1], pe, pxi, psf = sampleCubicHermiteCurves(x_temp, [d2_temp, d2_temp], elementsCount2) + + # Accessory lobe nodes + for n3 in range(elementsCount3 + 1): + nodeIds.append([]) + for n2 in range(elementsCount2 + 1): + nodeIds[n3].append([]) + for n1 in range(elementsCount1 + 1): + nodeIds[n3][n2].append(None) + + if (n1 != 1) and (n3 == elementsCount3): + continue + + node = nodes.createNode(nodeIdentifier, nodetemplate) + cache.setNode(node) + + x = px[n3][n1][n2] + d2 = pd2[n3][n1][n2] + + if n3 < elementsCount3: + d1 = [px[n3][n1][n2][i] - px[n3][n1-1][n2][i] for i in range(3)] if n1 == elementsCount1 else \ + [px[n3][n1+1][n2][i] - px[n3][n1][n2][i] for i in range(3)] + d3 = [px[n3+1][1][n2][i] - px[n3][n1][n2][i] for i in range(3)] if n3 == 1 else \ + [px[n3 + 1][n1][n2][i] - px[n3][n1][n2][i] for i in range(3)] + else: + d1 = [px[n3-1][n1][n2][i] - px[n3-1][n1-1][n2][i] for i in range(3)] + d3 = [px[n3][n1][n2][i] - px[n3-1][n1][n2][i] for i in range(3)] + + # Translate the mesh + x = [x[0] + centre[0], x[1]+centre[1], x[2]] + + coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_VALUE, 1, x) + coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS1, 1, d1) + coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS2, 1, d2) + coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS3, 1, d3) + nodeIds[n3][n2][n1] = nodeIdentifier + nodeIdentifier += 1 + + # Annotation + lungSideNodesetGroup.addNode(node) + lungNodesetGroup.addNode(node) + + return nodeIdentifier + +def createAccessorylobeLungElements(coordinates, eftfactory, eftRegular, elementtemplateRegular, elementtemplateCustom, + mesh, lungMeshGroup, diaphragmaticLobeMeshGroup, + elementsCount1, elementsCount2, elementsCount3, + NodeIds, elementIdentifier): + """ + Create a 3D triangular mesh from getAccessorylobeLungNodes + :parameter: elementsCount1 - x, elementsCount2 - y, elementsCount3 - z + :return: elementIdentifier + """ + + # Accessory lobe elements + for e3 in range(elementsCount3): + for e2 in range(elementsCount2): + for e1 in range(elementsCount1): + eft = eftRegular + nodeIdentifiers = [ + NodeIds[e3][e2][e1], NodeIds[e3][e2][e1 + 1], NodeIds[e3][e2 + 1][e1], + NodeIds[e3][e2 + 1][e1 + 1], + NodeIds[e3 + 1][e2][e1], NodeIds[e3 + 1][e2][e1 + 1], + NodeIds[e3 + 1][e2 + 1][e1], NodeIds[e3 + 1][e2 + 1][e1 + 1]] + + if (e1 == 0) and (e3 == (elementsCount3 - 1)): + # wedge elements along crest + nodeIdentifiers.pop(6) + nodeIdentifiers.pop(4) + eft = eftfactory.createEftBasic() + nodes = [5, 6, 7, 8] + collapseNodes = [5, 7] + remapEftNodeValueLabel(eft, nodes, Node.VALUE_LABEL_D_DS1, []) + remapEftNodeValueLabel(eft, collapseNodes, Node.VALUE_LABEL_D_DS3, + [(Node.VALUE_LABEL_D_DS1, []), (Node.VALUE_LABEL_D_DS3, [])]) + + ln_map = [1, 2, 3, 4, 5, 5, 6, 6] + remapEftLocalNodes(eft, 6, ln_map) + + elif (e1 == elementsCount1 - 1) and (e3 == (elementsCount3 - 1)): + nodeIdentifiers.pop(7) + nodeIdentifiers.pop(5) + eft = eftfactory.createEftBasic() + setEftScaleFactorIds(eft, [1], []) + nodes = [5, 6, 7, 8] + collapseNodes = [6, 8] + remapEftNodeValueLabel(eft, collapseNodes, Node.VALUE_LABEL_D_DS3, + [(Node.VALUE_LABEL_D_DS1, [1]), (Node.VALUE_LABEL_D_DS3, [])]) + remapEftNodeValueLabel(eft, nodes, Node.VALUE_LABEL_D_DS1, []) + ln_map = [1, 2, 3, 4, 5, 5, 6, 6] + remapEftLocalNodes(eft, 6, ln_map) + + elif None in nodeIdentifiers: + continue + + if eft is eftRegular: + element = mesh.createElement(elementIdentifier, elementtemplateRegular) + else: + elementtemplateCustom.defineField(coordinates, -1, eft) + element = mesh.createElement(elementIdentifier, elementtemplateCustom) + element.setNodesByIdentifier(eft, nodeIdentifiers) + if eft.getNumberOfLocalScaleFactors() == 1: + element.setScaleFactors(eft, [-1.0]) + elementIdentifier += 1 + + # Annotation + lungMeshGroup.addElement(element) + diaphragmaticLobeMeshGroup.addElement(element) + + return elementIdentifier + +def createDiscontinuity(coordinates, nodes, mesh, fieldcache, nodetemplate, leftUpperLobeElementID, rightUpperLobeElementID, nodeIdentifier): + """ + Refine source mesh into separate region, with change of basis. + :param meshrefinement: MeshRefinement, which knows source and target region. + :param options: Dict containing options. See getDefaultOptions(). + """ + # Replicating nodes at fissures + for e4 in range(2): + upperNodeIds = [] + store_coordinate = [] + UpperLobeElementID = leftUpperLobeElementID if e4 == 0 else rightUpperLobeElementID + if UpperLobeElementID == None: + continue + for e3 in range(len(UpperLobeElementID)): + upperNodeIds.append([]) + for e2 in range(len(UpperLobeElementID[e3])): + for e1 in range(len(UpperLobeElementID[e3][e2])): + # Fissures along the upper lobe + if ((e2 == 2) and (e3 < 2)) or ((e2 < 4) and (e3 == 2)): + elementIdentifier = UpperLobeElementID[e3][e2][e1] + element = mesh.findElementByIdentifier(elementIdentifier) + eft = element.getElementfieldtemplate(coordinates, -1) + + # Exclude horizontal fissure in the left lobe + if (e4 == 0) and (e3 == 2) and (e2 > 1): + continue + + if e3 < 2: + # Middle lobe + localNodeIndexes = [1, 2] if e1 == 0 else [2] + if e3 == 1: + if e1 == 0: + localNodeIndexes.append(5) + localNodeIndexes.append(6) + else: + # Upper lobe + if e2 == 0: + # Dorsal wedge + localNodeIndexes = [1] if (e1 == 0) else [] + elif e2 == 3: + # Ventral wedge + localNodeIndexes = [1, 2] if (e1 == 0) else [2, 3] + else: + # Regular element + localNodeIndexes = [1, 2] if (e1 == 0) else [2] + + for localNodeIndex in localNodeIndexes: + node = element.getNode(eft, localNodeIndex) + nodetemplate.defineFieldFromNode(coordinates, node) + versionsCount = nodetemplate.getValueNumberOfVersions(coordinates, -1, Node.VALUE_LABEL_VALUE) + + if versionsCount == 1: + fieldcache.setNode(node) + result0, x = coordinates.getNodeParameters(fieldcache, -1, Node.VALUE_LABEL_VALUE, 1, 3) + result0, d1 = coordinates.getNodeParameters(fieldcache, -1, Node.VALUE_LABEL_D_DS1, 1, 3) + result0, d2 = coordinates.getNodeParameters(fieldcache, -1, Node.VALUE_LABEL_D_DS2, 1, 3) + result0, d3 = coordinates.getNodeParameters(fieldcache, -1, Node.VALUE_LABEL_D_DS3, 1, 3) + + if (localNodeIndex > 2) and (e3 == 1): + # Store 3-way points in the middle lobe + store_coordinate.append([x, d1, d2, d3]) + continue + + node = nodes.createNode(nodeIdentifier, nodetemplate) + fieldcache.setNode(node) + coordinates.setNodeParameters(fieldcache, -1, Node.VALUE_LABEL_VALUE, 1, x) + coordinates.setNodeParameters(fieldcache, -1, Node.VALUE_LABEL_D_DS1, 1, d1) + coordinates.setNodeParameters(fieldcache, -1, Node.VALUE_LABEL_D_DS2, 1, d2) + coordinates.setNodeParameters(fieldcache, -1, Node.VALUE_LABEL_D_DS3, 1, d3) + upperNodeIds[e3].append(nodeIdentifier) + nodeIdentifier += 1 + + # Create 3-way points in the middle lobe + if (e3 == 1) and (e2 == 2) and (e1 == 1): + for i in range(len(store_coordinate)): + node = nodes.createNode(nodeIdentifier, nodetemplate) + fieldcache.setNode(node) + coordinates.setNodeParameters(fieldcache, -1, Node.VALUE_LABEL_VALUE, 1, store_coordinate[i][0]) + coordinates.setNodeParameters(fieldcache, -1, Node.VALUE_LABEL_D_DS1, 1, store_coordinate[i][1]) + coordinates.setNodeParameters(fieldcache, -1, Node.VALUE_LABEL_D_DS2, 1, store_coordinate[i][2]) + coordinates.setNodeParameters(fieldcache, -1, Node.VALUE_LABEL_D_DS3, 1, store_coordinate[i][3]) + upperNodeIds[e3].append(nodeIdentifier) + nodeIdentifier += 1 + + # Change node index in the element + for e3 in range(len(UpperLobeElementID)): + # middle lobe and upper lobe idx + temp_idx = upperNodeIds[0] + upperNodeIds[1] if e3 < 2 else upperNodeIds[2] + for e2 in range(len(UpperLobeElementID[e3])): + for e1 in range(len(UpperLobeElementID[e3][e2])): + # Fissures along the upper lobe + if ((e2 == 2) and (e3 < 2)) or ((e2 < 4) and (e3 == 2)): + elementIdentifier = UpperLobeElementID[e3][e2][e1] + element = mesh.findElementByIdentifier(elementIdentifier) + eft = element.getElementfieldtemplate(coordinates, -1) + nodeIds = get_element_node_identifiers(element, eft) + + # Exclude horizontal fissure in the left lobe + if (e4 == 0) and (e3 == 2) and (e2 > 2): + continue + + if (e3 == 2) and (e2 == 0): + # Dorsal wedge + nd2 = e1 + 1 + nodeIds[0] = temp_idx[0] + nodeIds[1:3] = [temp_idx[nd2], temp_idx[nd2 + 1]] + elif (e3 == 2) and (e2 < 3): + # upper and lower lobes + if e4 == 1: + # Right lung + nd1 = (e2 * e2) + e1 + nd2 = (e2 * 3) + e1 + 1 + nodeIds[:2] = [temp_idx[nd1], temp_idx[nd1 + 1]] + nodeIds[2:4] = [temp_idx[nd2], temp_idx[nd2 + 1]] + else: + # LeftLung + nd1 = (e2 * e2) + e1 + nd2 = (e2 * 2) + e1 + 1 if (e2 < 2) else e2 + e1 + 1 + temp_idx_1 = upperNodeIds[1] + nodeIds[:2] = [temp_idx[nd1], temp_idx[nd1 + 1]] if (e2 < 2) else \ + [temp_idx_1[nd2], temp_idx_1[nd2 + 1]] + if (e2 < 2): + nodeIds[2:4] = [temp_idx_1[nd2], temp_idx_1[nd2 + 1]] + elif (e3 == 2) and (e2 == 3): + # Ventral wedge + nd2 = (e3 * 3) + e1 + 1 + nodeIds[2] = temp_idx[-1] + nodeIds[:2] = [temp_idx[nd2], temp_idx[nd2 + 1]] + else: + # Middle lobe + nd1 = (e3 * 3) + e1 + nd2 = ((e3 + 1) * 3) + e1 + nodeIds[:2] = [temp_idx[nd1], temp_idx[nd1 + 1]] + nodeIds[4:6] = [temp_idx[nd2], temp_idx[nd2 + 1]] + element.setNodesByIdentifier(eft, nodeIds) + + return nodeIdentifier + +def concavingDiaphragmaticSurface(diaphragmCurvatureX, diaphragmCurvatureY, fm, coordinates, diaphragmCentreX, + diaphragmCentreY, lungNodesetGroup): + """ + # s_x = x - x_centre s_y = y - y_centre + # kappa_x = diaphragmCurvatureX kappa_y = diaphragmCurvatureY + # theta_x = kappa_x * s_x theta_y = kappa_y * s_y + # r_x = 1/kappa_x r_y = 1/kappa_y + # s_zx = z + r_x s_zy = z + r_y + # delta_zx = s_zx * (cos(theta_x) - 1.0) delta_zy = s_zy * (cos(theta_y) - 1.0) + # ---------------------------------------------------------------------------------------- + # x_new = s_zx * sin(theta_x) + # y_new = s_zy * sin(theta_y) + # z_new = z + deta_zx + deta_zy + + :param diaphragmCurvatureX: + :param diaphragmCurvatureY: + :param fm: + :param coordinates: + :param diaphragmCentre: + :param lungNodesetGroup: + :return: + """ + + # Initialise parameters + diaphragmCentre = fm.createFieldConstant([diaphragmCentreX, diaphragmCentreY, 0.0]) + offset_coordinates = fm.createFieldSubtract(coordinates, diaphragmCentre) + only_x = fm.createFieldConstant([1.0, 0.0, 0.0]) + only_y = fm.createFieldConstant([0.0, 1.0, 0.0]) + only_z = fm.createFieldConstant([0.0, 0.0, 1.0]) + + x_new = fm.createFieldMultiply(coordinates, only_x) + y_new = fm.createFieldMultiply(coordinates, only_y) + delta_zx = fm.createFieldConstant([0.0, 0.0, 0.0]) + delta_zy = fm.createFieldConstant([0.0, 0.0, 0.0]) + + # x-coordinates + if diaphragmCurvatureX != 0.0: + kappa_x = fm.createFieldConstant([diaphragmCurvatureX, 0.0, 0.0]) + r_x = fm.createFieldConstant([1/diaphragmCurvatureX, 0.0, 0.0]) + s_x = fm.createFieldMultiply(offset_coordinates, only_x) + z_x = fm.createFieldMultiply(coordinates, only_z) + z_x = fm.createFieldComponent(z_x, [3, 1, 1]) + s_zx = r_x # if no bulge s_zx = r_x + theta_x = fm.createFieldMultiply(kappa_x, s_x) + x_new = fm.createFieldMultiply(s_zx, fm.createFieldSin(theta_x)) + delta_zx = fm.createFieldMultiply(s_zx, fm.createFieldSubtract(fm.createFieldCos(theta_x), + fm.createFieldConstant([1.0, 0.0, 0.0]))) + delta_zx = fm.createFieldComponent(delta_zx, [3, 3, 1]) + + # y-coordinates + if diaphragmCurvatureY != 0.0: + kappa_y = fm.createFieldConstant([0.0, diaphragmCurvatureY, 0.0]) + r_y = fm.createFieldConstant([0.0, 1/diaphragmCurvatureY, 0.0]) + s_y = fm.createFieldMultiply(offset_coordinates, only_y) + z_y = fm.createFieldMultiply(coordinates, only_z) + z_y = fm.createFieldComponent(z_y, [1, 3, 1]) + s_zy = r_y # if no bulge s_zx = r_y + theta_y = fm.createFieldMultiply(kappa_y, s_y) + y_new = fm.createFieldMultiply(s_zy, fm.createFieldSin(theta_y)) + delta_zy = fm.createFieldMultiply(s_zy, fm.createFieldSubtract(fm.createFieldCos(theta_y), + fm.createFieldConstant([0.0, 1.0, 0.0]))) + delta_zy = fm.createFieldComponent(delta_zy, [3, 3, 2]) + + # z-coordinates + z = fm.createFieldMultiply(coordinates, only_z) + z_new = fm.createFieldAdd(delta_zx, delta_zy) + z_new = fm.createFieldAdd(z_new, z) + + new_coordinates = fm.createFieldAdd(x_new, y_new) + new_coordinates = fm.createFieldAdd(new_coordinates, z_new) + + fieldassignment = coordinates.createFieldassignment(new_coordinates) + fieldassignment.setNodeset(lungNodesetGroup) + fieldassignment.assign() + +def bendingAroundZAxis(curvature, fm, coordinates, lungNodesetGroup, spaceFromCentre): + """ + :param bulgeRadius: the radius and the centre of curvature to transfrom the scaffold + :param fm: + :param coordinates: + :param nodes: + :return: + """ + # cylindrical polar coordinates (x = r*cos(theta), y = r*sin(theta), z = z): + # r = x - bulgeRadius + # theta = y / bulgeRadius + # z = z + + radius = 1/curvature + scale = fm.createFieldConstant([1.0, curvature, 1.0]) + scaleCoordinates = fm.createFieldMultiply(coordinates, scale) + if isinstance(spaceFromCentre, list): + offset_y = fm.createFieldConstant([0.0, -spaceFromCentre[1], 0.0]) + coordinates_offsety = fm.createFieldAdd(coordinates, offset_y) + scaleCoordinates = fm.createFieldMultiply(coordinates_offsety, scale) + offset = fm.createFieldConstant([radius - spaceFromCentre[0], 0.0, 0.0]) + else: + offset = fm.createFieldConstant([radius + spaceFromCentre, 0.0, 0.0]) + polarCoordinates = fm.createFieldAdd(scaleCoordinates, offset) + polarCoordinates.setCoordinateSystemType(Field.COORDINATE_SYSTEM_TYPE_CYLINDRICAL_POLAR) + rcCoordinates = fm.createFieldCoordinateTransformation(polarCoordinates) + rcCoordinates.setCoordinateSystemType(Field.COORDINATE_SYSTEM_TYPE_RECTANGULAR_CARTESIAN) + newxyzCoordinates = fm.createFieldSubtract(rcCoordinates, offset) + if isinstance(spaceFromCentre, list): + newxyzCoordinates = fm.createFieldSubtract(newxyzCoordinates, offset_y) + fieldassignment = coordinates.createFieldassignment(newxyzCoordinates) + fieldassignment.setNodeset(lungNodesetGroup) + fieldassignment.assign() + +def sharpeningRidge(sharpeningFactor, fm, coordinates, lungNodesetGroup, spaceFromCentre, length): + """ + Linear transformation + :param sharpRadius: + :param fm: + :param coordinates: + :param lungNodesetGroup: + :param spaceFromCentre: + :return: + """ + # Transformation matrix = [ k1y + 1, | [x, + # 1, | y, + # 1] | z] + offset = fm.createFieldConstant([spaceFromCentre, 0.75 * length, 0.0]) + origin = fm.createFieldAdd(coordinates, offset) + k1 = -sharpeningFactor / (length * 1.75) + scale = fm.createFieldConstant([0.0, k1, 0.0]) + scaleFunction = fm.createFieldMultiply(origin, scale) + constant = fm.createFieldConstant([0.0, 1.0, 1.0]) + constantFunction = fm.createFieldAdd(scaleFunction, constant) + transformation_matrix = fm.createFieldComponent(constantFunction, [2, 3, 3]) + taper_coordinates = fm.createFieldMultiply(origin, transformation_matrix) + translate_coordinates = fm.createFieldSubtract(taper_coordinates, offset) + fieldassignment = coordinates.createFieldassignment(translate_coordinates) + fieldassignment.setNodeset(lungNodesetGroup) + fieldassignment.assign() + +def tiltLungs(tiltApex_xAxis, tiltApex_yAxis, tiltDiap_yAxis, tiltDiap_xAxis, fm, coordinates, lungNodesetGroup): + """ + :param tiltDegree: [tilted degree for apex, for diaphragm] + :param fm: + :param coordinates: + :param nodes: + :return: transformed lungs + """ + # FieldConstant - Matrix = [ x1, x4, sh_zx, + # x2, x5, sh_zy, + # sh_xz, sh_yz, x9] + sh_xz = tiltApex_xAxis + sh_yz = tiltApex_yAxis + sh_zy = tiltDiap_yAxis + sh_zx = tiltDiap_xAxis + shearMatrix = fm.createFieldConstant([1.0, 0.0, sh_xz, 0.0, 1.0, sh_yz, sh_zx, sh_zy, 1.0]) + newCoordinates = fm.createFieldMatrixMultiply(3, shearMatrix, coordinates) + fieldassignment = coordinates.createFieldassignment(newCoordinates) + fieldassignment.setNodeset(lungNodesetGroup) + fieldassignment.assign() + +def rotateLungs(rotateZ, fm, coordinates, lungNodesetGroup, spaceFromCentre): + """ + Rotate a specific lung at the center of the elements about z-axis + :param rotateZ: + :param rotateY: + :param rotateX: + :param fm: + :param coordinates: + :param lungNodesetGroup: + :param spaceFromCentre: + :return: + """ + # FieldConstant - Matrix = [ x1, x4, x7, + # x2, x5, x8, + # x3, x6, x9] + if isinstance(spaceFromCentre, list): + offset = fm.createFieldConstant([spaceFromCentre[0], -spaceFromCentre[1], 0.0]) + else: + offset = fm.createFieldConstant([spaceFromCentre, 0.0, 0.0]) + origin = fm.createFieldAdd(coordinates, offset) + + if rotateZ != 0.0: + rotateZ = -rotateZ / 180 * math.pi # negative value due to right handed rule + rotateZMatrix = fm.createFieldConstant([math.cos(rotateZ), math.sin(rotateZ), 0.0, -math.sin(rotateZ), math.cos(rotateZ), 0.0, 0.0, 0.0, 1.0]) + newCoordinates = fm.createFieldMatrixMultiply(3, rotateZMatrix, origin) + translate_coordinates = fm.createFieldSubtract(newCoordinates, offset) + fieldassignment = coordinates.createFieldassignment(translate_coordinates) + fieldassignment.setNodeset(lungNodesetGroup) + fieldassignment.assign() + +def medialProtrusion(protrusion_factor, fm, coordinates, lungNodesetGroup, spaceFromCentre, width, length, height): + """ + :param tiltDegree: [tilted degree for apex, for diaphragm] + :param fm: + :param coordinates: + :param nodes: + :return: transformed lungs + """ + # FieldConstant - Matrix = [ x1, x4, x7, + # x2, x5, x8, + # x3, x6, x9] + offset = fm.createFieldConstant([spaceFromCentre + width, length * 0.3, 0.0]) + origin = fm.createFieldAdd(coordinates, offset) + squaredOrigin = fm.createFieldMultiply(origin, origin) + peak = 2 - protrusion_factor # initial value is 1/(acceptable protrusion factor *0.67) 2.25 + rateOfChangeY = protrusion_factor*10 + rateOfChangeZ = protrusion_factor*10 + + scale = fm.createFieldConstant([1.0, 0.0, 0.0]) + scale_1 = fm.createFieldConstant([peak, 1.0, 1.0]) + + constant = fm.createFieldConstant([0.0, rateOfChangeY, 0.0]) + scaleFunction = fm.createFieldMultiply(constant, squaredOrigin) # [0.0, k1y^2, 0.0] + squaredY = fm.createFieldComponent(scaleFunction, [2, 1, 1]) + squaredZ = fm.createFieldComponent(scaleFunction, [3, 1, 1]) + squaredYZ = fm.createFieldAdd(squaredY, squaredZ) # [k1y^2, 0.0, 0.0] + squaredYZOne = fm.createFieldAdd(squaredYZ, scale_1) # [k1y^2 + peak, 1.0, 1.0] + + recipFunction = fm.createFieldDivide(scale, squaredYZOne) # 1/[k1y^2 + peak], 0.0/1.0, 0.0/1.0] + + constant_1 = fm.createFieldConstant([0.5, 0.0, 1.0]) + yFunction = fm.createFieldAdd(recipFunction, constant_1) # 2.0 * |width|/[k1y^2 + peak], 1.0, 1.0] + xyFunction = fm.createFieldComponent(yFunction, [1, 3, 3]) # 2.0 * |width|/[k1y^2 + peak], 1.0, 1.0] + + constant = fm.createFieldConstant([0.0, 0.0, rateOfChangeZ]) + scaleFunction = fm.createFieldMultiply(constant, squaredOrigin) # [0.0, 0.0, k2z^2] + squaredY = fm.createFieldComponent(scaleFunction, [2, 1, 1]) + squaredZ = fm.createFieldComponent(scaleFunction, [3, 1, 1]) + squaredYZ = fm.createFieldAdd(squaredY, squaredZ) # [k2z^2, 0.0, 0.0] + squaredYZOne = fm.createFieldAdd(squaredYZ, scale_1) # [k2z^2 + peak, 1.0, 1.0] + + recipFunction = fm.createFieldDivide(scale, squaredYZOne) # 1/[k2z^2 + peak], 0/1.0, 0/1.0] + + constant_1 = fm.createFieldConstant([height/2, 0.0, 1.0]) + zFunction = fm.createFieldAdd(recipFunction, constant_1) # height/[k2z^2 + peak], 0.0, 1.0 + xzFunction = fm.createFieldComponent(zFunction, [1, 3, 3]) # height/[k2z^2 + peak], 1.0, 1.0 + + transformation_matrix = fm.createFieldMultiply(xyFunction, xzFunction) + + taper_coordinates = fm.createFieldMultiply(origin, transformation_matrix) + translate_coordinates = fm.createFieldSubtract(taper_coordinates, offset) + + fieldassignment = coordinates.createFieldassignment(translate_coordinates) + fieldassignment.setNodeset(lungNodesetGroup) + fieldassignment.assign() diff --git a/src/scaffoldmaker/scaffolds.py b/src/scaffoldmaker/scaffolds.py index eb587c5f..89492fd6 100644 --- a/src/scaffoldmaker/scaffolds.py +++ b/src/scaffoldmaker/scaffolds.py @@ -33,6 +33,7 @@ from scaffoldmaker.meshtypes.meshtype_3d_heartventriclesbase2 import MeshType_3d_heartventriclesbase2 from scaffoldmaker.meshtypes.meshtype_3d_lens1 import MeshType_3d_lens1 from scaffoldmaker.meshtypes.meshtype_3d_lung1 import MeshType_3d_lung1 +from scaffoldmaker.meshtypes.meshtype_3d_lung2 import MeshType_3d_lung2 from scaffoldmaker.meshtypes.meshtype_3d_ostium1 import MeshType_3d_ostium1 from scaffoldmaker.meshtypes.meshtype_3d_smallintestine1 import MeshType_3d_smallintestine1 from scaffoldmaker.meshtypes.meshtype_3d_solidcylinder1 import MeshType_3d_solidcylinder1 @@ -83,6 +84,7 @@ def __init__(self): MeshType_3d_heartventriclesbase2, MeshType_3d_lens1, MeshType_3d_lung1, + MeshType_3d_lung2, MeshType_3d_ostium1, MeshType_3d_smallintestine1, MeshType_3d_solidcylinder1, diff --git a/tests/test_lung.py b/tests/test_lung.py index 728f2df4..75f62ec3 100644 --- a/tests/test_lung.py +++ b/tests/test_lung.py @@ -9,6 +9,7 @@ from scaffoldmaker.annotation.annotationgroup import getAnnotationGroupForTerm from scaffoldmaker.annotation.lung_terms import get_lung_term from scaffoldmaker.meshtypes.meshtype_3d_lung1 import MeshType_3d_lung1 +from scaffoldmaker.meshtypes.meshtype_3d_lung2 import MeshType_3d_lung2 from scaffoldmaker.utils.meshrefinement import MeshRefinement from testutils import assertAlmostEqualList @@ -179,5 +180,168 @@ def test_lung1(self): self.assertEqual(2353, element.getIdentifier()) assertAlmostEqualList(self, xi, [ 0.0, 0.0, 1.0 ], 1.0E-10) + def test_lung2(self): + """ + Test creation of heart scaffold. + """ + scaffold = MeshType_3d_lung2 + parameterSetNames = scaffold.getParameterSetNames() + self.assertEqual(parameterSetNames, [ "Default", "Human 1", "Mouse 1", "Rat 1", "Pig 1", "Material" ]) + options = scaffold.getDefaultOptions(["Human 1"]) + self.assertEqual(36, len(options)) + self.assertFalse(scaffold.checkOptions(options)) + + context = Context("Test") + region = context.getDefaultRegion() + self.assertTrue(region.isValid()) + annotationGroups = scaffold.generateMesh(region, options) + self.assertEqual(29, len(annotationGroups)) + fieldmodule = region.getFieldmodule() + mesh3d = fieldmodule.findMeshByDimension(3) + self.assertEqual(88, mesh3d.getSize()) + mesh2d = fieldmodule.findMeshByDimension(2) + self.assertEqual(292, mesh2d.getSize()) + mesh1d = fieldmodule.findMeshByDimension(1) + self.assertEqual(334, mesh1d.getSize()) + nodes = fieldmodule.findNodesetByFieldDomainType(Field.DOMAIN_TYPE_NODES) + self.assertEqual(139, nodes.getSize()) + datapoints = fieldmodule.findNodesetByFieldDomainType(Field.DOMAIN_TYPE_DATAPOINTS) + self.assertEqual(0, datapoints.getSize()) + + # check coordinates range, outside surface area and volume + coordinates = fieldmodule.findFieldByName("coordinates").castFiniteElement() + self.assertTrue(coordinates.isValid()) + minimums, maximums = evaluateFieldNodesetRange(coordinates, nodes) + assertAlmostEqualList(self, minimums, [ -0.6380112948407249, -0.5136821588344925, -0.2594693243518892 ], 1.0E-6) + assertAlmostEqualList(self, maximums, [ 0.6380112948407249, 0.36430478702255786, 0.9368327915434912 ], 1.0E-6) + with ChangeManager(fieldmodule): + one = fieldmodule.createFieldConstant(1.0) + upperRightFissureGroup = fieldmodule.findFieldByName('upper lobe of right lung').castGroup() + self.assertTrue(upperRightFissureGroup.isValid()) + upperRightFissureMeshGroup = upperRightFissureGroup.getFieldElementGroup(mesh2d).getMeshGroup() + self.assertTrue(upperRightFissureMeshGroup.isValid()) + surfaceAreaField = fieldmodule.createFieldMeshIntegral(one, coordinates, upperRightFissureMeshGroup) + surfaceAreaField.setNumbersOfPoints(4) + volumeField = fieldmodule.createFieldMeshIntegral(one, coordinates, mesh3d) + volumeField.setNumbersOfPoints(3) + fieldcache = fieldmodule.createFieldcache() + result, surfaceArea = surfaceAreaField.evaluateReal(fieldcache, 1) + self.assertEqual(result, RESULT_OK) + self.assertAlmostEqual(surfaceArea, 2.145407946129267, delta=1.0E-2) + result, volume = volumeField.evaluateReal(fieldcache, 1) + self.assertEqual(result, RESULT_OK) + self.assertAlmostEqual(volume, 0.5026277967433523, delta=1.0E-2) + + # check some annotationGroups: + expectedSizes3d = { + "lung": 88, + "left lung": 44, + "right lung": 44, + "upper lobe of left lung": 24, + "lower lobe of left lung": 20, + "upper lobe of right lung": 16, + "middle lobe of right lung": 8, + "lower lobe of right lung": 20, + # "right lung accessory lobe": 0 + } + for name in expectedSizes3d: + group = getAnnotationGroupForTerm(annotationGroups, get_lung_term(name)) + size = group.getMeshGroup(mesh3d).getSize() + self.assertEqual(expectedSizes3d[name], size, name) + expectedSizes2d = { + "horizontal fissure of right lung": 4, + "oblique fissure of left lung": 8, + "oblique fissure of right lung": 8 + } + for name in expectedSizes2d: + group = getAnnotationGroupForTerm(annotationGroups, get_lung_term(name)) + size = group.getMeshGroup(mesh2d).getSize() + self.assertEqual(expectedSizes2d[name], size, name) + + # test finding a marker in scaffold + markerGroup = fieldmodule.findFieldByName("marker").castGroup() + markerNodes = markerGroup.getFieldNodeGroup(nodes).getNodesetGroup() + self.assertEqual(7, markerNodes.getSize()) + markerName = fieldmodule.findFieldByName("marker_name") + self.assertTrue(markerName.isValid()) + markerLocation = fieldmodule.findFieldByName("marker_location") + self.assertTrue(markerLocation.isValid()) + # test apex marker point + cache = fieldmodule.createFieldcache() + node = findNodeWithName(markerNodes, markerName, "apex of left lung") + self.assertTrue(node.isValid()) + cache.setNode(node) + element, xi = markerLocation.evaluateMeshLocation(cache, 3) + self.assertEqual(40, element.getIdentifier()) + assertAlmostEqualList(self, xi, [ 0.0, 1.0, 1.0 ], 1.0E-10) + + # refine 4 and check result + # first remove any face (but not point) annotation groups as they are re-added by defineFaceAnnotations + removeAnnotationGroups = [] + for annotationGroup in annotationGroups: + if (not annotationGroup.hasMeshGroup(mesh3d)) and (annotationGroup.hasMeshGroup(mesh2d) or annotationGroup.hasMeshGroup(mesh1d)): + removeAnnotationGroups.append(annotationGroup) + for annotationGroup in removeAnnotationGroups: + annotationGroups.remove(annotationGroup) + self.assertEqual(15, len(annotationGroups)) + + refineRegion = region.createRegion() + refineFieldmodule = refineRegion.getFieldmodule() + options['Refine'] = True + options['Refine number of elements'] = 4 + refineNumberOfElements = options['Refine number of elements'] + meshrefinement = MeshRefinement(region, refineRegion, annotationGroups) + scaffold.refineMesh(meshrefinement, options) + annotationGroups = meshrefinement.getAnnotationGroups() + + refineFieldmodule.defineAllFaces() + oldAnnotationGroups = copy.copy(annotationGroups) + for annotationGroup in annotationGroups: + annotationGroup.addSubelements() + scaffold.defineFaceAnnotations(refineRegion, options, annotationGroups) + for annotation in annotationGroups: + if annotation not in oldAnnotationGroups: + annotationGroup.addSubelements() + self.assertEqual(29, len(annotationGroups)) + + mesh3d = refineFieldmodule.findMeshByDimension(3) + self.assertEqual(5632, mesh3d.getSize()) + mesh2d = refineFieldmodule.findMeshByDimension(2) + self.assertEqual(17344, mesh2d.getSize()) + mesh1d = refineFieldmodule.findMeshByDimension(1) + self.assertEqual(17848, mesh1d.getSize()) + nodes = refineFieldmodule.findNodesetByFieldDomainType(Field.DOMAIN_TYPE_NODES) + self.assertEqual(6145, nodes.getSize()) + datapoints = refineFieldmodule.findNodesetByFieldDomainType(Field.DOMAIN_TYPE_DATAPOINTS) + self.assertEqual(0, datapoints.getSize()) + + # check some refined annotationGroups: + for name in expectedSizes3d: + group = getAnnotationGroupForTerm(annotationGroups, get_lung_term(name)) + size = group.getMeshGroup(mesh3d).getSize() + self.assertEqual(expectedSizes3d[name]*(refineNumberOfElements**3), size, name) + for name in expectedSizes2d: + group = getAnnotationGroupForTerm(annotationGroups, get_lung_term(name)) + size = group.getMeshGroup(mesh2d).getSize() + self.assertEqual(expectedSizes2d[name]*(refineNumberOfElements**2), size, name) + + # test finding a marker in refined scaffold + markerGroup = refineFieldmodule.findFieldByName("marker").castGroup() + refinedNodes = refineFieldmodule.findNodesetByFieldDomainType(Field.DOMAIN_TYPE_NODES) + markerNodes = markerGroup.getFieldNodeGroup(refinedNodes).getNodesetGroup() + self.assertEqual(7, markerNodes.getSize()) + markerName = refineFieldmodule.findFieldByName("marker_name") + self.assertTrue(markerName.isValid()) + markerLocation = refineFieldmodule.findFieldByName("marker_location") + self.assertTrue(markerLocation.isValid()) + # test apex marker point + cache = refineFieldmodule.createFieldcache() + node = findNodeWithName(markerNodes, markerName, "apex of left lung") + self.assertTrue(node.isValid()) + cache.setNode(node) + element, xi = markerLocation.evaluateMeshLocation(cache, 3) + self.assertEqual(2557, element.getIdentifier()) + assertAlmostEqualList(self, xi, [ 0.0, 1.0, 1.0 ], 1.0E-10) + if __name__ == "__main__": unittest.main() From c4dae5f711a538a9dd5ad47122bcdea3f379a2f7 Mon Sep 17 00:00:00 2001 From: arti-sukasem <51779010+arti-sukasem@users.noreply.github.com> Date: Tue, 24 May 2022 00:29:21 +1200 Subject: [PATCH 02/10] Fix review and update parameters --- .../meshtypes/meshtype_3d_lung2.py | 215 ++++++++++-------- 1 file changed, 116 insertions(+), 99 deletions(-) diff --git a/src/scaffoldmaker/meshtypes/meshtype_3d_lung2.py b/src/scaffoldmaker/meshtypes/meshtype_3d_lung2.py index bc8f1fd3..3e3f49aa 100644 --- a/src/scaffoldmaker/meshtypes/meshtype_3d_lung2.py +++ b/src/scaffoldmaker/meshtypes/meshtype_3d_lung2.py @@ -65,14 +65,14 @@ def getDefaultOptions(cls, parameterSetName='Default'): 'Left lung depth': 1.0, 'Left lung height': 1.0, 'Left lung ventral edge sharpness factor': 0.0, - 'Left lung medial curvature factor': 0.0, - 'Left lung medial protrusion factor': 0.0, + 'Left lung medial curvature': 0.0, + 'Left lung base medial protrusion': 0.0, 'Right lung width': 0.5, 'Right lung depth': 1.0, 'Right lung height': 1.0, 'Right lung ventral edge sharpness factor': 0.0, - 'Right lung medial curvature factor': 0.0, - 'Right lung medial protrusion factor': 0.0, + 'Right lung medial curvature': 0.0, + 'Right lung base medial protrusion': 0.0, 'Open fissures': False, 'Accessory lobe': True, 'Accessory lobe medial curvature': 0.0, @@ -86,8 +86,8 @@ def getDefaultOptions(cls, parameterSetName='Default'): 'Diaphragm centre y': 0.0, 'Diaphragm curvature x': 0.0, 'Diaphragm curvature y': 0.0, - 'Left lung rotation in degree about z-axis': 0.0, - 'Right lung rotation in degree about z-axis': 0.0, + 'Left lung medial rotation in degree about z-axis': 0.0, + 'Right lung medial rotation in degree about z-axis': 0.0, 'Accessory lobe rotation in degree about z-axis': 0.0, 'Refine': False, 'Refine number of elements': 4, @@ -95,114 +95,114 @@ def getDefaultOptions(cls, parameterSetName='Default'): } materialOptions = copy.deepcopy(options) if 'Human 1' in parameterSetName: - options['Left-right lung spacing'] = 0.75 - options['Left-right apex medial shear displacement'] = 0.2 - options['Left-right apex ventral shear displacement'] = -0.2 + options['Left-right lung spacing'] = 0.9 + options['Left-right apex medial shear displacement'] = 0.4 + options['Left-right apex ventral shear displacement'] = -0.3 options['Left lung width'] = 0.8 options['Left lung depth'] = 1.0 options['Left lung height'] = 1.0 options['Left lung ventral edge sharpness factor'] = 0.8 - options['Left lung medial curvature factor'] = 1.0 - options['Left lung medial protrusion factor'] = 0.1 - options['Left lung rotation in degree about z-axis'] = -15.0 + options['Left lung medial curvature'] = 1.0 + options['Left lung base medial protrusion'] = 0.1 + options['Left lung medial rotation in degree about z-axis'] = 20.0 options['Right lung width'] = 0.8 options['Right lung depth'] = 1.0 options['Right lung height'] = 1.0 options['Right lung ventral edge sharpness factor'] = 0.8 - options['Right lung medial curvature factor'] = 1.0 - options['Right lung medial protrusion factor'] = 0.1 - options['Right lung rotation in degree about z-axis'] = 15.0 + options['Right lung medial curvature'] = 1.0 + options['Right lung base medial protrusion'] = 0.1 + options['Right lung medial rotation in degree about z-axis'] = 20.0 options['Accessory lobe'] = False options['Diaphragm curvature x'] = 1.0 options['Diaphragm curvature y'] = 1.0 elif 'Mouse 1' in parameterSetName: options['Number of left lung lobes'] = 1 - options['Left-right lung spacing'] = 0.9 + options['Left-right lung spacing'] = 0.95 options['Left-right apex medial shear displacement'] = 0.35 options['Left-right apex ventral shear displacement'] = -0.2 options['Left lung width'] = 0.5 options['Left lung depth'] = 1.0 options['Left lung height'] = 0.7 options['Left lung ventral edge sharpness factor'] = 0.5 - options['Left lung medial curvature factor'] = 1.0 - options['Left lung medial protrusion factor'] = 0.4 - options['Left lung rotation in degree about z-axis'] = -10.0 + options['Left lung medial curvature'] = 0.6 + options['Left lung base medial protrusion'] = 0.4 + options['Left lung medial rotation in degree about z-axis'] = 5.0 options['Right lung width'] = 0.8 options['Right lung depth'] = 1.2 options['Right lung height'] = 0.85 options['Right lung ventral edge sharpness factor'] = 0.8 - options['Right lung medial curvature factor'] = 0.8 - options['Right lung medial protrusion factor'] = 0.2 - options['Right lung rotation in degree about z-axis'] = 15.0 - options['Accessory lobe dorsal centre [x,y]'] = [0.0, 0.1] + options['Right lung medial curvature'] = 0.8 + options['Right lung base medial protrusion'] = 0.2 + options['Right lung medial rotation in degree about z-axis'] = 15.0 + options['Accessory lobe dorsal centre [x,y]'] = [-0.05, 0.125] options['Accessory lobe length'] = 0.6 options['Accessory lobe dorsal height'] = 0.5 options['Accessory lobe dorsal width'] = 0.5 options['Accessory lobe ventral height'] = 0.15 options['Accessory lobe ventral width'] = 0.2 - options['Accessory lobe medial curvature'] = 0.3 - options['Accessory lobe rotation in degree about z-axis'] = 0 + options['Accessory lobe medial curvature'] = -1.0 + options['Accessory lobe rotation in degree about z-axis'] = 40 options['Diaphragm centre y'] = 0.5 options['Diaphragm curvature x'] = 0.8 options['Diaphragm curvature y'] = 1.0 elif 'Rat 1' in parameterSetName: options['Number of left lung lobes'] = 1 options['Left-right lung spacing'] = 1.1 - options['Left-right apex medial shear displacement'] = 0.35 - options['Left-right apex ventral shear displacement'] = 0.0 + options['Left-right apex medial shear displacement'] = 0.4 + options['Left-right apex ventral shear displacement'] = 0.2 options['Left lung width'] = 0.5 options['Left lung depth'] = 1.4 options['Left lung height'] = 1.0 options['Left lung ventral edge sharpness factor'] = 0.6 - options['Left lung medial curvature factor'] = 0.8 - options['Left lung medial protrusion factor'] = 0.5 - options['Left lung rotation in degree about z-axis'] = 0.0 + options['Left lung medial curvature'] = 0.8 + options['Left lung base medial protrusion'] = 0.1 + options['Left lung medial rotation in degree about z-axis'] = -5.0 options['Right lung width'] = 0.5 options['Right lung depth'] = 1.8 options['Right lung height'] = 1.0 options['Right lung ventral edge sharpness factor'] = 0.0 - options['Right lung medial curvature factor'] = 0.8 - options['Right lung medial protrusion factor'] = 0.0 - options['Right lung rotation in degree about z-axis'] = 0.0 + options['Right lung medial curvature'] = 0.7 + options['Right lung base medial protrusion'] = 0.35 + options['Right lung medial rotation in degree about z-axis'] = 5.0 options['Accessory lobe'] = True - options['Accessory lobe dorsal centre [x,y]'] = [0.075, 0.1] - options['Accessory lobe length'] = 1.0 + options['Accessory lobe dorsal centre [x,y]'] = [-0.15, 0.25] + options['Accessory lobe length'] = 0.7 options['Accessory lobe dorsal height'] = 0.5 options['Accessory lobe dorsal width'] = 0.5 options['Accessory lobe ventral width'] = 0.2 options['Accessory lobe ventral height'] = 0.2 - options['Accessory lobe medial curvature'] = 0.5 - options['Accessory lobe rotation in degree about z-axis'] = 10.0 + options['Accessory lobe medial curvature'] = -0.7 + options['Accessory lobe rotation in degree about z-axis'] = 20.0 options['Diaphragm curvature x'] = 1.0 options['Diaphragm curvature y'] = 1.0 elif 'Pig 1' in parameterSetName: options['Number of left lung lobes'] = 1 options['Left-right lung spacing'] = 1.2 - options['Left-right apex medial shear displacement'] = 0.5 - options['Left-right apex ventral shear displacement'] = -0.4 + options['Left-right apex medial shear displacement'] = 0.4 + options['Left-right apex ventral shear displacement'] = 0.5 options['Left lung width'] = 0.8 options['Left lung depth'] = 1.6 options['Left lung height'] = 1.1 options['Left lung ventral edge sharpness factor'] = 0.7 - options['Left lung medial curvature factor'] = 0.7 - options['Left lung medial protrusion factor'] = 0.3 - options['Left lung rotation in degree about z-axis'] = -10.0 + options['Left lung medial curvature'] = 0.55 + options['Left lung base medial protrusion'] = 0.1 + options['Left lung medial rotation in degree about z-axis'] = 10.0 options['Right lung width'] = 0.8 options['Right lung depth'] = 1.5 options['Right lung height'] = 1.1 options['Right lung ventral edge sharpness factor'] = 0.7 - options['Right lung medial curvature factor'] = 0.7 - options['Right lung medial protrusion factor'] = 0.0 - options['Right lung rotation in degree about z-axis'] = 10.0 + options['Right lung medial curvature'] = 0.7 + options['Right lung base medial protrusion'] = 0.1 + options['Right lung medial rotation in degree about z-axis'] = 10.0 options['Accessory lobe'] = True - options['Accessory lobe dorsal centre [x,y]'] = [0.1, 0.15] - options['Accessory lobe length'] = 0.8 + options['Accessory lobe dorsal centre [x,y]'] = [-0.12, 0.15] + options['Accessory lobe length'] = 1.0 options['Accessory lobe dorsal height'] = 0.5 options['Accessory lobe dorsal width'] = 0.6 options['Accessory lobe ventral height'] = 0.1 options['Accessory lobe ventral width'] = 0.2 - options['Accessory lobe medial curvature'] = 0.7 - options['Accessory lobe rotation in degree about z-axis'] = 0.0 + options['Accessory lobe medial curvature'] = -0.9 + options['Accessory lobe rotation in degree about z-axis'] = 15.0 options['Diaphragm centre y'] = 0.1 options['Diaphragm curvature x'] = 1.0 options['Diaphragm curvature y'] = 1.0 @@ -222,16 +222,16 @@ def getOrderedOptionNames(): 'Left lung depth', 'Left lung height', 'Left lung ventral edge sharpness factor', - 'Left lung medial curvature factor', - 'Left lung medial protrusion factor', - 'Left lung rotation in degree about z-axis', + 'Left lung medial curvature', + 'Left lung base medial protrusion', + 'Left lung medial rotation in degree about z-axis', 'Right lung width', 'Right lung depth', 'Right lung height', 'Right lung ventral edge sharpness factor', - 'Right lung medial curvature factor', - 'Right lung medial protrusion factor', - 'Right lung rotation in degree about z-axis', + 'Right lung medial curvature', + 'Right lung base medial protrusion', + 'Right lung medial rotation in degree about z-axis', 'Open fissures', 'Accessory lobe', 'Accessory lobe dorsal centre [x,y]', @@ -302,14 +302,14 @@ def generateBaseMesh(cls, region, options): leftDepth = options['Left lung depth'] / 2.0 leftHeight = options['Left lung height'] leftEdgeSharpFactor = options['Left lung ventral edge sharpness factor'] - leftLungMedialCurvature = options['Left lung medial curvature factor'] * 2.0 - leftLungMedialProtrusion = options['Left lung medial protrusion factor'] + leftLungMedialCurvature = options['Left lung medial curvature'] * 2.0 + leftLungMedialProtrusion = options['Left lung base medial protrusion'] rightWidth = options['Right lung width'] / 2.0 rightDepth = options['Right lung depth'] / 2.0 rightHeight = options['Right lung height'] rightEdgeSharpFactor = options['Right lung ventral edge sharpness factor'] - rightLungMedialCurvature = options['Right lung medial curvature factor'] * 2.0 - rightLungMedialProtrusion = options['Right lung medial protrusion factor'] + rightLungMedialCurvature = options['Right lung medial curvature'] * 2.0 + rightLungMedialProtrusion = options['Right lung base medial protrusion'] isOpenfissure = options['Open fissures'] hasAccessoryLobe = options['Accessory lobe'] accessoryLobeDorsalCentre = options['Accessory lobe dorsal centre [x,y]'] @@ -323,8 +323,8 @@ def generateBaseMesh(cls, region, options): diaphragmCentreY = options['Diaphragm centre y'] diaphragmCurvatureX = options['Diaphragm curvature x'] diaphragmCurvatureY = options['Diaphragm curvature y'] - rotateLeftLung = options['Left lung rotation in degree about z-axis'] - rotateRightLung = options['Right lung rotation in degree about z-axis'] + rotateLeftLung = options['Left lung medial rotation in degree about z-axis'] + rotateRightLung = options['Right lung medial rotation in degree about z-axis'] rotateAccessoryLobe = options['Accessory lobe rotation in degree about z-axis'] nodes = fm.findNodesetByFieldDomainType(Field.DOMAIN_TYPE_NODES) @@ -419,6 +419,12 @@ def generateBaseMesh(cls, region, options): mediastinumLeftNodesetGroup = mediastinumLeftGroup.getNodesetGroup(nodes) mediastinumRightNodesetGroup = mediastinumRightGroup.getNodesetGroup(nodes) + # Arbitrary anatomical groups and nodeset for transformation + rightMedialLungGroup = findOrCreateAnnotationGroupForTerm(annotationGroups, region, ["medial right lung", "ILX: "]) + rightMedialLungNodesetGroup = rightMedialLungGroup.getNodesetGroup(nodes) + leftMedialLungGroup = findOrCreateAnnotationGroupForTerm(annotationGroups, region, ["medial left lung", "ILX: "]) + leftMedialLungNodesetGroup = leftMedialLungGroup.getNodesetGroup(nodes) + # Annotation fiducial point markerGroup = findOrCreateFieldGroup(fm, "marker") markerName = findOrCreateFieldStoredString(fm, name="marker_name") @@ -460,7 +466,8 @@ def generateBaseMesh(cls, region, options): nodeIdentifier = createLungNodes(spacingBetweenLeftRight, leftDepth, leftWidth, leftHeight, leftFissureAngle, leftObliqueProportion, leftLung, cache, coordinates, nodes, nodetemplate, - mediastinumLeftNodesetGroup, leftLungNodesetGroup, lungNodesetGroup, + mediastinumLeftNodesetGroup, leftMedialLungNodesetGroup, + leftLungNodesetGroup, lungNodesetGroup, lElementsCount1, lElementsCount2, lElementsCount3, uElementsCount1, uElementsCount2, uElementsCount3, lowerLeftNodeIds, upperLeftNodeIds, nodeIdentifier) @@ -469,7 +476,8 @@ def generateBaseMesh(cls, region, options): nodeIdentifier = createLungNodes(spacingBetweenLeftRight, rightDepth, rightWidth, rightHeight, rightFissureAngle, rightObliqueProportion, rightLung, cache, coordinates, nodes, nodetemplate, - mediastinumRightNodesetGroup, rightLungNodesetGroup, lungNodesetGroup, + mediastinumRightNodesetGroup, rightMedialLungNodesetGroup, + rightLungNodesetGroup, lungNodesetGroup, lElementsCount1, lElementsCount2, lElementsCount3, uElementsCount1, uElementsCount2, uElementsCount3, lowerRightNodeIds, upperRightNodeIds, nodeIdentifier) @@ -560,6 +568,8 @@ def generateBaseMesh(cls, region, options): #################################################################### for i in [leftLung, rightLung]: lungNodeset = leftLungNodesetGroup if i == 0 else rightLungNodesetGroup + medialLungNodeset = leftMedialLungNodesetGroup if i == 0 else rightMedialLungNodesetGroup + medialLungGroup = leftMedialLungGroup if i == 0 else rightMedialLungGroup edgeSharpFactor = leftEdgeSharpFactor if i == 0 else rightEdgeSharpFactor width = leftWidth if i == 0 else -rightWidth length = leftDepth if i == 0 else rightDepth @@ -567,12 +577,13 @@ def generateBaseMesh(cls, region, options): spacing = spacingBetweenLeftRight if i == 0 else -spacingBetweenLeftRight apexMedialDisplacement = lungsApexDisplacement if i == 0 else -lungsApexDisplacement lungMedialcurvature = -leftLungMedialCurvature if i == 0 else rightLungMedialCurvature - rotateLung = rotateLeftLung if i == 0 else rotateRightLung + rotateLung = -rotateLeftLung if i == 0 else rotateRightLung lungProtrusion = leftLungMedialProtrusion if i == 0 else rightLungMedialProtrusion # Transformation of the left and right lungs if lungProtrusion != 0.0: - medialProtrusion(lungProtrusion, fm, coordinates, lungNodeset, spacing, width, length, height) + medialProtrusion(lungProtrusion, fm, coordinates, medialLungNodeset, spacing, width, length, height) + annotationGroups.remove(medialLungGroup) if edgeSharpFactor != 0.0: sharpeningRidge(edgeSharpFactor, fm, coordinates, lungNodeset, spacing, length) @@ -696,9 +707,7 @@ def defineFaceAnnotations(cls, region, options, annotationGroups): is_exterior = fm.createFieldIsExterior() is_xi1_0 = fm.createFieldIsOnFace(Element.FACE_TYPE_XI1_0) is_xi1_1 = fm.createFieldIsOnFace(Element.FACE_TYPE_XI1_1) - is_xi2_1 = fm.createFieldIsOnFace(Element.FACE_TYPE_XI2_1) is_xi3_0 = fm.createFieldIsOnFace(Element.FACE_TYPE_XI3_0) - is_xi3_1 = fm.createFieldIsOnFace(Element.FACE_TYPE_XI3_1) # edge markers mediastanumTerms = ["anterior mediastinum of left lung", "anterior mediastinum of right lung"] @@ -776,7 +785,8 @@ def defineFaceAnnotations(cls, region, options, annotationGroups): def createLungNodes(spaceFromCentre, lengthUnit, widthUnit, heightUnit, fissureAngle, obliqueProportion, lungSide, cache, coordinates, nodes, nodetemplate, - mediastinumNodesetGroup, lungSideNodesetGroup, lungNodesetGroup, + mediastinumNodesetGroup, medialLungNodesetGroup, + lungSideNodesetGroup, lungNodesetGroup, lElementsCount1, lElementsCount2, lElementsCount3, uElementsCount1, uElementsCount2, uElementsCount3, lowerNodeIds, upperNodeIds, nodeIdentifier): @@ -1088,6 +1098,10 @@ def createLungNodes(spaceFromCentre, lengthUnit, widthUnit, heightUnit, fissureA nodeIdentifier += 1 # Annotation + if(lungSide == leftLung) and (n1 != 0): + medialLungNodesetGroup.addNode(node) + elif (lungSide != leftLung) and (n1 != lElementsCount1): + medialLungNodesetGroup.addNode(node) lungSideNodesetGroup.addNode(node) lungNodesetGroup.addNode(node) @@ -1343,6 +1357,10 @@ def createLungNodes(spaceFromCentre, lengthUnit, widthUnit, heightUnit, fissureA # Annotation if j > 2: mediastinumNodesetGroup.addNode(node) + if (lungSide == leftLung) and (k != 0): + medialLungNodesetGroup.addNode(node) + elif (lungSide != leftLung) and (k != uElementsCount1): + medialLungNodesetGroup.addNode(node) lungSideNodesetGroup.addNode(node) lungNodesetGroup.addNode(node) @@ -1987,6 +2005,7 @@ def bendingAroundZAxis(curvature, fm, coordinates, lungNodesetGroup, spaceFromCe # theta = y / bulgeRadius # z = z + rotateAround = 0 radius = 1/curvature scale = fm.createFieldConstant([1.0, curvature, 1.0]) scaleCoordinates = fm.createFieldMultiply(coordinates, scale) @@ -2018,7 +2037,7 @@ def sharpeningRidge(sharpeningFactor, fm, coordinates, lungNodesetGroup, spaceFr :param spaceFromCentre: :return: """ - # Transformation matrix = [ k1y + 1, | [x, + # Transformation matrix = [ -k1y + 1, | [x, # 1, | y, # 1] | z] offset = fm.createFieldConstant([spaceFromCentre, 0.75 * length, 0.0]) @@ -2072,7 +2091,7 @@ def rotateLungs(rotateZ, fm, coordinates, lungNodesetGroup, spaceFromCentre): # x2, x5, x8, # x3, x6, x9] if isinstance(spaceFromCentre, list): - offset = fm.createFieldConstant([spaceFromCentre[0], -spaceFromCentre[1], 0.0]) + offset = fm.createFieldConstant([-spaceFromCentre[0], -spaceFromCentre[1], 0.0]) else: offset = fm.createFieldConstant([spaceFromCentre, 0.0, 0.0]) origin = fm.createFieldAdd(coordinates, offset) @@ -2086,7 +2105,7 @@ def rotateLungs(rotateZ, fm, coordinates, lungNodesetGroup, spaceFromCentre): fieldassignment.setNodeset(lungNodesetGroup) fieldassignment.assign() -def medialProtrusion(protrusion_factor, fm, coordinates, lungNodesetGroup, spaceFromCentre, width, length, height): +def medialProtrusion(protrusion_factor, fm, coordinates, medialLungNodesetGroup, spaceFromCentre, width, length, height): """ :param tiltDegree: [tilted degree for apex, for diaphragm] :param fm: @@ -2097,47 +2116,45 @@ def medialProtrusion(protrusion_factor, fm, coordinates, lungNodesetGroup, space # FieldConstant - Matrix = [ x1, x4, x7, # x2, x5, x8, # x3, x6, x9] - offset = fm.createFieldConstant([spaceFromCentre + width, length * 0.3, 0.0]) + offset = fm.createFieldConstant([spaceFromCentre + width, length * 0.5, -height]) origin = fm.createFieldAdd(coordinates, offset) squaredOrigin = fm.createFieldMultiply(origin, origin) - peak = 2 - protrusion_factor # initial value is 1/(acceptable protrusion factor *0.67) 2.25 - rateOfChangeY = protrusion_factor*10 - rateOfChangeZ = protrusion_factor*10 - + peakY = 1 + peakZ = 1 + rateOfChangeY = 5 * protrusion_factor + rateOfChangeZ = protrusion_factor / abs(2*width) scale = fm.createFieldConstant([1.0, 0.0, 0.0]) - scale_1 = fm.createFieldConstant([peak, 1.0, 1.0]) + scale_y = fm.createFieldConstant([peakY, 1.0, 1.0]) + scale_z = fm.createFieldConstant([peakZ, 1.0, 1.0]) - constant = fm.createFieldConstant([0.0, rateOfChangeY, 0.0]) - scaleFunction = fm.createFieldMultiply(constant, squaredOrigin) # [0.0, k1y^2, 0.0] - squaredY = fm.createFieldComponent(scaleFunction, [2, 1, 1]) - squaredZ = fm.createFieldComponent(scaleFunction, [3, 1, 1]) - squaredYZ = fm.createFieldAdd(squaredY, squaredZ) # [k1y^2, 0.0, 0.0] - squaredYZOne = fm.createFieldAdd(squaredYZ, scale_1) # [k1y^2 + peak, 1.0, 1.0] + zConstant = fm.createFieldConstant([0.0, 0.0, 1.0]) + scaleZFunction = fm.createFieldMultiply(zConstant, origin) + Zcoor = fm.createFieldComponent(scaleZFunction, [3, 1, 1]) + absZcoor = fm.createFieldAbs(Zcoor) - recipFunction = fm.createFieldDivide(scale, squaredYZOne) # 1/[k1y^2 + peak], 0.0/1.0, 0.0/1.0] + xConstant = fm.createFieldConstant([1.0, 0.0, 0.0]) + Xcoor = fm.createFieldMultiply(xConstant, origin) + absXcoor = fm.createFieldAbs(Xcoor) - constant_1 = fm.createFieldConstant([0.5, 0.0, 1.0]) - yFunction = fm.createFieldAdd(recipFunction, constant_1) # 2.0 * |width|/[k1y^2 + peak], 1.0, 1.0] - xyFunction = fm.createFieldComponent(yFunction, [1, 3, 3]) # 2.0 * |width|/[k1y^2 + peak], 1.0, 1.0] + constant = fm.createFieldConstant([0.0, rateOfChangeY, 0.0]) + scaleFunction = fm.createFieldMultiply(constant, squaredOrigin) # [0.0, k1y^2, 0.0] + squaredY = fm.createFieldComponent(scaleFunction, [2, 1, 1]) # [k1y^2, 0.0, 0.0] + squaredY_Z = fm.createFieldMultiply(absZcoor, squaredY) # [(k1)(y^2)(|z|), 0.0, 0.0] + squaredY_XZ = fm.createFieldMultiply(absXcoor, squaredY_Z) # [(k1)(y^2)(|z|), 0.0, 0.0] + squaredYOne = fm.createFieldAdd(squaredY_XZ, scale_y) # [k1|x||z|y^2 + peak, 1.0, 1.0] + recipFunction = fm.createFieldDivide(scale, squaredYOne) # 1/[k1|x||z|y^2 + peak], 0.0/1.0, 0.0/1.0] + constant_1 = fm.createFieldConstant([0.0, 1.0, 1.0]) + yFunction = fm.createFieldAdd(recipFunction, constant_1) # 1/[k1y^2 + peak], 1.0, 1.0] constant = fm.createFieldConstant([0.0, 0.0, rateOfChangeZ]) scaleFunction = fm.createFieldMultiply(constant, squaredOrigin) # [0.0, 0.0, k2z^2] - squaredY = fm.createFieldComponent(scaleFunction, [2, 1, 1]) squaredZ = fm.createFieldComponent(scaleFunction, [3, 1, 1]) - squaredYZ = fm.createFieldAdd(squaredY, squaredZ) # [k2z^2, 0.0, 0.0] - squaredYZOne = fm.createFieldAdd(squaredYZ, scale_1) # [k2z^2 + peak, 1.0, 1.0] - - recipFunction = fm.createFieldDivide(scale, squaredYZOne) # 1/[k2z^2 + peak], 0/1.0, 0/1.0] - - constant_1 = fm.createFieldConstant([height/2, 0.0, 1.0]) - zFunction = fm.createFieldAdd(recipFunction, constant_1) # height/[k2z^2 + peak], 0.0, 1.0 - xzFunction = fm.createFieldComponent(zFunction, [1, 3, 3]) # height/[k2z^2 + peak], 1.0, 1.0 - - transformation_matrix = fm.createFieldMultiply(xyFunction, xzFunction) + squaredZOne = fm.createFieldAdd(squaredZ, scale_z) # [k2z^2 + peak, 1.0, 1.0] + transformation_matrix = fm.createFieldMultiply(yFunction, squaredZOne) taper_coordinates = fm.createFieldMultiply(origin, transformation_matrix) translate_coordinates = fm.createFieldSubtract(taper_coordinates, offset) fieldassignment = coordinates.createFieldassignment(translate_coordinates) - fieldassignment.setNodeset(lungNodesetGroup) + fieldassignment.setNodeset(medialLungNodesetGroup) fieldassignment.assign() From 00e298e2d43a3721fecadd806ae1ea94119ca21e Mon Sep 17 00:00:00 2001 From: arti-sukasem <51779010+arti-sukasem@users.noreply.github.com> Date: Tue, 24 May 2022 17:39:31 +1200 Subject: [PATCH 03/10] Fix parameter names and update test_lung for lung2 --- .../meshtypes/meshtype_3d_lung2.py | 84 +++++++++---------- tests/test_lung.py | 6 +- 2 files changed, 45 insertions(+), 45 deletions(-) diff --git a/src/scaffoldmaker/meshtypes/meshtype_3d_lung2.py b/src/scaffoldmaker/meshtypes/meshtype_3d_lung2.py index 3e3f49aa..476ec824 100644 --- a/src/scaffoldmaker/meshtypes/meshtype_3d_lung2.py +++ b/src/scaffoldmaker/meshtypes/meshtype_3d_lung2.py @@ -65,17 +65,17 @@ def getDefaultOptions(cls, parameterSetName='Default'): 'Left lung depth': 1.0, 'Left lung height': 1.0, 'Left lung ventral edge sharpness factor': 0.0, - 'Left lung medial curvature': 0.0, + 'Left lung dorsal-ventral medial curvature': 0.0, 'Left lung base medial protrusion': 0.0, 'Right lung width': 0.5, 'Right lung depth': 1.0, 'Right lung height': 1.0, 'Right lung ventral edge sharpness factor': 0.0, - 'Right lung medial curvature': 0.0, + 'Right lung dorsal-ventral medial curvature': 0.0, 'Right lung base medial protrusion': 0.0, 'Open fissures': False, 'Accessory lobe': True, - 'Accessory lobe medial curvature': 0.0, + 'Accessory lobe medial curvature about z-axis': 0.0, 'Accessory lobe length': 0.5, 'Accessory lobe dorsal centre [x,y]': [0.0, 0.25], 'Accessory lobe dorsal height': 0.5, @@ -86,9 +86,9 @@ def getDefaultOptions(cls, parameterSetName='Default'): 'Diaphragm centre y': 0.0, 'Diaphragm curvature x': 0.0, 'Diaphragm curvature y': 0.0, - 'Left lung medial rotation in degree about z-axis': 0.0, - 'Right lung medial rotation in degree about z-axis': 0.0, - 'Accessory lobe rotation in degree about z-axis': 0.0, + 'Left lung ventral-medial rotation degrees': 0.0, + 'Right lung ventral-medial rotation degrees': 0.0, + 'Accessory lobe ventral-left rotation degrees': 0.0, 'Refine': False, 'Refine number of elements': 4, 'Material Parameters': None @@ -102,16 +102,16 @@ def getDefaultOptions(cls, parameterSetName='Default'): options['Left lung depth'] = 1.0 options['Left lung height'] = 1.0 options['Left lung ventral edge sharpness factor'] = 0.8 - options['Left lung medial curvature'] = 1.0 + options['Left lung dorsal-ventral medial curvature'] = 1.0 options['Left lung base medial protrusion'] = 0.1 - options['Left lung medial rotation in degree about z-axis'] = 20.0 + options['Left lung ventral-medial rotation degrees'] = 20.0 options['Right lung width'] = 0.8 options['Right lung depth'] = 1.0 options['Right lung height'] = 1.0 options['Right lung ventral edge sharpness factor'] = 0.8 - options['Right lung medial curvature'] = 1.0 + options['Right lung dorsal-ventral medial curvature'] = 1.0 options['Right lung base medial protrusion'] = 0.1 - options['Right lung medial rotation in degree about z-axis'] = 20.0 + options['Right lung ventral-medial rotation degrees'] = 20.0 options['Accessory lobe'] = False options['Diaphragm curvature x'] = 1.0 options['Diaphragm curvature y'] = 1.0 @@ -124,24 +124,24 @@ def getDefaultOptions(cls, parameterSetName='Default'): options['Left lung depth'] = 1.0 options['Left lung height'] = 0.7 options['Left lung ventral edge sharpness factor'] = 0.5 - options['Left lung medial curvature'] = 0.6 + options['Left lung dorsal-ventral medial curvature'] = 0.6 options['Left lung base medial protrusion'] = 0.4 - options['Left lung medial rotation in degree about z-axis'] = 5.0 + options['Left lung ventral-medial rotation degrees'] = 5.0 options['Right lung width'] = 0.8 options['Right lung depth'] = 1.2 options['Right lung height'] = 0.85 options['Right lung ventral edge sharpness factor'] = 0.8 - options['Right lung medial curvature'] = 0.8 + options['Right lung dorsal-ventral medial curvature'] = 0.8 options['Right lung base medial protrusion'] = 0.2 - options['Right lung medial rotation in degree about z-axis'] = 15.0 - options['Accessory lobe dorsal centre [x,y]'] = [-0.05, 0.125] - options['Accessory lobe length'] = 0.6 + options['Right lung ventral-medial rotation degrees'] = 15.0 + options['Accessory lobe dorsal centre [x,y]'] = [-0.09, 0.13] + options['Accessory lobe length'] = 0.65 options['Accessory lobe dorsal height'] = 0.5 options['Accessory lobe dorsal width'] = 0.5 options['Accessory lobe ventral height'] = 0.15 options['Accessory lobe ventral width'] = 0.2 - options['Accessory lobe medial curvature'] = -1.0 - options['Accessory lobe rotation in degree about z-axis'] = 40 + options['Accessory lobe medial curvature about z-axis'] = -0.7 + options['Accessory lobe ventral-left rotation degrees'] = 30 options['Diaphragm centre y'] = 0.5 options['Diaphragm curvature x'] = 0.8 options['Diaphragm curvature y'] = 1.0 @@ -154,16 +154,16 @@ def getDefaultOptions(cls, parameterSetName='Default'): options['Left lung depth'] = 1.4 options['Left lung height'] = 1.0 options['Left lung ventral edge sharpness factor'] = 0.6 - options['Left lung medial curvature'] = 0.8 + options['Left lung dorsal-ventral medial curvature'] = 0.8 options['Left lung base medial protrusion'] = 0.1 - options['Left lung medial rotation in degree about z-axis'] = -5.0 + options['Left lung ventral-medial rotation degrees'] = -5.0 options['Right lung width'] = 0.5 options['Right lung depth'] = 1.8 options['Right lung height'] = 1.0 options['Right lung ventral edge sharpness factor'] = 0.0 - options['Right lung medial curvature'] = 0.7 + options['Right lung dorsal-ventral medial curvature'] = 0.7 options['Right lung base medial protrusion'] = 0.35 - options['Right lung medial rotation in degree about z-axis'] = 5.0 + options['Right lung ventral-medial rotation degrees'] = 5.0 options['Accessory lobe'] = True options['Accessory lobe dorsal centre [x,y]'] = [-0.15, 0.25] options['Accessory lobe length'] = 0.7 @@ -171,8 +171,8 @@ def getDefaultOptions(cls, parameterSetName='Default'): options['Accessory lobe dorsal width'] = 0.5 options['Accessory lobe ventral width'] = 0.2 options['Accessory lobe ventral height'] = 0.2 - options['Accessory lobe medial curvature'] = -0.7 - options['Accessory lobe rotation in degree about z-axis'] = 20.0 + options['Accessory lobe medial curvature about z-axis'] = -0.7 + options['Accessory lobe ventral-left rotation degrees'] = 20.0 options['Diaphragm curvature x'] = 1.0 options['Diaphragm curvature y'] = 1.0 elif 'Pig 1' in parameterSetName: @@ -184,16 +184,16 @@ def getDefaultOptions(cls, parameterSetName='Default'): options['Left lung depth'] = 1.6 options['Left lung height'] = 1.1 options['Left lung ventral edge sharpness factor'] = 0.7 - options['Left lung medial curvature'] = 0.55 + options['Left lung dorsal-ventral medial curvature'] = 0.55 options['Left lung base medial protrusion'] = 0.1 - options['Left lung medial rotation in degree about z-axis'] = 10.0 + options['Left lung ventral-medial rotation degrees'] = 10.0 options['Right lung width'] = 0.8 options['Right lung depth'] = 1.5 options['Right lung height'] = 1.1 options['Right lung ventral edge sharpness factor'] = 0.7 - options['Right lung medial curvature'] = 0.7 + options['Right lung dorsal-ventral medial curvature'] = 0.7 options['Right lung base medial protrusion'] = 0.1 - options['Right lung medial rotation in degree about z-axis'] = 10.0 + options['Right lung ventral-medial rotation degrees'] = 10.0 options['Accessory lobe'] = True options['Accessory lobe dorsal centre [x,y]'] = [-0.12, 0.15] options['Accessory lobe length'] = 1.0 @@ -201,8 +201,8 @@ def getDefaultOptions(cls, parameterSetName='Default'): options['Accessory lobe dorsal width'] = 0.6 options['Accessory lobe ventral height'] = 0.1 options['Accessory lobe ventral width'] = 0.2 - options['Accessory lobe medial curvature'] = -0.9 - options['Accessory lobe rotation in degree about z-axis'] = 15.0 + options['Accessory lobe medial curvature about z-axis'] = -0.9 + options['Accessory lobe ventral-left rotation degrees'] = 15.0 options['Diaphragm centre y'] = 0.1 options['Diaphragm curvature x'] = 1.0 options['Diaphragm curvature y'] = 1.0 @@ -222,16 +222,16 @@ def getOrderedOptionNames(): 'Left lung depth', 'Left lung height', 'Left lung ventral edge sharpness factor', - 'Left lung medial curvature', + 'Left lung dorsal-ventral medial curvature', 'Left lung base medial protrusion', - 'Left lung medial rotation in degree about z-axis', + 'Left lung ventral-medial rotation degrees', 'Right lung width', 'Right lung depth', 'Right lung height', 'Right lung ventral edge sharpness factor', - 'Right lung medial curvature', + 'Right lung dorsal-ventral medial curvature', 'Right lung base medial protrusion', - 'Right lung medial rotation in degree about z-axis', + 'Right lung ventral-medial rotation degrees', 'Open fissures', 'Accessory lobe', 'Accessory lobe dorsal centre [x,y]', @@ -240,8 +240,8 @@ def getOrderedOptionNames(): 'Accessory lobe dorsal width', 'Accessory lobe ventral height', 'Accessory lobe ventral width', - 'Accessory lobe medial curvature', - 'Accessory lobe rotation in degree about z-axis', + 'Accessory lobe medial curvature about z-axis', + 'Accessory lobe ventral-left rotation degrees', 'Diaphragm centre x', 'Diaphragm centre y', 'Diaphragm curvature x', @@ -302,13 +302,13 @@ def generateBaseMesh(cls, region, options): leftDepth = options['Left lung depth'] / 2.0 leftHeight = options['Left lung height'] leftEdgeSharpFactor = options['Left lung ventral edge sharpness factor'] - leftLungMedialCurvature = options['Left lung medial curvature'] * 2.0 + leftLungMedialCurvature = options['Left lung dorsal-ventral medial curvature'] * 2.0 leftLungMedialProtrusion = options['Left lung base medial protrusion'] rightWidth = options['Right lung width'] / 2.0 rightDepth = options['Right lung depth'] / 2.0 rightHeight = options['Right lung height'] rightEdgeSharpFactor = options['Right lung ventral edge sharpness factor'] - rightLungMedialCurvature = options['Right lung medial curvature'] * 2.0 + rightLungMedialCurvature = options['Right lung dorsal-ventral medial curvature'] * 2.0 rightLungMedialProtrusion = options['Right lung base medial protrusion'] isOpenfissure = options['Open fissures'] hasAccessoryLobe = options['Accessory lobe'] @@ -318,14 +318,14 @@ def generateBaseMesh(cls, region, options): accessoryLobeDorsalWidth = options['Accessory lobe dorsal width'] accessoryLobeVentralHeight = options['Accessory lobe ventral height'] accessoryLobeVentralWidth = options['Accessory lobe ventral width'] - accessoryLobeMedialCurve = options['Accessory lobe medial curvature'] * 2.0 + accessoryLobeMedialCurve = options['Accessory lobe medial curvature about z-axis'] * 2.0 diaphragmCentreX = options['Diaphragm centre x'] diaphragmCentreY = options['Diaphragm centre y'] diaphragmCurvatureX = options['Diaphragm curvature x'] diaphragmCurvatureY = options['Diaphragm curvature y'] - rotateLeftLung = options['Left lung medial rotation in degree about z-axis'] - rotateRightLung = options['Right lung medial rotation in degree about z-axis'] - rotateAccessoryLobe = options['Accessory lobe rotation in degree about z-axis'] + rotateLeftLung = options['Left lung ventral-medial rotation degrees'] + rotateRightLung = options['Right lung ventral-medial rotation degrees'] + rotateAccessoryLobe = options['Accessory lobe ventral-left rotation degrees'] nodes = fm.findNodesetByFieldDomainType(Field.DOMAIN_TYPE_NODES) nodetemplate = nodes.createNodetemplate() diff --git a/tests/test_lung.py b/tests/test_lung.py index 75f62ec3..a90a5dfe 100644 --- a/tests/test_lung.py +++ b/tests/test_lung.py @@ -212,8 +212,8 @@ def test_lung2(self): coordinates = fieldmodule.findFieldByName("coordinates").castFiniteElement() self.assertTrue(coordinates.isValid()) minimums, maximums = evaluateFieldNodesetRange(coordinates, nodes) - assertAlmostEqualList(self, minimums, [ -0.6380112948407249, -0.5136821588344925, -0.2594693243518892 ], 1.0E-6) - assertAlmostEqualList(self, maximums, [ 0.6380112948407249, 0.36430478702255786, 0.9368327915434912 ], 1.0E-6) + assertAlmostEqualList(self, minimums, [ -0.708377823030876, -0.590270294734182, -0.3219838508144919 ], 1.0E-6) + assertAlmostEqualList(self, maximums, [ 0.708377823030876, 0.3397959413350077, 0.9011820259396395 ], 1.0E-6) with ChangeManager(fieldmodule): one = fieldmodule.createFieldConstant(1.0) upperRightFissureGroup = fieldmodule.findFieldByName('upper lobe of right lung').castGroup() @@ -227,7 +227,7 @@ def test_lung2(self): fieldcache = fieldmodule.createFieldcache() result, surfaceArea = surfaceAreaField.evaluateReal(fieldcache, 1) self.assertEqual(result, RESULT_OK) - self.assertAlmostEqual(surfaceArea, 2.145407946129267, delta=1.0E-2) + self.assertAlmostEqual(surfaceArea, 2.1894323301334744, delta=1.0E-2) result, volume = volumeField.evaluateReal(fieldcache, 1) self.assertEqual(result, RESULT_OK) self.assertAlmostEqual(volume, 0.5026277967433523, delta=1.0E-2) From a7f9cf0a1f0dc0bca0387921af4cb990995ab040 Mon Sep 17 00:00:00 2001 From: arti-sukasem <51779010+arti-sukasem@users.noreply.github.com> Date: Thu, 9 Jun 2022 14:23:15 +1200 Subject: [PATCH 04/10] Add discontinuity to utils, test_lung, and lung terms --- src/scaffoldmaker/annotation/lung_terms.py | 26 + .../meshtypes/meshtype_3d_lung2.py | 659 +++++++++--------- src/scaffoldmaker/utils/zinc_utils.py | 76 ++ tests/test_lung.py | 641 ++++++++++++++++- 4 files changed, 1072 insertions(+), 330 deletions(-) diff --git a/src/scaffoldmaker/annotation/lung_terms.py b/src/scaffoldmaker/annotation/lung_terms.py index dced2b77..13ac6737 100644 --- a/src/scaffoldmaker/annotation/lung_terms.py +++ b/src/scaffoldmaker/annotation/lung_terms.py @@ -12,13 +12,27 @@ ("apex of left lung", "ILX:0778112"), ("apex of right lung", "ILX:0778113"), ("base of left lung surface", "ILX:0793187"), + ("base of lower lobe of left lung surface", "ILX:0793577"), + ("base of upper lobe of left lung surface", "ILX:0793578"), ("base of right lung accessory lobe surface", "ILX:0793189"), ("base of right lung surface", "ILX:0793188"), + ("base of lower lobe of right lung surface", "ILX:0793579"), + ("base of middle lobe of right lung surface", "ILX:0793580"), ("dorsal base of right lung accessory lobe", "ILX:0778125"), ("dorsal base of left lung", "ILX:0778126"), ("dorsal base of right lung", "ILX:0778127"), ("horizontal fissure of right lung", "ILX:0746327"), + ("horizontal fissure of lower lobe of right lung", "ILX:0793581"), + ("horizontal fissure of middle lobe of right lung", "ILX:0793582"), + ("horizontal fissure of upper lobe of right lung", "ILX:0793583"), ("laterodorsal tip of middle lobe of right lung", "ILX:0778124"), + ("lateral surface of left lung", "ILX:0793601"), + ("lateral surface of right lung", "ILX:0793602"), + ("lateral surface of lower lobe of left lung", "ILX:0793584"), + ("lateral surface of lower lobe of right lung", "ILX:0793585"), + ("lateral surface of middle lobe of right lung", "ILX:0793586"), + ("lateral surface of upper lobe of left lung", "ILX:0793587"), + ("lateral surface of upper lobe of right lung", "ILX:0793588"), ("left lung", "UBERON:0002168", "ILX:0733450"), ("left lung surface", "ILX:0793186"), ("lower lobe of left lung", "UBERON:0008953", "ILX:0735534"), @@ -26,6 +40,13 @@ ("lower lobe of right lung", "UBERON:0002171", "ILX:0725712"), ("lower lobe of right lung surface", "ILX:0793191"), ("lung", "UBERON:0002048", "ILX:0726937"), + ("medial surface of left lung", "ILX:0793599"), + ("medial surface of right lung", "ILX:0793600"), + ("medial surface of lower lobe of left lung", "ILX:0793589"), + ("medial surface of lower lobe of right lung", "ILX:0793590"), + ("medial surface of middle lobe of right lung", "ILX:0793591"), + ("medial surface of upper lobe of left lung", "ILX:0793592"), + ("medial surface of upper lobe of right lung", "ILX:0793593"), ("middle lobe of right lung", "UBERON:0002174", "ILX:0733737"), ("middle lobe of right lung surface", "ILX:0793193"), ("medial base of left lung", "ILX:0778120"), @@ -33,7 +54,12 @@ ("anterior mediastinum of left lung", "UBERON:0008820", "ILX:0725455"), ("anterior mediastinum of right lung", "UBERON:0008820", "ILX:0725455"), ("oblique fissure of left lung", "ILX:0778115"), + ("oblique fissure of lower lobe of left lung", "ILX:0793594"), + ("oblique fissure of upper lobe of left lung", "ILX:0793595"), ("oblique fissure of right lung", "ILX:0778114"), + ("oblique fissure of lower lobe of right lung", "ILX:0793596"), + ("oblique fissure of middle lobe of right lung", "ILX:0793597"), + ("oblique fissure of upper lobe of right lung", "ILX:0793598"), ("right lung", "UBERON:0002167", "ILX:0729582"), ("right lung surface", "ILX:0793185"), ("right lung accessory lobe", "UBERON:0004890", "ILX:0728696"), diff --git a/src/scaffoldmaker/meshtypes/meshtype_3d_lung2.py b/src/scaffoldmaker/meshtypes/meshtype_3d_lung2.py index 476ec824..00b6c485 100644 --- a/src/scaffoldmaker/meshtypes/meshtype_3d_lung2.py +++ b/src/scaffoldmaker/meshtypes/meshtype_3d_lung2.py @@ -19,6 +19,7 @@ from scaffoldmaker.utils.derivativemoothing import DerivativeSmoothing from scaffoldmaker.utils.meshrefinement import MeshRefinement from scaffoldmaker.utils.vector import magnitude, setMagnitude, crossproduct3, normalise +from scaffoldmaker.utils.zinc_utils import disconnectFieldMeshGroupBoundaryNodes from opencmiss.utils.zinc.field import Field, findOrCreateFieldCoordinates, findOrCreateFieldGroup, \ findOrCreateFieldNodeGroup, findOrCreateFieldStoredMeshLocation, findOrCreateFieldStoredString, createFieldEulerAnglesRotationMatrix from opencmiss.utils.zinc.finiteelement import get_element_node_identifiers @@ -281,11 +282,13 @@ def generateBaseMesh(cls, region, options): # point reference fm = fm_0 coordinates = coordinates_0 + region_temp = region else: region_1 = region.createRegion() fm_1 = region_1.getFieldmodule() coordinates_1 = findOrCreateFieldCoordinates(fm_1, name="lung coordinates") # point reference + region = region_1 fm = fm_1 coordinates = coordinates_1 # update material coordinates according to geometric parameters @@ -350,37 +353,70 @@ def generateBaseMesh(cls, region, options): #################### # Annotation groups #################### - lungGroup = AnnotationGroup(region, get_lung_term("lung")) - leftLungGroup = AnnotationGroup(region, get_lung_term("left lung")) - annotationGroups = [leftLungGroup, lungGroup] - lungMeshGroup = lungGroup.getMeshGroup(mesh) - leftLungMeshGroup = leftLungGroup.getMeshGroup(mesh) - rightLungGroup = AnnotationGroup(region, get_lung_term("right lung")) - rightLungMeshGroup = rightLungGroup.getMeshGroup(mesh) - annotationGroups.append(rightLungGroup) - lowerRightLungGroup = AnnotationGroup(region, get_lung_term("lower lobe of right lung")) - lowerRightLungMeshGroup = lowerRightLungGroup.getMeshGroup(mesh) - annotationGroups.append(lowerRightLungGroup) - upperRightLungGroup = AnnotationGroup(region, get_lung_term("upper lobe of right lung")) - upperRightLungMeshGroup = upperRightLungGroup.getMeshGroup(mesh) - annotationGroups.append(upperRightLungGroup) - middleRightLungGroup = AnnotationGroup(region, get_lung_term("middle lobe of right lung")) - middleRightLungMeshGroup = middleRightLungGroup.getMeshGroup(mesh) - annotationGroups.append(middleRightLungGroup) - mediastinumLeftGroup = AnnotationGroup(region, get_lung_term("anterior mediastinum of left lung")) - mediastinumLeftGroupMeshGroup = mediastinumLeftGroup.getMeshGroup(mesh) - annotationGroups.append(mediastinumLeftGroup) - mediastinumRightGroup = AnnotationGroup(region, get_lung_term("anterior mediastinum of right lung")) - mediastinumRightGroupMeshGroup = mediastinumRightGroup.getMeshGroup(mesh) - annotationGroups.append(mediastinumRightGroup) - - if numberOfLeftLung == 2: - lowerLeftLungGroup = AnnotationGroup(region, get_lung_term("lower lobe of left lung")) - lowerLeftLungMeshGroup = lowerLeftLungGroup.getMeshGroup(mesh) - annotationGroups.append(lowerLeftLungGroup) - upperLeftLungGroup = AnnotationGroup(region, get_lung_term("upper lobe of left lung")) - upperLeftLungMeshGroup = upperLeftLungGroup.getMeshGroup(mesh) - annotationGroups.append(upperLeftLungGroup) + if coordinate == 0: + lungGroup = AnnotationGroup(region, get_lung_term("lung")) + leftLungGroup = AnnotationGroup(region, get_lung_term("left lung")) + annotationGroups = [leftLungGroup, lungGroup] + lungMeshGroup = lungGroup.getMeshGroup(mesh) + leftLungMeshGroup = leftLungGroup.getMeshGroup(mesh) + rightLungGroup = AnnotationGroup(region, get_lung_term("right lung")) + rightLungMeshGroup = rightLungGroup.getMeshGroup(mesh) + annotationGroups.append(rightLungGroup) + lowerRightLungGroup = AnnotationGroup(region, get_lung_term("lower lobe of right lung")) + lowerRightLungMeshGroup = lowerRightLungGroup.getMeshGroup(mesh) + annotationGroups.append(lowerRightLungGroup) + upperRightLungGroup = AnnotationGroup(region, get_lung_term("upper lobe of right lung")) + upperRightLungMeshGroup = upperRightLungGroup.getMeshGroup(mesh) + annotationGroups.append(upperRightLungGroup) + middleRightLungGroup = AnnotationGroup(region, get_lung_term("middle lobe of right lung")) + middleRightLungMeshGroup = middleRightLungGroup.getMeshGroup(mesh) + annotationGroups.append(middleRightLungGroup) + mediastinumLeftGroup = AnnotationGroup(region, get_lung_term("anterior mediastinum of left lung")) + mediastinumLeftGroupMeshGroup = mediastinumLeftGroup.getMeshGroup(mesh) + annotationGroups.append(mediastinumLeftGroup) + mediastinumRightGroup = AnnotationGroup(region, get_lung_term("anterior mediastinum of right lung")) + mediastinumRightGroupMeshGroup = mediastinumRightGroup.getMeshGroup(mesh) + annotationGroups.append(mediastinumRightGroup) + + if numberOfLeftLung == 2: + lowerLeftLungGroup = AnnotationGroup(region, get_lung_term("lower lobe of left lung")) + lowerLeftLungMeshGroup = lowerLeftLungGroup.getMeshGroup(mesh) + annotationGroups.append(lowerLeftLungGroup) + upperLeftLungGroup = AnnotationGroup(region, get_lung_term("upper lobe of left lung")) + upperLeftLungMeshGroup = upperLeftLungGroup.getMeshGroup(mesh) + annotationGroups.append(upperLeftLungGroup) + + if hasAccessoryLobe: + # Annotation groups + rightLungAccessoryLobeGroup = AnnotationGroup(region, get_lung_term("right lung accessory lobe")) + rightLungAccessoryLobeMeshGroup = rightLungAccessoryLobeGroup.getMeshGroup(mesh) + annotationGroups.append(rightLungAccessoryLobeGroup) + rightLungAccessoryLobeNodesetGroup = rightLungAccessoryLobeGroup.getNodesetGroup(nodes) + # Marker points + accessoryApexGroup = findOrCreateAnnotationGroupForTerm(annotationGroups, region, + get_lung_term("apex of right lung accessory lobe")) + accessoryVentralGroup = findOrCreateAnnotationGroupForTerm(annotationGroups, region, + get_lung_term("ventral base of right lung accessory lobe")) + accessoryDorsalGroup = findOrCreateAnnotationGroupForTerm(annotationGroups, region, + get_lung_term("dorsal base of right lung accessory lobe")) + + # Nodeset group + leftLungNodesetGroup = leftLungGroup.getNodesetGroup(nodes) + rightLungNodesetGroup = rightLungGroup.getNodesetGroup(nodes) + lungNodesetGroup = lungGroup.getNodesetGroup(nodes) + mediastinumLeftNodesetGroup = mediastinumLeftGroup.getNodesetGroup(nodes) + mediastinumRightNodesetGroup = mediastinumRightGroup.getNodesetGroup(nodes) + + # Arbitrary anatomical groups and nodesets for transformation + upperLeftDorsalLungGroup = findOrCreateAnnotationGroupForTerm(annotationGroups, region, ["upper lobe of left lung dorsal", "None"]) + upperLeftDorsalLungMeshGroup = upperLeftDorsalLungGroup.getMeshGroup(mesh) + upperRightDorsalLungGroup = findOrCreateAnnotationGroupForTerm(annotationGroups, region, ["upper lobe of right lung dorsal", "None"]) + upperRightDorsalLungMeshGroup = upperRightDorsalLungGroup.getMeshGroup(mesh) + rightMedialLungGroup = findOrCreateAnnotationGroupForTerm(annotationGroups, region, ["medial right lung", "None"]) + leftMedialLungGroup = findOrCreateAnnotationGroupForTerm(annotationGroups, region, ["medial left lung", "None"]) + + rightMedialLungNodesetGroup = rightMedialLungGroup.getNodesetGroup(nodes) + leftMedialLungNodesetGroup = leftMedialLungGroup.getNodesetGroup(nodes) # Marker points/groups leftApexGroup = findOrCreateAnnotationGroupForTerm(annotationGroups, region, @@ -392,39 +428,13 @@ def generateBaseMesh(cls, region, options): rightVentralGroup = findOrCreateAnnotationGroupForTerm(annotationGroups, region, get_lung_term("ventral base of right lung")) rightLateralGroup = findOrCreateAnnotationGroupForTerm(annotationGroups, region, - get_lung_term("laterodorsal tip of middle lobe of right lung")) + get_lung_term( + "laterodorsal tip of middle lobe of right lung")) leftMedialGroup = findOrCreateAnnotationGroupForTerm(annotationGroups, region, get_lung_term("medial base of left lung")) rightMedialGroup = findOrCreateAnnotationGroupForTerm(annotationGroups, region, get_lung_term("medial base of right lung")) - if hasAccessoryLobe: - # Annotation groups - rightLungAccessoryLobeGroup = AnnotationGroup(region, get_lung_term("right lung accessory lobe")) - rightLungAccessoryLobeMeshGroup = rightLungAccessoryLobeGroup.getMeshGroup(mesh) - annotationGroups.append(rightLungAccessoryLobeGroup) - rightLungAccessoryLobeNodesetGroup = rightLungAccessoryLobeGroup.getNodesetGroup(nodes) - # Marker points - accessoryApexGroup = findOrCreateAnnotationGroupForTerm(annotationGroups, region, - get_lung_term("apex of right lung accessory lobe")) - accessoryVentralGroup = findOrCreateAnnotationGroupForTerm(annotationGroups, region, - get_lung_term("ventral base of right lung accessory lobe")) - accessoryDorsalGroup = findOrCreateAnnotationGroupForTerm(annotationGroups, region, - get_lung_term("dorsal base of right lung accessory lobe")) - - # Nodeset group - leftLungNodesetGroup = leftLungGroup.getNodesetGroup(nodes) - rightLungNodesetGroup = rightLungGroup.getNodesetGroup(nodes) - lungNodesetGroup = lungGroup.getNodesetGroup(nodes) - mediastinumLeftNodesetGroup = mediastinumLeftGroup.getNodesetGroup(nodes) - mediastinumRightNodesetGroup = mediastinumRightGroup.getNodesetGroup(nodes) - - # Arbitrary anatomical groups and nodeset for transformation - rightMedialLungGroup = findOrCreateAnnotationGroupForTerm(annotationGroups, region, ["medial right lung", "ILX: "]) - rightMedialLungNodesetGroup = rightMedialLungGroup.getNodesetGroup(nodes) - leftMedialLungGroup = findOrCreateAnnotationGroupForTerm(annotationGroups, region, ["medial left lung", "ILX: "]) - leftMedialLungNodesetGroup = leftMedialLungGroup.getNodesetGroup(nodes) - # Annotation fiducial point markerGroup = findOrCreateFieldGroup(fm, "marker") markerName = findOrCreateFieldStoredString(fm, name="marker_name") @@ -482,6 +492,7 @@ def generateBaseMesh(cls, region, options): uElementsCount1, uElementsCount2, uElementsCount3, lowerRightNodeIds, upperRightNodeIds, nodeIdentifier) + if hasAccessoryLobe: # The number of the elements in the accessory lobe right lung accesssoryLobeElementsCount1 = 2 @@ -500,13 +511,14 @@ def generateBaseMesh(cls, region, options): # Create elements ################## elementIdentifier = 1 + if numberOfLeftLung == 2: # Left lung elements elementIdentifier, leftUpperLobeElementID, leftLowerLobeElementID = \ createLungElements(coordinates, eftfactory, eftRegular, elementtemplateRegular, elementtemplateCustom, mesh, lungMeshGroup, leftLungMeshGroup, lowerLeftLungMeshGroup, None, - upperLeftLungMeshGroup, mediastinumLeftGroupMeshGroup, + upperLeftLungMeshGroup, mediastinumLeftGroupMeshGroup, upperLeftDorsalLungMeshGroup, lElementsCount1, lElementsCount2, lElementsCount3, uElementsCount1, uElementsCount2, uElementsCount3, lowerLeftNodeIds, upperLeftNodeIds, elementIdentifier) @@ -514,7 +526,8 @@ def generateBaseMesh(cls, region, options): elementIdentifier, leftUpperLobeElementID, leftLowerLobeElementID = \ createLungElements(coordinates, eftfactory, eftRegular, elementtemplateRegular, elementtemplateCustom, mesh, lungMeshGroup, - leftLungMeshGroup, None, None, None, mediastinumLeftGroupMeshGroup, + leftLungMeshGroup, None, None, None, + mediastinumLeftGroupMeshGroup, None, lElementsCount1, lElementsCount2, lElementsCount3, uElementsCount1, uElementsCount2, uElementsCount3, lowerLeftNodeIds, upperLeftNodeIds, elementIdentifier) @@ -524,17 +537,11 @@ def generateBaseMesh(cls, region, options): createLungElements(coordinates, eftfactory, eftRegular, elementtemplateRegular, elementtemplateCustom, mesh, lungMeshGroup, rightLungMeshGroup, lowerRightLungMeshGroup, middleRightLungMeshGroup, - upperRightLungMeshGroup, mediastinumRightGroupMeshGroup, + upperRightLungMeshGroup, mediastinumRightGroupMeshGroup, upperRightDorsalLungMeshGroup, lElementsCount1, lElementsCount2, lElementsCount3, uElementsCount1, uElementsCount2, uElementsCount3, lowerRightNodeIds, upperRightNodeIds, elementIdentifier) - if isOpenfissure: - leftUpperLobeElementID_temp = None if numberOfLeftLung == 1 else leftUpperLobeElementID - # create discontinuity/seperation along the fissures by making lower and upper lobes independently - nodeIdentifier = createDiscontinuity(coordinates, nodes, mesh, cache, nodetemplate, leftUpperLobeElementID_temp, - rightUpperLobeElementID, nodeIdentifier) - if hasAccessoryLobe: # Accessory lobe right lung elements createAccessorylobeLungElements(coordinates, eftfactory, eftRegular, elementtemplateRegular, @@ -544,75 +551,73 @@ def generateBaseMesh(cls, region, options): accesssoryLobeElementsCount3, accessoryLobeNodeIds, elementIdentifier) - ######################## + ########################### # Combining two coordinates - ######################## + ########################### if coordinate == 1: - sir = region_1.createStreaminformationRegion() - srm = sir.createStreamresourceMemory() - region_1.write(sir) - result, buffer = srm.getBuffer() + sir_1 = region_1.createStreaminformationRegion() + srm_1 = sir_1.createStreamresourceMemory() + region_1.write(sir_1) + result, buffer = srm_1.getBuffer() + region = region_temp sir = region.createStreaminformationRegion() srm = sir.createStreamresourceMemoryBuffer(buffer) region.read(sir) - del srm - del sir - del fm_1 - del coordinates_1 - del region_1 + lung_coordinates = fm_0.findFieldByName("lung coordinates").castFiniteElement() #################################################################### # Transformation and translation to the left (0) and right lungs (1) #################################################################### - for i in [leftLung, rightLung]: - lungNodeset = leftLungNodesetGroup if i == 0 else rightLungNodesetGroup - medialLungNodeset = leftMedialLungNodesetGroup if i == 0 else rightMedialLungNodesetGroup - medialLungGroup = leftMedialLungGroup if i == 0 else rightMedialLungGroup - edgeSharpFactor = leftEdgeSharpFactor if i == 0 else rightEdgeSharpFactor - width = leftWidth if i == 0 else -rightWidth - length = leftDepth if i == 0 else rightDepth - height = leftHeight if i == 0 else rightHeight - spacing = spacingBetweenLeftRight if i == 0 else -spacingBetweenLeftRight - apexMedialDisplacement = lungsApexDisplacement if i == 0 else -lungsApexDisplacement - lungMedialcurvature = -leftLungMedialCurvature if i == 0 else rightLungMedialCurvature - rotateLung = -rotateLeftLung if i == 0 else rotateRightLung - lungProtrusion = leftLungMedialProtrusion if i == 0 else rightLungMedialProtrusion - - # Transformation of the left and right lungs - if lungProtrusion != 0.0: - medialProtrusion(lungProtrusion, fm, coordinates, medialLungNodeset, spacing, width, length, height) - annotationGroups.remove(medialLungGroup) - - if edgeSharpFactor != 0.0: - sharpeningRidge(edgeSharpFactor, fm, coordinates, lungNodeset, spacing, length) - - if lungMedialcurvature != 0.0: - bendingAroundZAxis(lungMedialcurvature, fm, coordinates, lungNodeset, spacing) - - if apexMedialDisplacement != 0.0: - medialShearRadian = math.atan(apexMedialDisplacement/height) - tiltLungs(medialShearRadian, 0, 0, 0, fm, coordinates, lungNodeset) - - if forwardLeftRightApex != 0.0: - ventralShearRadian = math.atan(forwardLeftRightApex / height) - tiltLungs(0, ventralShearRadian, 0, 0, fm, coordinates, lungNodeset) - - if rotateLung != 0.0: - rotateLungs(rotateLung, fm, coordinates, lungNodeset, spacing) - - if (accessoryLobeMedialCurve != 0.0) and hasAccessoryLobe: - spacing = accessoryLobeDorsalCentre - bendingAroundZAxis(accessoryLobeMedialCurve, fm, coordinates, rightLungAccessoryLobeNodesetGroup, spacing) - - if (rotateAccessoryLobe != 0.0) and hasAccessoryLobe: - spacing = accessoryLobeDorsalCentre - rotateLungs(rotateAccessoryLobe, fm, coordinates, rightLungAccessoryLobeNodesetGroup, spacing) - - if (diaphragmCurvatureX != 0.0) or (diaphragmCurvatureY != 0.0): - concavingDiaphragmaticSurface(diaphragmCurvatureX, diaphragmCurvatureY, fm, coordinates, diaphragmCentreX, - diaphragmCentreY, lungNodesetGroup) + if coordinate == 0: + for i in [leftLung, rightLung]: + lungNodeset = leftLungNodesetGroup if i == 0 else rightLungNodesetGroup + medialLungNodeset = leftMedialLungNodesetGroup if i == 0 else rightMedialLungNodesetGroup + medialLungGroup = leftMedialLungGroup if i == 0 else rightMedialLungGroup + edgeSharpFactor = leftEdgeSharpFactor if i == 0 else rightEdgeSharpFactor + width = leftWidth if i == 0 else -rightWidth + length = leftDepth if i == 0 else rightDepth + height = leftHeight if i == 0 else rightHeight + spacing = spacingBetweenLeftRight if i == 0 else -spacingBetweenLeftRight + apexMedialDisplacement = lungsApexDisplacement if i == 0 else -lungsApexDisplacement + lungMedialcurvature = -leftLungMedialCurvature if i == 0 else rightLungMedialCurvature + rotateLung = -rotateLeftLung if i == 0 else rotateRightLung + lungProtrusion = leftLungMedialProtrusion if i == 0 else rightLungMedialProtrusion + + # Transformation of the left and right lungs + if lungProtrusion != 0.0: + medialProtrusion(lungProtrusion, fm, coordinates, medialLungNodeset, spacing, width, length, height) + annotationGroups.remove(medialLungGroup) + + if edgeSharpFactor != 0.0: + sharpeningRidge(edgeSharpFactor, fm, coordinates, lungNodeset, spacing, length) + + if lungMedialcurvature != 0.0: + bendingAroundZAxis(lungMedialcurvature, fm, coordinates, lungNodeset, spacing) + + if apexMedialDisplacement != 0.0: + medialShearRadian = math.atan(apexMedialDisplacement/height) + tiltLungs(medialShearRadian, 0, 0, 0, fm, coordinates, lungNodeset) + + if forwardLeftRightApex != 0.0: + ventralShearRadian = math.atan(forwardLeftRightApex / height) + tiltLungs(0, ventralShearRadian, 0, 0, fm, coordinates, lungNodeset) + + if rotateLung != 0.0: + rotateLungs(rotateLung, fm, coordinates, lungNodeset, spacing) + + if (accessoryLobeMedialCurve != 0.0) and hasAccessoryLobe: + spacing = accessoryLobeDorsalCentre + bendingAroundZAxis(accessoryLobeMedialCurve, fm, coordinates, rightLungAccessoryLobeNodesetGroup, spacing) + + if (rotateAccessoryLobe != 0.0) and hasAccessoryLobe: + spacing = accessoryLobeDorsalCentre + rotateLungs(rotateAccessoryLobe, fm, coordinates, rightLungAccessoryLobeNodesetGroup, spacing) + + if (diaphragmCurvatureX != 0.0) or (diaphragmCurvatureY != 0.0): + concavingDiaphragmaticSurface(diaphragmCurvatureX, diaphragmCurvatureY, fm, coordinates, diaphragmCentreX, + diaphragmCentreY, lungNodesetGroup) ############### # Marker points @@ -672,6 +677,17 @@ def generateBaseMesh(cls, region, options): lungNodesetGroup.addNode(markerPoint) nodeIdentifier += 1 + if isOpenfissure: + if numberOfLeftLung > 1: + nodeIdentifier, copyIdentifiersLLU = disconnectFieldMeshGroupBoundaryNodes( + [coordinates_0, lung_coordinates], lowerLeftLungMeshGroup, upperLeftLungMeshGroup, nodeIdentifier) + nodeIdentifier, copyIdentifiersRLM = disconnectFieldMeshGroupBoundaryNodes( + [coordinates_0, lung_coordinates], lowerRightLungMeshGroup, middleRightLungMeshGroup, nodeIdentifier) + nodeIdentifier, copyIdentifiersRLU = disconnectFieldMeshGroupBoundaryNodes( + [coordinates_0, lung_coordinates], lowerRightLungMeshGroup, upperRightLungMeshGroup, nodeIdentifier) + nodeIdentifier, copyIdentifiersRMU = disconnectFieldMeshGroupBoundaryNodes( + [coordinates_0, lung_coordinates], middleRightLungMeshGroup, upperRightLungMeshGroup, nodeIdentifier) + return annotationGroups @classmethod @@ -697,6 +713,7 @@ def defineFaceAnnotations(cls, region, options, annotationGroups): """ numberOfLeftLung = options['Number of left lung lobes'] hasAccessoryLobe = options['Accessory lobe'] + openFissures = options['Open fissures'] # create fissure groups fm = region.getFieldmodule() @@ -707,9 +724,12 @@ def defineFaceAnnotations(cls, region, options, annotationGroups): is_exterior = fm.createFieldIsExterior() is_xi1_0 = fm.createFieldIsOnFace(Element.FACE_TYPE_XI1_0) is_xi1_1 = fm.createFieldIsOnFace(Element.FACE_TYPE_XI1_1) + is_xi2_0 = fm.createFieldIsOnFace(Element.FACE_TYPE_XI2_0) + is_xi2_1 = fm.createFieldIsOnFace(Element.FACE_TYPE_XI2_1) is_xi3_0 = fm.createFieldIsOnFace(Element.FACE_TYPE_XI3_0) + is_xi3_1 = fm.createFieldIsOnFace(Element.FACE_TYPE_XI3_1) - # edge markers + # 1D edge markers mediastanumTerms = ["anterior mediastinum of left lung", "anterior mediastinum of right lung"] for mediastanumTerm in mediastanumTerms: mediastanumGroup = findOrCreateAnnotationGroupForTerm(annotationGroups, region, get_lung_term(mediastanumTerm)) @@ -717,31 +737,42 @@ def defineFaceAnnotations(cls, region, options, annotationGroups): is_mediastanumGroup_exterior = fm.createFieldAnd(is_anteriorBorderGroup, is_exterior) is_mediastanumGroup_exterior_xi1_0 = fm.createFieldAnd(is_mediastanumGroup_exterior, is_xi1_0) is_mediastanumGroup_exterior_xi1_01 = fm.createFieldAnd(is_mediastanumGroup_exterior_xi1_0, is_xi1_1) - is_mediastanumGroup_exterior_xi3_0 = fm.createFieldAnd(is_mediastanumGroup_exterior_xi1_01, is_xi3_0) - - is_upperLeftGroup_Diaphragm = fm.createFieldAnd(is_mediastanumGroup_exterior_xi3_0, is_mediastanumGroup_exterior_xi1_01) - - is_ridge = fm.createFieldAnd(is_mediastanumGroup_exterior_xi1_01, fm.createFieldNot(is_upperLeftGroup_Diaphragm)) + is_ridge = fm.createFieldAnd(is_mediastanumGroup_exterior_xi1_01, fm.createFieldNot(is_xi3_0)) + if openFissures: + is_ridge = fm.createFieldAnd(is_ridge, fm.createFieldNot(is_xi3_1)) borderTerm = "anterior border of left lung" if "left lung" in mediastanumTerm else "anterior border of right lung" anteriorBorderLeftGroup = findOrCreateAnnotationGroupForTerm(annotationGroups, region, get_lung_term(borderTerm)) anteriorBorderLeftGroup.getMeshGroup(mesh1d).addElementsConditional(is_ridge) annotationGroups.remove(mediastanumGroup) - # exterior surfaces + # Arbitrary terms - are removed from the annotation groups later + arbLobe_group = {} + arbLobe_exterior = {} + arbLobe_2dgroup = {} + arbTerms = ["upper lobe of left lung dorsal", "upper lobe of right lung dorsal"] + for arbTerm in arbTerms: + group = findOrCreateAnnotationGroupForTerm(annotationGroups, region, [arbTerm, "None"]) + group2d = group.getFieldElementGroup(mesh2d) + group2d_exterier = fm.createFieldAnd(group2d, is_exterior) + arbLobe_group.update({arbTerm: group}) + arbLobe_2dgroup.update({arbTerm: group2d}) + arbLobe_exterior.update({arbTerm: group2d_exterier}) + + # Exterior surfaces of lungs surfaceTerms = [ "left lung", - "right lung", - "upper lobe of right lung", - "middle lobe of right lung", - "lower lobe of right lung", "lower lobe of left lung", "upper lobe of left lung", - "right lung accessory lobe", + "right lung", + "lower lobe of right lung", + "middle lobe of right lung", + "upper lobe of right lung", + "right lung accessory lobe" ] subLeftLungTerms = ["lower lobe of left lung", "upper lobe of left lung"] - baseLungTerms = ["left lung", "right lung", "right lung accessory lobe"] - lobe = {} + lobe = {} + lobe_exterior = {} for term in surfaceTerms: if (numberOfLeftLung == 1) and (term in subLeftLungTerms): continue @@ -750,38 +781,148 @@ def defineFaceAnnotations(cls, region, options, annotationGroups): group = getAnnotationGroupForTerm(annotationGroups, get_lung_term(term)) group2d = group.getFieldElementGroup(mesh2d) - - if "lobe of" in term: - lobe.update({term: group2d}) - group2d_exterior = fm.createFieldAnd(group2d, is_exterior) + surfaceGroup = findOrCreateAnnotationGroupForTerm(annotationGroups, region, get_lung_term(term + " surface")) surfaceGroup.getMeshGroup(mesh2d).addElementsConditional(group2d_exterior) + lobe_exterior.update({term + " surface": group2d_exterior}) - if term in baseLungTerms: - if term == "right lung accessory lobe": - group2D_exterior_bottom = fm.createFieldAnd(group2d_exterior, is_xi3_0) - else: - group2d_exterior_xi1_0 = fm.createFieldAnd(group2d_exterior, is_xi1_0) - group2d_exterior_xi1_1 = fm.createFieldAnd(group2d_exterior, is_xi1_1) - group2d_exterior_xi1_01 = fm.createFieldOr(group2d_exterior_xi1_0, group2d_exterior_xi1_1) - group2D_exterior_bottom = fm.createFieldXor(group2d_exterior, group2d_exterior_xi1_01) + if "lobe of" in term: + lobe.update({term: group2d}) - baseSurfaceGroup = findOrCreateAnnotationGroupForTerm(annotationGroups, region, get_lung_term("base of " + term + " surface")) - baseSurfaceGroup.getMeshGroup(mesh2d).addElementsConditional(group2D_exterior_bottom) + # medial and lateral in the subgroup + if term != "right lung accessory lobe": + for sideTerm in ['lateral surface of ', 'medial surface of ']: + surfaceGroup = findOrCreateAnnotationGroupForTerm(annotationGroups, region, get_lung_term(sideTerm + term)) + if (('lateral' in sideTerm) and ('left' in term)) or (('medial' in sideTerm) and ('right' in term)): + surfaceGroup.getMeshGroup(mesh2d).addElementsConditional(fm.createFieldAnd(group2d_exterior, is_xi1_0)) + elif (('medial' in sideTerm) and ('left' in term)) or (('lateral' in sideTerm) and ('right' in term)): + surfaceGroup.getMeshGroup(mesh2d).addElementsConditional(fm.createFieldAnd(group2d_exterior, is_xi1_1)) + + # Base surface of lungs (incl. lobes) + groupTerm = [] + baseTerms = ['left lung surface', 'lower lobe of right lung surface', 'middle lobe of right lung surface', 'right lung surface'] + + # Base of left lung + if numberOfLeftLung > 1: + baseTerms = ['lower lobe of left lung surface', 'upper lobe of left lung surface'] + baseTerms + baseLeftLowerLung = fm.createFieldAnd(lobe_exterior[baseTerms[0]], is_xi3_0) + groupTerm.append(baseLeftLowerLung) + baseLeftUpperLung = fm.createFieldAnd(fm.createFieldAnd(lobe_exterior[baseTerms[1]], is_xi3_0), + fm.createFieldNot(arbLobe_exterior["upper lobe of left lung dorsal"])) + groupTerm.append(baseLeftUpperLung) + baseLeftLung = fm.createFieldOr(baseLeftLowerLung, baseLeftUpperLung) + groupTerm.append(baseLeftLung) + else: + baseLeftLung = fm.createFieldAnd(lobe_exterior['left lung surface'], is_xi3_0) + groupTerm.append(baseLeftLung) + + # Base of right lung + baseRightLowerLung = fm.createFieldAnd(lobe_exterior['lower lobe of right lung surface'], is_xi3_0) + groupTerm.append(baseRightLowerLung) + baseRightMiddleLung = fm.createFieldAnd(fm.createFieldAnd(lobe_exterior['middle lobe of right lung surface'], is_xi3_0), + fm.createFieldNot(arbLobe_exterior["upper lobe of right lung dorsal"])) + groupTerm.append(baseRightMiddleLung) + baseRightLung = fm.createFieldOr(baseRightLowerLung, baseRightMiddleLung) + groupTerm.append(baseRightLung) + + # Base of right lung accessory lobe + if hasAccessoryLobe: + baseTerms.append('right lung accessory lobe surface') + baseRightaccessory = fm.createFieldAnd(lobe_exterior[baseTerms[-1]], is_xi3_0) + groupTerm.append(baseRightaccessory) + + for term in baseTerms: + baseSurfaceGroup = findOrCreateAnnotationGroupForTerm(annotationGroups, region, get_lung_term("base of " + term)) + baseSurfaceGroup.getMeshGroup(mesh2d).addElementsConditional(groupTerm[baseTerms.index(term)]) # Fissures - fissureTerms = ["oblique fissure of right lung", "horizontal fissure of right lung", "oblique fissure of left lung"] + fissureTerms = ["oblique fissure of right lung", "horizontal fissure of right lung"] + if numberOfLeftLung > 1: fissureTerms.append("oblique fissure of left lung") + lobeFissureTerms = ["oblique fissure of lower lobe of left lung", "oblique fissure of upper lobe of left lung", + "oblique fissure of lower lobe of right lung", "oblique fissure of middle lobe of right lung", + "oblique fissure of upper lobe of right lung", "horizontal fissure of middle lobe of right lung", + "horizontal fissure of upper lobe of right lung"] + for fissureTerm in fissureTerms: - if (fissureTerm == "oblique fissure of left lung") and (numberOfLeftLung == 2): - fissureGroup = fm.createFieldAnd(lobe["upper lobe of left lung"], lobe["lower lobe of left lung"]) - elif fissureTerm == "oblique fissure of right lung": - fissureGroup = fm.createFieldAnd(fm.createFieldOr(lobe["middle lobe of right lung"], lobe["upper lobe of right lung"]), - lobe["lower lobe of right lung"]) - elif fissureTerm == "horizontal fissure of right lung": - fissureGroup = fm.createFieldAnd(lobe["upper lobe of right lung"], lobe["middle lobe of right lung"]) - fissureSurfaceGroup = findOrCreateAnnotationGroupForTerm(annotationGroups, region, get_lung_term(fissureTerm)) - fissureSurfaceGroup.getMeshGroup(mesh2d).addElementsConditional(fissureGroup) + if not openFissures: + if (fissureTerm == "oblique fissure of left lung") and (numberOfLeftLung > 1): + fissureGroup = fm.createFieldAnd(lobe["upper lobe of left lung"], lobe["lower lobe of left lung"]) + elif fissureTerm == "oblique fissure of right lung": + fissureGroup = fm.createFieldAnd(fm.createFieldOr(lobe["middle lobe of right lung"], lobe["upper lobe of right lung"]), + lobe["lower lobe of right lung"]) + elif fissureTerm == "horizontal fissure of right lung": + fissureGroup = fm.createFieldAnd(lobe["upper lobe of right lung"], lobe["middle lobe of right lung"]) + + fissureSurfaceGroup = findOrCreateAnnotationGroupForTerm(annotationGroups, region, get_lung_term(fissureTerm)) + fissureSurfaceGroup.getMeshGroup(mesh2d).addElementsConditional(fissureGroup) + fissureGroup_temp = fissureGroup + + for lobeFissureTerm in lobeFissureTerms: + temp_splitTerm = fissureTerm.split("of") + if (temp_splitTerm[0] in lobeFissureTerm) and (temp_splitTerm[1] in lobeFissureTerm): + if "oblique fissure of upper lobe of right lung" in lobeFissureTerm: + fissureGroup = fm.createFieldAnd(fissureGroup_temp, arbLobe_2dgroup['upper lobe of right lung dorsal']) + elif "oblique fissure of middle lobe of right lung" in lobeFissureTerm: + fissureGroup = fm.createFieldAnd(fissureGroup_temp, fm.createFieldNot(arbLobe_2dgroup['upper lobe of right lung dorsal'])) + fissureSurfaceGroup = findOrCreateAnnotationGroupForTerm(annotationGroups, region, get_lung_term(lobeFissureTerm)) + fissureSurfaceGroup.getMeshGroup(mesh2d).addElementsConditional(fissureGroup) + + # Open fissures + if openFissures: + if numberOfLeftLung > 1: + obliqueLowerLeft = fm.createFieldOr(fm.createFieldAnd(lobe_exterior['lower lobe of left lung surface'], is_xi2_1), + fm.createFieldAnd(lobe_exterior['lower lobe of left lung surface'], is_xi3_1)) + + obliqueUpperLeft = fm.createFieldOr(fm.createFieldAnd(lobe_exterior['upper lobe of left lung surface'], is_xi2_0), + fm.createFieldAnd(arbLobe_exterior['upper lobe of left lung dorsal'], is_xi3_0)) + + fissureSurfaceGroup = findOrCreateAnnotationGroupForTerm(annotationGroups, region, get_lung_term('oblique fissure of ' + 'lower lobe of left lung')) + fissureSurfaceGroup.getMeshGroup(mesh2d).addElementsConditional(obliqueLowerLeft) + + fissureSurfaceGroup = findOrCreateAnnotationGroupForTerm(annotationGroups, region, get_lung_term('oblique fissure of ' + 'upper lobe of left lung')) + fissureSurfaceGroup.getMeshGroup(mesh2d).addElementsConditional(obliqueUpperLeft) + + fissureSurfaceGroup = findOrCreateAnnotationGroupForTerm(annotationGroups, region, get_lung_term('oblique fissure of ' + 'left lung')) + fissureSurfaceGroup.getMeshGroup(mesh2d).addElementsConditional(fm.createFieldOr(obliqueUpperLeft, obliqueLowerLeft)) + + obliqueLowerRight = fm.createFieldOr(fm.createFieldAnd(lobe_exterior['lower lobe of right lung surface'], is_xi2_1), + fm.createFieldAnd(lobe_exterior['lower lobe of right lung surface'], is_xi3_1)) + + obliqueMiddleRight = fm.createFieldAnd(lobe_exterior['middle lobe of right lung surface'], is_xi2_0) + + obliqueUpperRight = fm.createFieldAnd(arbLobe_exterior['upper lobe of right lung dorsal'], is_xi3_0) + + horizontalMiddleRight = fm.createFieldAnd(lobe_exterior['middle lobe of right lung surface'], is_xi3_1) + + horizontalUpperRight = fm.createFieldAnd(fm.createFieldAnd(lobe_exterior['upper lobe of right lung surface'], is_xi3_0), + fm.createFieldNot(obliqueUpperRight)) + + fissureSurfaceGroup = findOrCreateAnnotationGroupForTerm(annotationGroups, region, get_lung_term('oblique fissure of ' + 'lower lobe of right lung')) + fissureSurfaceGroup.getMeshGroup(mesh2d).addElementsConditional(obliqueLowerRight) + + fissureSurfaceGroup = findOrCreateAnnotationGroupForTerm(annotationGroups, region, get_lung_term('oblique fissure of ' + 'middle lobe of right lung')) + fissureSurfaceGroup.getMeshGroup(mesh2d).addElementsConditional(obliqueMiddleRight) + + fissureSurfaceGroup = findOrCreateAnnotationGroupForTerm(annotationGroups, region, get_lung_term('oblique fissure of ' + 'upper lobe of right lung')) + fissureSurfaceGroup.getMeshGroup(mesh2d).addElementsConditional(obliqueUpperRight) + + fissureSurfaceGroup = findOrCreateAnnotationGroupForTerm(annotationGroups, region, get_lung_term('horizontal fissure of ' + 'middle lobe of right lung')) + fissureSurfaceGroup.getMeshGroup(mesh2d).addElementsConditional(horizontalMiddleRight) + + fissureSurfaceGroup = findOrCreateAnnotationGroupForTerm(annotationGroups, region, get_lung_term('horizontal fissure of ' + 'upper lobe of right lung')) + fissureSurfaceGroup.getMeshGroup(mesh2d).addElementsConditional(horizontalUpperRight) + + fissureSurfaceGroup = findOrCreateAnnotationGroupForTerm(annotationGroups, region,get_lung_term('oblique fissure of ' + 'right lung')) + fissureSurfaceGroup.getMeshGroup(mesh2d).addElementsConditional(fm.createFieldOr( + fm.createFieldOr(obliqueLowerRight, obliqueUpperRight), + fm.createFieldOr(obliqueLowerRight, obliqueMiddleRight))) + + fissureSurfaceGroup = findOrCreateAnnotationGroupForTerm(annotationGroups, region, get_lung_term('horizontal fissure of ' + 'right lung')) + fissureSurfaceGroup.getMeshGroup(mesh2d).addElementsConditional(fm.createFieldOr(horizontalMiddleRight, horizontalUpperRight)) + + for key, value in arbLobe_group.items(): + annotationGroups.remove(value) def createLungNodes(spaceFromCentre, lengthUnit, widthUnit, heightUnit, fissureAngle, obliqueProportion, lungSide, cache, coordinates, nodes, nodetemplate, @@ -1098,12 +1239,13 @@ def createLungNodes(spaceFromCentre, lengthUnit, widthUnit, heightUnit, fissureA nodeIdentifier += 1 # Annotation - if(lungSide == leftLung) and (n1 != 0): - medialLungNodesetGroup.addNode(node) - elif (lungSide != leftLung) and (n1 != lElementsCount1): - medialLungNodesetGroup.addNode(node) - lungSideNodesetGroup.addNode(node) - lungNodesetGroup.addNode(node) + if lungNodesetGroup or lungSideNodesetGroup or medialLungNodesetGroup: + if(lungSide == leftLung) and (n1 != 0): + medialLungNodesetGroup.addNode(node) + elif (lungSide != leftLung) and (n1 != lElementsCount1): + medialLungNodesetGroup.addNode(node) + lungSideNodesetGroup.addNode(node) + lungNodesetGroup.addNode(node) # ---------------------------------------------------- Upper lobe -------------------------------------------- upper_row1 = [] @@ -1355,20 +1497,21 @@ def createLungNodes(spaceFromCentre, lengthUnit, widthUnit, heightUnit, fissureA nodeIdentifier += 1 # Annotation - if j > 2: - mediastinumNodesetGroup.addNode(node) - if (lungSide == leftLung) and (k != 0): - medialLungNodesetGroup.addNode(node) - elif (lungSide != leftLung) and (k != uElementsCount1): - medialLungNodesetGroup.addNode(node) - lungSideNodesetGroup.addNode(node) - lungNodesetGroup.addNode(node) + if lungNodesetGroup or lungSideNodesetGroup or medialLungNodesetGroup or mediastinumNodesetGroup: + if j > 2: + mediastinumNodesetGroup.addNode(node) + if (lungSide == leftLung) and (k != 0): + medialLungNodesetGroup.addNode(node) + elif (lungSide != leftLung) and (k != uElementsCount1): + medialLungNodesetGroup.addNode(node) + lungSideNodesetGroup.addNode(node) + lungNodesetGroup.addNode(node) return nodeIdentifier def createLungElements(coordinates, eftfactory, eftRegular, elementtemplateRegular, elementtemplateCustom, mesh, lungMeshGroup, lungSideMeshGroup, lowerLobeMeshGroup, middleLobeMeshGroup, - upperLobeMeshGroup, mediastinumGroupMeshGroup, + upperLobeMeshGroup, mediastinumMeshGroup, upperDorsalMeshGroup, lElementsCount1, lElementsCount2, lElementsCount3, uElementsCount1, uElementsCount2, uElementsCount3, lowerNodeIds, upperNodeIds, elementIdentifier): @@ -1453,8 +1596,10 @@ def createLungElements(coordinates, eftfactory, eftRegular, elementtemplateRegul elementIdentifier += 1 # Annotation - lungMeshGroup.addElement(element) - lungSideMeshGroup.addElement(element) + if lungMeshGroup: + lungMeshGroup.addElement(element) + if lungSideMeshGroup: + lungSideMeshGroup.addElement(element) if lowerLobeMeshGroup: lowerLobeMeshGroup.addElement(element) @@ -1611,14 +1756,20 @@ def createLungElements(coordinates, eftfactory, eftRegular, elementtemplateRegul elementIdentifier += 1 # Annotation - lungMeshGroup.addElement(element) - lungSideMeshGroup.addElement(element) + if lungMeshGroup: + lungMeshGroup.addElement(element) + if lungSideMeshGroup: + lungSideMeshGroup.addElement(element) + if upperDorsalMeshGroup and (e3 > (uElementsCount3 - 3)) and (e2 < (uElementsCount2//2)): + upperDorsalMeshGroup.addElement(element) + if middleLobeMeshGroup and (e3 < (uElementsCount3 - 2)): middleLobeMeshGroup.addElement(element) elif upperLobeMeshGroup: upperLobeMeshGroup.addElement(element) - if is_mediastanum is True: - mediastinumGroupMeshGroup.addElement(element) + + if mediastinumMeshGroup and (is_mediastanum is True): + mediastinumMeshGroup.addElement(element) return elementIdentifier, upperLobeElementID, lowerLobeElementID @@ -1779,144 +1930,6 @@ def createAccessorylobeLungElements(coordinates, eftfactory, eftRegular, element return elementIdentifier -def createDiscontinuity(coordinates, nodes, mesh, fieldcache, nodetemplate, leftUpperLobeElementID, rightUpperLobeElementID, nodeIdentifier): - """ - Refine source mesh into separate region, with change of basis. - :param meshrefinement: MeshRefinement, which knows source and target region. - :param options: Dict containing options. See getDefaultOptions(). - """ - # Replicating nodes at fissures - for e4 in range(2): - upperNodeIds = [] - store_coordinate = [] - UpperLobeElementID = leftUpperLobeElementID if e4 == 0 else rightUpperLobeElementID - if UpperLobeElementID == None: - continue - for e3 in range(len(UpperLobeElementID)): - upperNodeIds.append([]) - for e2 in range(len(UpperLobeElementID[e3])): - for e1 in range(len(UpperLobeElementID[e3][e2])): - # Fissures along the upper lobe - if ((e2 == 2) and (e3 < 2)) or ((e2 < 4) and (e3 == 2)): - elementIdentifier = UpperLobeElementID[e3][e2][e1] - element = mesh.findElementByIdentifier(elementIdentifier) - eft = element.getElementfieldtemplate(coordinates, -1) - - # Exclude horizontal fissure in the left lobe - if (e4 == 0) and (e3 == 2) and (e2 > 1): - continue - - if e3 < 2: - # Middle lobe - localNodeIndexes = [1, 2] if e1 == 0 else [2] - if e3 == 1: - if e1 == 0: - localNodeIndexes.append(5) - localNodeIndexes.append(6) - else: - # Upper lobe - if e2 == 0: - # Dorsal wedge - localNodeIndexes = [1] if (e1 == 0) else [] - elif e2 == 3: - # Ventral wedge - localNodeIndexes = [1, 2] if (e1 == 0) else [2, 3] - else: - # Regular element - localNodeIndexes = [1, 2] if (e1 == 0) else [2] - - for localNodeIndex in localNodeIndexes: - node = element.getNode(eft, localNodeIndex) - nodetemplate.defineFieldFromNode(coordinates, node) - versionsCount = nodetemplate.getValueNumberOfVersions(coordinates, -1, Node.VALUE_LABEL_VALUE) - - if versionsCount == 1: - fieldcache.setNode(node) - result0, x = coordinates.getNodeParameters(fieldcache, -1, Node.VALUE_LABEL_VALUE, 1, 3) - result0, d1 = coordinates.getNodeParameters(fieldcache, -1, Node.VALUE_LABEL_D_DS1, 1, 3) - result0, d2 = coordinates.getNodeParameters(fieldcache, -1, Node.VALUE_LABEL_D_DS2, 1, 3) - result0, d3 = coordinates.getNodeParameters(fieldcache, -1, Node.VALUE_LABEL_D_DS3, 1, 3) - - if (localNodeIndex > 2) and (e3 == 1): - # Store 3-way points in the middle lobe - store_coordinate.append([x, d1, d2, d3]) - continue - - node = nodes.createNode(nodeIdentifier, nodetemplate) - fieldcache.setNode(node) - coordinates.setNodeParameters(fieldcache, -1, Node.VALUE_LABEL_VALUE, 1, x) - coordinates.setNodeParameters(fieldcache, -1, Node.VALUE_LABEL_D_DS1, 1, d1) - coordinates.setNodeParameters(fieldcache, -1, Node.VALUE_LABEL_D_DS2, 1, d2) - coordinates.setNodeParameters(fieldcache, -1, Node.VALUE_LABEL_D_DS3, 1, d3) - upperNodeIds[e3].append(nodeIdentifier) - nodeIdentifier += 1 - - # Create 3-way points in the middle lobe - if (e3 == 1) and (e2 == 2) and (e1 == 1): - for i in range(len(store_coordinate)): - node = nodes.createNode(nodeIdentifier, nodetemplate) - fieldcache.setNode(node) - coordinates.setNodeParameters(fieldcache, -1, Node.VALUE_LABEL_VALUE, 1, store_coordinate[i][0]) - coordinates.setNodeParameters(fieldcache, -1, Node.VALUE_LABEL_D_DS1, 1, store_coordinate[i][1]) - coordinates.setNodeParameters(fieldcache, -1, Node.VALUE_LABEL_D_DS2, 1, store_coordinate[i][2]) - coordinates.setNodeParameters(fieldcache, -1, Node.VALUE_LABEL_D_DS3, 1, store_coordinate[i][3]) - upperNodeIds[e3].append(nodeIdentifier) - nodeIdentifier += 1 - - # Change node index in the element - for e3 in range(len(UpperLobeElementID)): - # middle lobe and upper lobe idx - temp_idx = upperNodeIds[0] + upperNodeIds[1] if e3 < 2 else upperNodeIds[2] - for e2 in range(len(UpperLobeElementID[e3])): - for e1 in range(len(UpperLobeElementID[e3][e2])): - # Fissures along the upper lobe - if ((e2 == 2) and (e3 < 2)) or ((e2 < 4) and (e3 == 2)): - elementIdentifier = UpperLobeElementID[e3][e2][e1] - element = mesh.findElementByIdentifier(elementIdentifier) - eft = element.getElementfieldtemplate(coordinates, -1) - nodeIds = get_element_node_identifiers(element, eft) - - # Exclude horizontal fissure in the left lobe - if (e4 == 0) and (e3 == 2) and (e2 > 2): - continue - - if (e3 == 2) and (e2 == 0): - # Dorsal wedge - nd2 = e1 + 1 - nodeIds[0] = temp_idx[0] - nodeIds[1:3] = [temp_idx[nd2], temp_idx[nd2 + 1]] - elif (e3 == 2) and (e2 < 3): - # upper and lower lobes - if e4 == 1: - # Right lung - nd1 = (e2 * e2) + e1 - nd2 = (e2 * 3) + e1 + 1 - nodeIds[:2] = [temp_idx[nd1], temp_idx[nd1 + 1]] - nodeIds[2:4] = [temp_idx[nd2], temp_idx[nd2 + 1]] - else: - # LeftLung - nd1 = (e2 * e2) + e1 - nd2 = (e2 * 2) + e1 + 1 if (e2 < 2) else e2 + e1 + 1 - temp_idx_1 = upperNodeIds[1] - nodeIds[:2] = [temp_idx[nd1], temp_idx[nd1 + 1]] if (e2 < 2) else \ - [temp_idx_1[nd2], temp_idx_1[nd2 + 1]] - if (e2 < 2): - nodeIds[2:4] = [temp_idx_1[nd2], temp_idx_1[nd2 + 1]] - elif (e3 == 2) and (e2 == 3): - # Ventral wedge - nd2 = (e3 * 3) + e1 + 1 - nodeIds[2] = temp_idx[-1] - nodeIds[:2] = [temp_idx[nd2], temp_idx[nd2 + 1]] - else: - # Middle lobe - nd1 = (e3 * 3) + e1 - nd2 = ((e3 + 1) * 3) + e1 - nodeIds[:2] = [temp_idx[nd1], temp_idx[nd1 + 1]] - nodeIds[4:6] = [temp_idx[nd2], temp_idx[nd2 + 1]] - element.setNodesByIdentifier(eft, nodeIds) - - return nodeIdentifier - def concavingDiaphragmaticSurface(diaphragmCurvatureX, diaphragmCurvatureY, fm, coordinates, diaphragmCentreX, diaphragmCentreY, lungNodesetGroup): """ diff --git a/src/scaffoldmaker/utils/zinc_utils.py b/src/scaffoldmaker/utils/zinc_utils.py index 95605ea6..7f4c5b34 100644 --- a/src/scaffoldmaker/utils/zinc_utils.py +++ b/src/scaffoldmaker/utils/zinc_utils.py @@ -459,3 +459,79 @@ def print_node_field_parameters(value_labels, node_field_parameters, format_stri nodeIdentifier = nodeParameters[0] print('( ' + str(nodeIdentifier) + ', [ ' + ', '.join(parameter_lists_to_string(valueParameters, format_string) for valueParameters in nodeParameters[1]) + ' ] )' + (', ' if (nodeIdentifier != lastNodeIdentifier) else '')) print(']\n') + +def disconnectFieldMeshGroupBoundaryNodes(coordinateFields, meshGroup1, meshGroup2, nextNodeIdentifier): + """ + Duplicate nodes in use by coordinateField on meshGroup1 and meshGroup2 and use these exclusively in meshGroup2. + :param coordinateFields: The sequence of field to disconnect, type Zinc FieldFiniteElement. These are expected to + be define with the same Elementfieldtemplate for all components in any element. + :param meshGroup1: A Zinc MeshGroup containing elements in first group. + :param meshGroup2: A Zinc MeshGroup containing elements in second group, which will be changed to use the new copies of nodes. + :param nextNodeIdentifier: First available node identifier. + :return: Final nextNodeIdentifier to use after this call, list of created node identifiers. + """ + elemiter = meshGroup1.createElementiterator() + element = elemiter.next() + nodeIdentifiers1 = set() + while element.isValid(): + eft = element.getElementfieldtemplate(coordinateFields[0], -1) + nodeCount = eft.getNumberOfLocalNodes() + for n in range(1, nodeCount + 1): + node = element.getNode(eft, n) + nodeIdentifiers1.add(node.getIdentifier()) + element = elemiter.next() + copyIdentifiersMap = {} + fieldmodule = coordinateFields[0].getFieldmodule() + fieldcache = fieldmodule.createFieldcache() + nodes = fieldmodule.findNodesetByFieldDomainType(Field.DOMAIN_TYPE_NODES) + nodetemplate = nodes.createNodetemplate() + + allValueLabels = [ + Node.VALUE_LABEL_VALUE, Node.VALUE_LABEL_D_DS1, Node.VALUE_LABEL_D_DS2, Node.VALUE_LABEL_D2_DS1DS2, + Node.VALUE_LABEL_D2_DS1DS3, Node.VALUE_LABEL_D2_DS2DS3, Node.VALUE_LABEL_D_DS3, Node.VALUE_LABEL_D3_DS1DS2DS3] + + with ChangeManager(fieldmodule): + elemiter = meshGroup2.createElementiterator() + element = elemiter.next() + while element.isValid(): + eft = element.getElementfieldtemplate(coordinateFields[0], -1) + nodeCount = eft.getNumberOfLocalNodes() + for n in range(1, nodeCount + 1): + existingNode = element.getNode(eft, n) + existingNodeIdentifier = existingNode.getIdentifier() + copyNodeIdentifier = copyIdentifiersMap.get(existingNodeIdentifier) + copyNode = None + + if copyNodeIdentifier: + copyNode = nodes.findNodeByIdentifier(copyNodeIdentifier) + else: + if existingNodeIdentifier in nodeIdentifiers1: + copyNodeIdentifier = nextNodeIdentifier + for coordinateField in coordinateFields: + nodetemplate.defineFieldFromNode(coordinateField, existingNode) + copyNode = nodes.createNode(nextNodeIdentifier, nodetemplate) + copyIdentifiersMap[existingNodeIdentifier] = copyNodeIdentifier + nextNodeIdentifier += 1 + + # copy field parameters from existingNode to copyNode + for coordinateField in coordinateFields: + componentsCount = coordinateField.getNumberOfComponents() + + for valueLabel in allValueLabels: + versionCount = nodetemplate.getValueNumberOfVersions(coordinateField, -1, valueLabel) + + for version in range(1, versionCount + 1): + fieldcache.setNode(existingNode) + result, values = coordinateField.getNodeParameters( + fieldcache, -1, valueLabel, version, componentsCount) + fieldcache.setNode(copyNode) + coordinateField.setNodeParameters( + fieldcache, -1, valueLabel, version, values) + + if copyNode: + result = element.setNode(eft, n, copyNode) + assert result == 1 + + element = elemiter.next() + + return nextNodeIdentifier, list(copyIdentifiersMap.values()) diff --git a/tests/test_lung.py b/tests/test_lung.py index a90a5dfe..646c2af5 100644 --- a/tests/test_lung.py +++ b/tests/test_lung.py @@ -180,7 +180,7 @@ def test_lung1(self): self.assertEqual(2353, element.getIdentifier()) assertAlmostEqualList(self, xi, [ 0.0, 0.0, 1.0 ], 1.0E-10) - def test_lung2(self): + def test_lung2_human(self): """ Test creation of heart scaffold. """ @@ -190,12 +190,11 @@ def test_lung2(self): options = scaffold.getDefaultOptions(["Human 1"]) self.assertEqual(36, len(options)) self.assertFalse(scaffold.checkOptions(options)) - context = Context("Test") region = context.getDefaultRegion() self.assertTrue(region.isValid()) annotationGroups = scaffold.generateMesh(region, options) - self.assertEqual(29, len(annotationGroups)) + self.assertEqual(54, len(annotationGroups)) fieldmodule = region.getFieldmodule() mesh3d = fieldmodule.findMeshByDimension(3) self.assertEqual(88, mesh3d.getSize()) @@ -242,21 +241,58 @@ def test_lung2(self): "upper lobe of right lung": 16, "middle lobe of right lung": 8, "lower lobe of right lung": 20, - # "right lung accessory lobe": 0 } for name in expectedSizes3d: group = getAnnotationGroupForTerm(annotationGroups, get_lung_term(name)) size = group.getMeshGroup(mesh3d).getSize() self.assertEqual(expectedSizes3d[name], size, name) expectedSizes2d = { + "base of left lung surface": 12, + "base of lower lobe of left lung surface": 8, + "base of upper lobe of left lung surface": 4, + "base of right lung surface": 12, + "base of lower lobe of right lung surface": 8, + "base of middle lobe of right lung surface": 4, "horizontal fissure of right lung": 4, + "horizontal fissure of middle lobe of right lung": 4, + "horizontal fissure of upper lobe of right lung": 4, + 'lateral surface of lower lobe of left lung': 10, + 'lateral surface of upper lobe of left lung': 12, + 'lateral surface of lower lobe of right lung': 10, + 'lateral surface of middle lobe of right lung': 4, + 'lateral surface of upper lobe of right lung': 8, + 'medial surface of lower lobe of left lung': 10, + 'medial surface of upper lobe of left lung': 12, + 'medial surface of lower lobe of right lung': 10, + 'medial surface of middle lobe of right lung': 4, + 'medial surface of upper lobe of right lung': 8, "oblique fissure of left lung": 8, - "oblique fissure of right lung": 8 - } + "oblique fissure of right lung": 8, + "oblique fissure of lower lobe of left lung": 8, + "oblique fissure of upper lobe of left lung": 8, + "oblique fissure of lower lobe of right lung": 8, + # "oblique fissure of middle lobe of right lung": 4, + # "oblique fissure of upper lobe of right lung": 4, + "left lung surface": 56, + "lower lobe of left lung surface": 28, + "upper lobe of left lung surface": 28, + "right lung surface": 56, + "lower lobe of right lung surface": 28, + "middle lobe of right lung surface": 12, + "upper lobe of right lung surface": 16, + } for name in expectedSizes2d: group = getAnnotationGroupForTerm(annotationGroups, get_lung_term(name)) size = group.getMeshGroup(mesh2d).getSize() self.assertEqual(expectedSizes2d[name], size, name) + expectedSizes1d = { + "anterior border of left lung": 5, + "anterior border of right lung": 5 + } + for name in expectedSizes1d: + group = getAnnotationGroupForTerm(annotationGroups, get_lung_term(name)) + size = group.getMeshGroup(mesh1d).getSize() + self.assertEqual(expectedSizes1d[name], size, name) # test finding a marker in scaffold markerGroup = fieldmodule.findFieldByName("marker").castGroup() @@ -302,7 +338,7 @@ def test_lung2(self): for annotation in annotationGroups: if annotation not in oldAnnotationGroups: annotationGroup.addSubelements() - self.assertEqual(29, len(annotationGroups)) + self.assertEqual(54, len(annotationGroups)) mesh3d = refineFieldmodule.findMeshByDimension(3) self.assertEqual(5632, mesh3d.getSize()) @@ -324,6 +360,11 @@ def test_lung2(self): group = getAnnotationGroupForTerm(annotationGroups, get_lung_term(name)) size = group.getMeshGroup(mesh2d).getSize() self.assertEqual(expectedSizes2d[name]*(refineNumberOfElements**2), size, name) + # for name in expectedSizes1d: + # group = getAnnotationGroupForTerm(annotationGroups, get_lung_term(name)) + # size = group.getMeshGroup(mesh1d).getSize() + # self.assertEqual(expectedSizes1d[name]*refineNumberOfElements, size, name) + # test finding a marker in refined scaffold markerGroup = refineFieldmodule.findFieldByName("marker").castGroup() @@ -343,5 +384,591 @@ def test_lung2(self): self.assertEqual(2557, element.getIdentifier()) assertAlmostEqualList(self, xi, [ 0.0, 1.0, 1.0 ], 1.0E-10) + def test_lung2_mouse(self): + """ + Test creation of heart scaffold. + """ + scaffold = MeshType_3d_lung2 + parameterSetNames = scaffold.getParameterSetNames() + self.assertEqual(parameterSetNames, [ "Default", "Human 1", "Mouse 1", "Rat 1", "Pig 1", "Material" ]) + options = scaffold.getDefaultOptions(["Mouse 1"]) + self.assertEqual(36, len(options)) + self.assertFalse(scaffold.checkOptions(options)) + context = Context("Test") + region = context.getDefaultRegion() + self.assertTrue(region.isValid()) + annotationGroups = scaffold.generateMesh(region, options) + self.assertEqual(47, len(annotationGroups)) + fieldmodule = region.getFieldmodule() + mesh3d = fieldmodule.findMeshByDimension(3) + self.assertEqual(108, mesh3d.getSize()) + mesh2d = fieldmodule.findMeshByDimension(2) + self.assertEqual(366, mesh2d.getSize()) + mesh1d = fieldmodule.findMeshByDimension(1) + self.assertEqual(429, mesh1d.getSize()) + nodes = fieldmodule.findNodesetByFieldDomainType(Field.DOMAIN_TYPE_NODES) + self.assertEqual(184, nodes.getSize()) + datapoints = fieldmodule.findNodesetByFieldDomainType(Field.DOMAIN_TYPE_DATAPOINTS) + self.assertEqual(0, datapoints.getSize()) + + # check coordinates range, outside surface area and volume + coordinates = fieldmodule.findFieldByName("coordinates").castFiniteElement() + self.assertTrue(coordinates.isValid()) + minimums, maximums = evaluateFieldNodesetRange(coordinates, nodes) + assertAlmostEqualList(self, minimums, [ -0.648319962173382, -0.9031395107306447, -0.6868158569004461 ], 1.0E-6) + assertAlmostEqualList(self, maximums, [ 0.74645928674602, -0.05493567915525492, 0.632338342474981 ], 1.0E-6) + with ChangeManager(fieldmodule): + one = fieldmodule.createFieldConstant(1.0) + upperRightFissureGroup = fieldmodule.findFieldByName('upper lobe of right lung').castGroup() + self.assertTrue(upperRightFissureGroup.isValid()) + upperRightFissureMeshGroup = upperRightFissureGroup.getFieldElementGroup(mesh2d).getMeshGroup() + self.assertTrue(upperRightFissureMeshGroup.isValid()) + surfaceAreaField = fieldmodule.createFieldMeshIntegral(one, coordinates, upperRightFissureMeshGroup) + surfaceAreaField.setNumbersOfPoints(4) + volumeField = fieldmodule.createFieldMeshIntegral(one, coordinates, mesh3d) + volumeField.setNumbersOfPoints(3) + fieldcache = fieldmodule.createFieldcache() + result, surfaceArea = surfaceAreaField.evaluateReal(fieldcache, 1) + self.assertEqual(result, RESULT_OK) + self.assertAlmostEqual(surfaceArea, 1.9553254914411873, delta=1.0E-2) + result, volume = volumeField.evaluateReal(fieldcache, 1) + self.assertEqual(result, RESULT_OK) + self.assertAlmostEqual(volume, 0.36135705926365336, delta=1.0E-2) + + # check some annotationGroups: + expectedSizes3d = { + "lung": 108, + "left lung": 44, + "right lung": 44, + "upper lobe of right lung": 16, + "middle lobe of right lung": 8, + "lower lobe of right lung": 20, + "right lung accessory lobe": 20 + } + for name in expectedSizes3d: + group = getAnnotationGroupForTerm(annotationGroups, get_lung_term(name)) + size = group.getMeshGroup(mesh3d).getSize() + self.assertEqual(expectedSizes3d[name], size, name) + expectedSizes2d = { + "base of left lung surface": 12, + "base of right lung surface": 12, + "base of lower lobe of right lung surface": 8, + "base of middle lobe of right lung surface": 4, + "horizontal fissure of right lung": 4, + "horizontal fissure of middle lobe of right lung": 4, + "horizontal fissure of upper lobe of right lung": 4, + 'lateral surface of lower lobe of right lung': 10, + 'lateral surface of middle lobe of right lung': 4, + 'lateral surface of upper lobe of right lung': 8, + 'medial surface of lower lobe of right lung': 10, + 'medial surface of middle lobe of right lung': 4, + 'medial surface of upper lobe of right lung': 8, + "oblique fissure of right lung": 8, + "oblique fissure of lower lobe of right lung": 8, + # "oblique fissure of middle lobe of right lung": 4, + # "oblique fissure of upper lobe of right lung": 4, + "left lung surface": 56, + "right lung surface": 56, + "lower lobe of right lung surface": 28, + "middle lobe of right lung surface": 12, + "upper lobe of right lung surface": 16, + } + for name in expectedSizes2d: + group = getAnnotationGroupForTerm(annotationGroups, get_lung_term(name)) + size = group.getMeshGroup(mesh2d).getSize() + self.assertEqual(expectedSizes2d[name], size, name) + expectedSizes1d = { + "anterior border of left lung": 5, + "anterior border of right lung": 5 + } + for name in expectedSizes1d: + group = getAnnotationGroupForTerm(annotationGroups, get_lung_term(name)) + size = group.getMeshGroup(mesh1d).getSize() + self.assertEqual(expectedSizes1d[name], size, name) + + # test finding a marker in scaffold + markerGroup = fieldmodule.findFieldByName("marker").castGroup() + markerNodes = markerGroup.getFieldNodeGroup(nodes).getNodesetGroup() + self.assertEqual(10, markerNodes.getSize()) + markerName = fieldmodule.findFieldByName("marker_name") + self.assertTrue(markerName.isValid()) + markerLocation = fieldmodule.findFieldByName("marker_location") + self.assertTrue(markerLocation.isValid()) + # test apex marker point + cache = fieldmodule.createFieldcache() + node = findNodeWithName(markerNodes, markerName, "apex of left lung") + self.assertTrue(node.isValid()) + cache.setNode(node) + element, xi = markerLocation.evaluateMeshLocation(cache, 3) + self.assertEqual(40, element.getIdentifier()) + assertAlmostEqualList(self, xi, [ 0.0, 1.0, 1.0 ], 1.0E-10) + + # refine 4 and check result + # first remove any face (but not point) annotation groups as they are re-added by defineFaceAnnotations + removeAnnotationGroups = [] + for annotationGroup in annotationGroups: + if (not annotationGroup.hasMeshGroup(mesh3d)) and (annotationGroup.hasMeshGroup(mesh2d) or annotationGroup.hasMeshGroup(mesh1d)): + removeAnnotationGroups.append(annotationGroup) + for annotationGroup in removeAnnotationGroups: + annotationGroups.remove(annotationGroup) + self.assertEqual(17, len(annotationGroups)) + + refineRegion = region.createRegion() + refineFieldmodule = refineRegion.getFieldmodule() + options['Refine'] = True + options['Refine number of elements'] = 4 + refineNumberOfElements = options['Refine number of elements'] + meshrefinement = MeshRefinement(region, refineRegion, annotationGroups) + scaffold.refineMesh(meshrefinement, options) + annotationGroups = meshrefinement.getAnnotationGroups() + + refineFieldmodule.defineAllFaces() + oldAnnotationGroups = copy.copy(annotationGroups) + for annotationGroup in annotationGroups: + annotationGroup.addSubelements() + scaffold.defineFaceAnnotations(refineRegion, options, annotationGroups) + for annotation in annotationGroups: + if annotation not in oldAnnotationGroups: + annotationGroup.addSubelements() + self.assertEqual(47, len(annotationGroups)) + + mesh3d = refineFieldmodule.findMeshByDimension(3) + self.assertEqual(6912, mesh3d.getSize()) + mesh2d = refineFieldmodule.findMeshByDimension(2) + self.assertEqual(21408, mesh2d.getSize()) + mesh1d = refineFieldmodule.findMeshByDimension(1) + self.assertEqual(22164, mesh1d.getSize()) + nodes = refineFieldmodule.findNodesetByFieldDomainType(Field.DOMAIN_TYPE_NODES) + self.assertEqual(7681, nodes.getSize()) + datapoints = refineFieldmodule.findNodesetByFieldDomainType(Field.DOMAIN_TYPE_DATAPOINTS) + self.assertEqual(0, datapoints.getSize()) + + # check some refined annotationGroups: + for name in expectedSizes3d: + group = getAnnotationGroupForTerm(annotationGroups, get_lung_term(name)) + size = group.getMeshGroup(mesh3d).getSize() + self.assertEqual(expectedSizes3d[name]*(refineNumberOfElements**3), size, name) + for name in expectedSizes2d: + group = getAnnotationGroupForTerm(annotationGroups, get_lung_term(name)) + size = group.getMeshGroup(mesh2d).getSize() + self.assertEqual(expectedSizes2d[name]*(refineNumberOfElements**2), size, name) + # for name in expectedSizes1d: + # group = getAnnotationGroupForTerm(annotationGroups, get_lung_term(name)) + # size = group.getMeshGroup(mesh1d).getSize() + # self.assertEqual(expectedSizes1d[name]*refineNumberOfElements, size, name) + + + # test finding a marker in refined scaffold + markerGroup = refineFieldmodule.findFieldByName("marker").castGroup() + refinedNodes = refineFieldmodule.findNodesetByFieldDomainType(Field.DOMAIN_TYPE_NODES) + markerNodes = markerGroup.getFieldNodeGroup(refinedNodes).getNodesetGroup() + self.assertEqual(10, markerNodes.getSize()) + markerName = refineFieldmodule.findFieldByName("marker_name") + self.assertTrue(markerName.isValid()) + markerLocation = refineFieldmodule.findFieldByName("marker_location") + self.assertTrue(markerLocation.isValid()) + # test apex marker point + cache = refineFieldmodule.createFieldcache() + node = findNodeWithName(markerNodes, markerName, "apex of left lung") + self.assertTrue(node.isValid()) + cache.setNode(node) + element, xi = markerLocation.evaluateMeshLocation(cache, 3) + self.assertEqual(2557, element.getIdentifier()) + assertAlmostEqualList(self, xi, [ 0.0, 1.0, 1.0 ], 1.0E-10) + + def test_lung2_human_openFissures(self): + """ + Test creation of heart scaffold. + """ + scaffold = MeshType_3d_lung2 + parameterSetNames = scaffold.getParameterSetNames() + self.assertEqual(parameterSetNames, [ "Default", "Human 1", "Mouse 1", "Rat 1", "Pig 1", "Material" ]) + options = scaffold.getDefaultOptions(["Human 1"]) + options['Open fissures'] = True + self.assertEqual(36, len(options)) + self.assertFalse(scaffold.checkOptions(options)) + context = Context("Test") + region = context.getDefaultRegion() + self.assertTrue(region.isValid()) + annotationGroups = scaffold.generateMesh(region, options) + self.assertEqual(54, len(annotationGroups)) + fieldmodule = region.getFieldmodule() + mesh3d = fieldmodule.findMeshByDimension(3) + self.assertEqual(88, mesh3d.getSize()) + mesh2d = fieldmodule.findMeshByDimension(2) + self.assertEqual(312, mesh2d.getSize()) + mesh1d = fieldmodule.findMeshByDimension(1) + self.assertEqual(384, mesh1d.getSize()) + nodes = fieldmodule.findNodesetByFieldDomainType(Field.DOMAIN_TYPE_NODES) + self.assertEqual(172, nodes.getSize()) + datapoints = fieldmodule.findNodesetByFieldDomainType(Field.DOMAIN_TYPE_DATAPOINTS) + self.assertEqual(0, datapoints.getSize()) + + # check coordinates range, outside surface area and volume + coordinates = fieldmodule.findFieldByName("coordinates").castFiniteElement() + self.assertTrue(coordinates.isValid()) + minimums, maximums = evaluateFieldNodesetRange(coordinates, nodes) + assertAlmostEqualList(self, minimums, [ -0.708377823030876, -0.590270294734182, -0.3219838508144919 ], 1.0E-6) + assertAlmostEqualList(self, maximums, [ 0.708377823030876, 0.3397959413350077, 0.9011820259396395 ], 1.0E-6) + with ChangeManager(fieldmodule): + one = fieldmodule.createFieldConstant(1.0) + upperRightFissureGroup = fieldmodule.findFieldByName('upper lobe of right lung').castGroup() + self.assertTrue(upperRightFissureGroup.isValid()) + upperRightFissureMeshGroup = upperRightFissureGroup.getFieldElementGroup(mesh2d).getMeshGroup() + self.assertTrue(upperRightFissureMeshGroup.isValid()) + surfaceAreaField = fieldmodule.createFieldMeshIntegral(one, coordinates, upperRightFissureMeshGroup) + surfaceAreaField.setNumbersOfPoints(4) + volumeField = fieldmodule.createFieldMeshIntegral(one, coordinates, mesh3d) + volumeField.setNumbersOfPoints(3) + fieldcache = fieldmodule.createFieldcache() + result, surfaceArea = surfaceAreaField.evaluateReal(fieldcache, 1) + self.assertEqual(result, RESULT_OK) + self.assertAlmostEqual(surfaceArea, 2.1894323301334744, delta=1.0E-2) + result, volume = volumeField.evaluateReal(fieldcache, 1) + self.assertEqual(result, RESULT_OK) + self.assertAlmostEqual(volume, 0.5026277967433523, delta=1.0E-2) + + # check some annotationGroups: + expectedSizes3d = { + "lung": 88, + "left lung": 44, + "right lung": 44, + "upper lobe of left lung": 24, + "lower lobe of left lung": 20, + "upper lobe of right lung": 16, + "middle lobe of right lung": 8, + "lower lobe of right lung": 20, + # "right lung accessory lobe": 0 + } + for name in expectedSizes3d: + group = getAnnotationGroupForTerm(annotationGroups, get_lung_term(name)) + size = group.getMeshGroup(mesh3d).getSize() + self.assertEqual(expectedSizes3d[name], size, name) + expectedSizes2d = { + "base of left lung surface": 12, + "base of lower lobe of left lung surface": 8, + "base of upper lobe of left lung surface": 4, + "base of right lung surface": 12, + "base of lower lobe of right lung surface": 8, + "base of middle lobe of right lung surface": 4, + "horizontal fissure of right lung": 8, + "horizontal fissure of middle lobe of right lung": 4, + "horizontal fissure of upper lobe of right lung": 4, + 'lateral surface of lower lobe of left lung': 10, + 'lateral surface of upper lobe of left lung': 12, + 'lateral surface of lower lobe of right lung': 10, + 'lateral surface of middle lobe of right lung': 4, + 'lateral surface of upper lobe of right lung': 8, + 'medial surface of lower lobe of left lung': 10, + 'medial surface of upper lobe of left lung': 12, + 'medial surface of lower lobe of right lung': 10, + 'medial surface of middle lobe of right lung': 4, + 'medial surface of upper lobe of right lung': 8, + "oblique fissure of left lung": 16, + "oblique fissure of right lung": 16, + "oblique fissure of lower lobe of left lung": 8, + "oblique fissure of upper lobe of left lung": 8, + "oblique fissure of lower lobe of right lung": 8, + # "oblique fissure of middle lobe of right lung": 4, + # "oblique fissure of upper lobe of right lung": 4, + "left lung surface": 72, + "lower lobe of left lung surface": 36, + "upper lobe of left lung surface": 36, + "right lung surface": 80, + "lower lobe of right lung surface": 36, + "middle lobe of right lung surface": 20, + "upper lobe of right lung surface": 24, + } + for name in expectedSizes2d: + group = getAnnotationGroupForTerm(annotationGroups, get_lung_term(name)) + size = group.getMeshGroup(mesh2d).getSize() + self.assertEqual(expectedSizes2d[name], size, name) + expectedSizes1d = { + "anterior border of left lung": 5, + "anterior border of right lung": 5 + } + for name in expectedSizes1d: + group = getAnnotationGroupForTerm(annotationGroups, get_lung_term(name)) + size = group.getMeshGroup(mesh1d).getSize() + self.assertEqual(expectedSizes1d[name], size, name) + + # test finding a marker in scaffold + markerGroup = fieldmodule.findFieldByName("marker").castGroup() + markerNodes = markerGroup.getFieldNodeGroup(nodes).getNodesetGroup() + self.assertEqual(7, markerNodes.getSize()) + markerName = fieldmodule.findFieldByName("marker_name") + self.assertTrue(markerName.isValid()) + markerLocation = fieldmodule.findFieldByName("marker_location") + self.assertTrue(markerLocation.isValid()) + # test apex marker point + cache = fieldmodule.createFieldcache() + node = findNodeWithName(markerNodes, markerName, "apex of left lung") + self.assertTrue(node.isValid()) + cache.setNode(node) + element, xi = markerLocation.evaluateMeshLocation(cache, 3) + self.assertEqual(40, element.getIdentifier()) + assertAlmostEqualList(self, xi, [ 0.0, 1.0, 1.0 ], 1.0E-10) + + # refine 4 and check result + # first remove any face (but not point) annotation groups as they are re-added by defineFaceAnnotations + # removeAnnotationGroups = [] + # for annotationGroup in annotationGroups: + # if (not annotationGroup.hasMeshGroup(mesh3d)) and (annotationGroup.hasMeshGroup(mesh2d) or annotationGroup.hasMeshGroup(mesh1d)): + # removeAnnotationGroups.append(annotationGroup) + # for annotationGroup in removeAnnotationGroups: + # annotationGroups.remove(annotationGroup) + # self.assertEqual(15, len(annotationGroups)) + # + # refineRegion = region.createRegion() + # refineFieldmodule = refineRegion.getFieldmodule() + # options['Refine'] = True + # options['Refine number of elements'] = 4 + # refineNumberOfElements = options['Refine number of elements'] + # meshrefinement = MeshRefinement(region, refineRegion, annotationGroups) + # scaffold.refineMesh(meshrefinement, options) + # annotationGroups = meshrefinement.getAnnotationGroups() + # + # refineFieldmodule.defineAllFaces() + # oldAnnotationGroups = copy.copy(annotationGroups) + # for annotationGroup in annotationGroups: + # annotationGroup.addSubelements() + # scaffold.defineFaceAnnotations(refineRegion, options, annotationGroups) + # for annotation in annotationGroups: + # if annotation not in oldAnnotationGroups: + # annotationGroup.addSubelements() + # + # self.assertEqual(44, len(annotationGroups)) + # mesh3d = refineFieldmodule.findMeshByDimension(3) + # self.assertEqual(5632, mesh3d.getSize()) + # mesh2d = refineFieldmodule.findMeshByDimension(2) + # self.assertEqual(17344, mesh2d.getSize()) + # mesh1d = refineFieldmodule.findMeshByDimension(1) + # self.assertEqual(17848, mesh1d.getSize()) + # nodes = refineFieldmodule.findNodesetByFieldDomainType(Field.DOMAIN_TYPE_NODES) + # self.assertEqual(6145, nodes.getSize()) + # datapoints = refineFieldmodule.findNodesetByFieldDomainType(Field.DOMAIN_TYPE_DATAPOINTS) + # self.assertEqual(0, datapoints.getSize()) + # + # # check some refined annotationGroups: + # for name in expectedSizes3d: + # group = getAnnotationGroupForTerm(annotationGroups, get_lung_term(name)) + # size = group.getMeshGroup(mesh3d).getSize() + # self.assertEqual(expectedSizes3d[name]*(refineNumberOfElements**3), size, name) + # for name in expectedSizes2d: + # group = getAnnotationGroupForTerm(annotationGroups, get_lung_term(name)) + # size = group.getMeshGroup(mesh2d).getSize() + # self.assertEqual(expectedSizes2d[name]//2*(refineNumberOfElements**2), size, name) + # + # # test finding a marker in refined scaffold + # markerGroup = refineFieldmodule.findFieldByName("marker").castGroup() + # refinedNodes = refineFieldmodule.findNodesetByFieldDomainType(Field.DOMAIN_TYPE_NODES) + # markerNodes = markerGroup.getFieldNodeGroup(refinedNodes).getNodesetGroup() + # self.assertEqual(7, markerNodes.getSize()) + # markerName = refineFieldmodule.findFieldByName("marker_name") + # self.assertTrue(markerName.isValid()) + # markerLocation = refineFieldmodule.findFieldByName("marker_location") + # self.assertTrue(markerLocation.isValid()) + # # test apex marker point + # cache = refineFieldmodule.createFieldcache() + # node = findNodeWithName(markerNodes, markerName, "apex of left lung") + # self.assertTrue(node.isValid()) + # cache.setNode(node) + # element, xi = markerLocation.evaluateMeshLocation(cache, 3) + # self.assertEqual(2557, element.getIdentifier()) + # assertAlmostEqualList(self, xi, [ 0.0, 1.0, 1.0 ], 1.0E-10) + + def test_lung2_mouse_openFissures(self): + """ + Test creation of heart scaffold. + """ + scaffold = MeshType_3d_lung2 + parameterSetNames = scaffold.getParameterSetNames() + self.assertEqual(parameterSetNames, [ "Default", "Human 1", "Mouse 1", "Rat 1", "Pig 1", "Material" ]) + options = scaffold.getDefaultOptions(["Mouse 1"]) + options['Open fissures'] = True + self.assertEqual(36, len(options)) + self.assertFalse(scaffold.checkOptions(options)) + context = Context("Test") + region = context.getDefaultRegion() + self.assertTrue(region.isValid()) + annotationGroups = scaffold.generateMesh(region, options) + self.assertEqual(47, len(annotationGroups)) + fieldmodule = region.getFieldmodule() + mesh3d = fieldmodule.findMeshByDimension(3) + self.assertEqual(108, mesh3d.getSize()) + mesh2d = fieldmodule.findMeshByDimension(2) + self.assertEqual(378, mesh2d.getSize()) + mesh1d = fieldmodule.findMeshByDimension(1) + self.assertEqual(459, mesh1d.getSize()) + nodes = fieldmodule.findNodesetByFieldDomainType(Field.DOMAIN_TYPE_NODES) + self.assertEqual(204, nodes.getSize()) + datapoints = fieldmodule.findNodesetByFieldDomainType(Field.DOMAIN_TYPE_DATAPOINTS) + self.assertEqual(0, datapoints.getSize()) + + # check coordinates range, outside surface area and volume + coordinates = fieldmodule.findFieldByName("coordinates").castFiniteElement() + self.assertTrue(coordinates.isValid()) + minimums, maximums = evaluateFieldNodesetRange(coordinates, nodes) + assertAlmostEqualList(self, minimums, [ -0.648319962173382, -0.9031395107306447, -0.6868158569004461 ], 1.0E-6) + assertAlmostEqualList(self, maximums, [ 0.74645928674602, -0.05493567915525492, 0.632338342474981 ], 1.0E-6) + with ChangeManager(fieldmodule): + one = fieldmodule.createFieldConstant(1.0) + upperRightFissureGroup = fieldmodule.findFieldByName('upper lobe of right lung').castGroup() + self.assertTrue(upperRightFissureGroup.isValid()) + upperRightFissureMeshGroup = upperRightFissureGroup.getFieldElementGroup(mesh2d).getMeshGroup() + self.assertTrue(upperRightFissureMeshGroup.isValid()) + surfaceAreaField = fieldmodule.createFieldMeshIntegral(one, coordinates, upperRightFissureMeshGroup) + surfaceAreaField.setNumbersOfPoints(4) + volumeField = fieldmodule.createFieldMeshIntegral(one, coordinates, mesh3d) + volumeField.setNumbersOfPoints(3) + fieldcache = fieldmodule.createFieldcache() + result, surfaceArea = surfaceAreaField.evaluateReal(fieldcache, 1) + self.assertEqual(result, RESULT_OK) + self.assertAlmostEqual(surfaceArea, 1.9553254914411873, delta=1.0E-2) + result, volume = volumeField.evaluateReal(fieldcache, 1) + self.assertEqual(result, RESULT_OK) + self.assertAlmostEqual(volume, 0.36135705926365336, delta=1.0E-2) + + # check some annotationGroups: + expectedSizes3d = { + "lung": 108, + "left lung": 44, + "right lung": 44, + "upper lobe of right lung": 16, + "middle lobe of right lung": 8, + "lower lobe of right lung": 20, + "right lung accessory lobe": 20 + } + for name in expectedSizes3d: + group = getAnnotationGroupForTerm(annotationGroups, get_lung_term(name)) + size = group.getMeshGroup(mesh3d).getSize() + self.assertEqual(expectedSizes3d[name], size, name) + expectedSizes2d = { + "base of left lung surface": 12, + "base of right lung surface": 12, + "base of lower lobe of right lung surface": 8, + "base of middle lobe of right lung surface": 4, + "horizontal fissure of right lung": 8, + "horizontal fissure of middle lobe of right lung": 4, + "horizontal fissure of upper lobe of right lung": 4, + 'lateral surface of lower lobe of right lung': 10, + 'lateral surface of middle lobe of right lung': 4, + 'lateral surface of upper lobe of right lung': 8, + 'medial surface of lower lobe of right lung': 10, + 'medial surface of middle lobe of right lung': 4, + 'medial surface of upper lobe of right lung': 8, + "oblique fissure of right lung": 16, + "oblique fissure of lower lobe of right lung": 8, + # "oblique fissure of middle lobe of right lung": 4, + # "oblique fissure of upper lobe of right lung": 4, + "left lung surface": 56, + "right lung surface": 80, + "lower lobe of right lung surface": 36, + "middle lobe of right lung surface": 20, + "upper lobe of right lung surface": 24, + } + for name in expectedSizes2d: + group = getAnnotationGroupForTerm(annotationGroups, get_lung_term(name)) + size = group.getMeshGroup(mesh2d).getSize() + self.assertEqual(expectedSizes2d[name], size, name) + expectedSizes1d = { + "anterior border of left lung": 5, + "anterior border of right lung": 5 + } + for name in expectedSizes1d: + group = getAnnotationGroupForTerm(annotationGroups, get_lung_term(name)) + size = group.getMeshGroup(mesh1d).getSize() + self.assertEqual(expectedSizes1d[name], size, name) + + # test finding a marker in scaffold + markerGroup = fieldmodule.findFieldByName("marker").castGroup() + markerNodes = markerGroup.getFieldNodeGroup(nodes).getNodesetGroup() + self.assertEqual(10, markerNodes.getSize()) + markerName = fieldmodule.findFieldByName("marker_name") + self.assertTrue(markerName.isValid()) + markerLocation = fieldmodule.findFieldByName("marker_location") + self.assertTrue(markerLocation.isValid()) + # test apex marker point + cache = fieldmodule.createFieldcache() + node = findNodeWithName(markerNodes, markerName, "apex of left lung") + self.assertTrue(node.isValid()) + cache.setNode(node) + element, xi = markerLocation.evaluateMeshLocation(cache, 3) + self.assertEqual(40, element.getIdentifier()) + assertAlmostEqualList(self, xi, [ 0.0, 1.0, 1.0 ], 1.0E-10) + + # refine 4 and check result + # first remove any face (but not point) annotation groups as they are re-added by defineFaceAnnotations + # removeAnnotationGroups = [] + # for annotationGroup in annotationGroups: + # if (not annotationGroup.hasMeshGroup(mesh3d)) and (annotationGroup.hasMeshGroup(mesh2d) or annotationGroup.hasMeshGroup(mesh1d)): + # removeAnnotationGroups.append(annotationGroup) + # for annotationGroup in removeAnnotationGroups: + # annotationGroups.remove(annotationGroup) + # self.assertEqual(17, len(annotationGroups)) + # + # refineRegion = region.createRegion() + # refineFieldmodule = refineRegion.getFieldmodule() + # options['Refine'] = True + # options['Refine number of elements'] = 4 + # refineNumberOfElements = options['Refine number of elements'] + # meshrefinement = MeshRefinement(region, refineRegion, annotationGroups) + # scaffold.refineMesh(meshrefinement, options) + # annotationGroups = meshrefinement.getAnnotationGroups() + # + # refineFieldmodule.defineAllFaces() + # oldAnnotationGroups = copy.copy(annotationGroups) + # for annotationGroup in annotationGroups: + # annotationGroup.addSubelements() + # scaffold.defineFaceAnnotations(refineRegion, options, annotationGroups) + # for annotation in annotationGroups: + # if annotation not in oldAnnotationGroups: + # annotationGroup.addSubelements() + # self.assertEqual(47, len(annotationGroups)) + # + # mesh3d = refineFieldmodule.findMeshByDimension(3) + # self.assertEqual(6912, mesh3d.getSize()) + # mesh2d = refineFieldmodule.findMeshByDimension(2) + # self.assertEqual(21408, mesh2d.getSize()) + # mesh1d = refineFieldmodule.findMeshByDimension(1) + # self.assertEqual(22164, mesh1d.getSize()) + # nodes = refineFieldmodule.findNodesetByFieldDomainType(Field.DOMAIN_TYPE_NODES) + # self.assertEqual(7681, nodes.getSize()) + # datapoints = refineFieldmodule.findNodesetByFieldDomainType(Field.DOMAIN_TYPE_DATAPOINTS) + # self.assertEqual(0, datapoints.getSize()) + # + # # check some refined annotationGroups: + # for name in expectedSizes3d: + # group = getAnnotationGroupForTerm(annotationGroups, get_lung_term(name)) + # size = group.getMeshGroup(mesh3d).getSize() + # self.assertEqual(expectedSizes3d[name]*(refineNumberOfElements**3), size, name) + # for name in expectedSizes2d: + # group = getAnnotationGroupForTerm(annotationGroups, get_lung_term(name)) + # size = group.getMeshGroup(mesh2d).getSize() + # self.assertEqual(expectedSizes2d[name]*(refineNumberOfElements**2), size, name) + # # for name in expectedSizes1d: + # # group = getAnnotationGroupForTerm(annotationGroups, get_lung_term(name)) + # # size = group.getMeshGroup(mesh1d).getSize() + # # self.assertEqual(expectedSizes1d[name]*refineNumberOfElements, size, name) + # + # + # # test finding a marker in refined scaffold + # markerGroup = refineFieldmodule.findFieldByName("marker").castGroup() + # refinedNodes = refineFieldmodule.findNodesetByFieldDomainType(Field.DOMAIN_TYPE_NODES) + # markerNodes = markerGroup.getFieldNodeGroup(refinedNodes).getNodesetGroup() + # self.assertEqual(10, markerNodes.getSize()) + # markerName = refineFieldmodule.findFieldByName("marker_name") + # self.assertTrue(markerName.isValid()) + # markerLocation = refineFieldmodule.findFieldByName("marker_location") + # self.assertTrue(markerLocation.isValid()) + # # test apex marker point + # cache = refineFieldmodule.createFieldcache() + # node = findNodeWithName(markerNodes, markerName, "apex of left lung") + # self.assertTrue(node.isValid()) + # cache.setNode(node) + # element, xi = markerLocation.evaluateMeshLocation(cache, 3) + # self.assertEqual(2557, element.getIdentifier()) + # assertAlmostEqualList(self, xi, [ 0.0, 1.0, 1.0 ], 1.0E-10) + if __name__ == "__main__": unittest.main() From 9cff049fc2bc06b6cd6004376eb6d7732cff413c Mon Sep 17 00:00:00 2001 From: arti-sukasem <51779010+arti-sukasem@users.noreply.github.com> Date: Tue, 14 Jun 2022 14:34:01 +1200 Subject: [PATCH 05/10] Fix right lung and left lung annotation --- .../meshtypes/meshtype_3d_lung2.py | 61 +++++++++++++++---- 1 file changed, 49 insertions(+), 12 deletions(-) diff --git a/src/scaffoldmaker/meshtypes/meshtype_3d_lung2.py b/src/scaffoldmaker/meshtypes/meshtype_3d_lung2.py index 00b6c485..133ad646 100644 --- a/src/scaffoldmaker/meshtypes/meshtype_3d_lung2.py +++ b/src/scaffoldmaker/meshtypes/meshtype_3d_lung2.py @@ -96,23 +96,23 @@ def getDefaultOptions(cls, parameterSetName='Default'): } materialOptions = copy.deepcopy(options) if 'Human 1' in parameterSetName: - options['Left-right lung spacing'] = 0.9 - options['Left-right apex medial shear displacement'] = 0.4 + options['Left-right lung spacing'] = 0.8 + options['Left-right apex medial shear displacement'] = 0.3 options['Left-right apex ventral shear displacement'] = -0.3 options['Left lung width'] = 0.8 options['Left lung depth'] = 1.0 options['Left lung height'] = 1.0 options['Left lung ventral edge sharpness factor'] = 0.8 options['Left lung dorsal-ventral medial curvature'] = 1.0 - options['Left lung base medial protrusion'] = 0.1 - options['Left lung ventral-medial rotation degrees'] = 20.0 + options['Left lung base medial protrusion'] = 0.0 + options['Left lung ventral-medial rotation degrees'] = 10.0 options['Right lung width'] = 0.8 options['Right lung depth'] = 1.0 options['Right lung height'] = 1.0 options['Right lung ventral edge sharpness factor'] = 0.8 options['Right lung dorsal-ventral medial curvature'] = 1.0 - options['Right lung base medial protrusion'] = 0.1 - options['Right lung ventral-medial rotation degrees'] = 20.0 + options['Right lung base medial protrusion'] = 0.0 + options['Right lung ventral-medial rotation degrees'] = 10.0 options['Accessory lobe'] = False options['Diaphragm curvature x'] = 1.0 options['Diaphragm curvature y'] = 1.0 @@ -784,7 +784,9 @@ def defineFaceAnnotations(cls, region, options, annotationGroups): group2d_exterior = fm.createFieldAnd(group2d, is_exterior) surfaceGroup = findOrCreateAnnotationGroupForTerm(annotationGroups, region, get_lung_term(term + " surface")) - surfaceGroup.getMeshGroup(mesh2d).addElementsConditional(group2d_exterior) + if (not openFissures) and (('left lung' != term) or ('right lung' != term)) or (term == 'right lung accessory lobe'): + surfaceGroup.getMeshGroup(mesh2d).addElementsConditional(group2d_exterior) + lobe_exterior.update({term + " surface": group2d_exterior}) if "lobe of" in term: @@ -883,8 +885,23 @@ def defineFaceAnnotations(cls, region, options, annotationGroups): fissureSurfaceGroup = findOrCreateAnnotationGroupForTerm(annotationGroups, region, get_lung_term('oblique fissure of ' + 'upper lobe of left lung')) fissureSurfaceGroup.getMeshGroup(mesh2d).addElementsConditional(obliqueUpperLeft) + obliqueLeft = fm.createFieldOr(obliqueUpperLeft, obliqueLowerLeft) fissureSurfaceGroup = findOrCreateAnnotationGroupForTerm(annotationGroups, region, get_lung_term('oblique fissure of ' + 'left lung')) - fissureSurfaceGroup.getMeshGroup(mesh2d).addElementsConditional(fm.createFieldOr(obliqueUpperLeft, obliqueLowerLeft)) + fissureSurfaceGroup.getMeshGroup(mesh2d).addElementsConditional(obliqueLeft) + + leftLungSurfaceGroup = findOrCreateAnnotationGroupForTerm(annotationGroups, region, get_lung_term('left lung surface')) + leftLungSurfaceGroup.getMeshGroup(mesh2d).addElementsConditional(fm.createFieldAnd(lobe_exterior['left lung surface'], fm.createFieldNot(obliqueLeft))) + + leftLungSurfaceGroup = findOrCreateAnnotationGroupForTerm(annotationGroups, region, get_lung_term('lower lobe of left lung surface')) + leftLungSurface = fm.createFieldAnd(lobe_exterior['lower lobe of left lung surface'], fm.createFieldNot(obliqueLeft)) + leftLungSurfaceGroup.getMeshGroup(mesh2d).addElementsConditional(leftLungSurface) + + leftLungSurfaceGroup = findOrCreateAnnotationGroupForTerm(annotationGroups, region, get_lung_term('upper lobe of left lung surface')) + leftLungSurface = fm.createFieldAnd(lobe_exterior['upper lobe of left lung surface'], fm.createFieldNot(obliqueLeft)) + leftLungSurfaceGroup.getMeshGroup(mesh2d).addElementsConditional(leftLungSurface) + else: + leftLungSurfaceGroup = findOrCreateAnnotationGroupForTerm(annotationGroups, region, get_lung_term('left lung surface')) + leftLungSurfaceGroup.getMeshGroup(mesh2d).addElementsConditional(lobe_exterior['left lung surface']) obliqueLowerRight = fm.createFieldOr(fm.createFieldAnd(lobe_exterior['lower lobe of right lung surface'], is_xi2_1), fm.createFieldAnd(lobe_exterior['lower lobe of right lung surface'], is_xi3_1)) @@ -893,11 +910,33 @@ def defineFaceAnnotations(cls, region, options, annotationGroups): obliqueUpperRight = fm.createFieldAnd(arbLobe_exterior['upper lobe of right lung dorsal'], is_xi3_0) + obliqueRight = fm.createFieldOr(fm.createFieldOr(obliqueLowerRight, obliqueUpperRight), obliqueMiddleRight) + horizontalMiddleRight = fm.createFieldAnd(lobe_exterior['middle lobe of right lung surface'], is_xi3_1) horizontalUpperRight = fm.createFieldAnd(fm.createFieldAnd(lobe_exterior['upper lobe of right lung surface'], is_xi3_0), fm.createFieldNot(obliqueUpperRight)) + horizontalRight = fm.createFieldOr(horizontalMiddleRight, horizontalUpperRight) + + fissureRight = fm.createFieldOr(obliqueRight, horizontalRight) + + rightLungSurfaceGroup = findOrCreateAnnotationGroupForTerm(annotationGroups, region, get_lung_term('lower lobe of right lung surface')) + rightLungSurface = fm.createFieldAnd(lobe_exterior['lower lobe of right lung surface'], fm.createFieldNot(fissureRight)) + rightLungSurfaceGroup.getMeshGroup(mesh2d).addElementsConditional(rightLungSurface) + + rightLungSurfaceGroup = findOrCreateAnnotationGroupForTerm(annotationGroups, region, get_lung_term('middle lobe of right lung surface')) + rightLungSurface = fm.createFieldAnd(lobe_exterior['middle lobe of right lung surface'], fm.createFieldNot(fissureRight)) + rightLungSurfaceGroup.getMeshGroup(mesh2d).addElementsConditional(rightLungSurface) + + rightLungSurfaceGroup = findOrCreateAnnotationGroupForTerm(annotationGroups, region,get_lung_term('upper lobe of right lung surface')) + rightLungSurface = fm.createFieldAnd(lobe_exterior['upper lobe of right lung surface'], fm.createFieldNot(fissureRight)) + rightLungSurfaceGroup.getMeshGroup(mesh2d).addElementsConditional(rightLungSurface) + + rightLungSurfaceGroup = findOrCreateAnnotationGroupForTerm(annotationGroups, region,get_lung_term('right lung surface')) + rightLungSurface = fm.createFieldAnd(lobe_exterior['right lung surface'], fm.createFieldNot(fissureRight)) + rightLungSurfaceGroup.getMeshGroup(mesh2d).addElementsConditional(rightLungSurface) + fissureSurfaceGroup = findOrCreateAnnotationGroupForTerm(annotationGroups, region, get_lung_term('oblique fissure of ' + 'lower lobe of right lung')) fissureSurfaceGroup.getMeshGroup(mesh2d).addElementsConditional(obliqueLowerRight) @@ -914,12 +953,10 @@ def defineFaceAnnotations(cls, region, options, annotationGroups): fissureSurfaceGroup.getMeshGroup(mesh2d).addElementsConditional(horizontalUpperRight) fissureSurfaceGroup = findOrCreateAnnotationGroupForTerm(annotationGroups, region,get_lung_term('oblique fissure of ' + 'right lung')) - fissureSurfaceGroup.getMeshGroup(mesh2d).addElementsConditional(fm.createFieldOr( - fm.createFieldOr(obliqueLowerRight, obliqueUpperRight), - fm.createFieldOr(obliqueLowerRight, obliqueMiddleRight))) + fissureSurfaceGroup.getMeshGroup(mesh2d).addElementsConditional(obliqueRight) fissureSurfaceGroup = findOrCreateAnnotationGroupForTerm(annotationGroups, region, get_lung_term('horizontal fissure of ' + 'right lung')) - fissureSurfaceGroup.getMeshGroup(mesh2d).addElementsConditional(fm.createFieldOr(horizontalMiddleRight, horizontalUpperRight)) + fissureSurfaceGroup.getMeshGroup(mesh2d).addElementsConditional(horizontalRight) for key, value in arbLobe_group.items(): annotationGroups.remove(value) From f14ca2c4fd3bb9c08e683fd47a2bb45a00b5dddf Mon Sep 17 00:00:00 2001 From: arti-sukasem <51779010+arti-sukasem@users.noreply.github.com> Date: Tue, 14 Jun 2022 15:05:58 +1200 Subject: [PATCH 06/10] Fix material parameters --- .../meshtypes/meshtype_3d_lung2.py | 159 +++++++++--------- 1 file changed, 80 insertions(+), 79 deletions(-) diff --git a/src/scaffoldmaker/meshtypes/meshtype_3d_lung2.py b/src/scaffoldmaker/meshtypes/meshtype_3d_lung2.py index 133ad646..907da92e 100644 --- a/src/scaffoldmaker/meshtypes/meshtype_3d_lung2.py +++ b/src/scaffoldmaker/meshtypes/meshtype_3d_lung2.py @@ -37,6 +37,44 @@ class MeshType_3d_lung2(Scaffold_base): Regions and markers of the lung are annotated. """ + materialOptions = { + 'Number of left lung lobes': 2, + 'Left-right lung spacing': 1.0, + 'Left-right apex medial shear displacement': 0.0, + 'Left-right apex ventral shear displacement': 0.0, + 'Left lung width': 0.5, + 'Left lung depth': 1.0, + 'Left lung height': 1.0, + 'Left lung ventral edge sharpness factor': 0.0, + 'Left lung dorsal-ventral medial curvature': 0.0, + 'Left lung base medial protrusion': 0.0, + 'Right lung width': 0.5, + 'Right lung depth': 1.0, + 'Right lung height': 1.0, + 'Right lung ventral edge sharpness factor': 0.0, + 'Right lung dorsal-ventral medial curvature': 0.0, + 'Right lung base medial protrusion': 0.0, + 'Open fissures': False, + 'Accessory lobe': True, + 'Accessory lobe medial curvature about z-axis': 0.0, + 'Accessory lobe length': 0.5, + 'Accessory lobe dorsal centre [x,y]': [0.0, 0.25], + 'Accessory lobe dorsal height': 0.5, + 'Accessory lobe dorsal width': 0.5, + 'Accessory lobe ventral height': 0.5, + 'Accessory lobe ventral width': 0.5, + 'Diaphragm centre x': 0.0, + 'Diaphragm centre y': 0.0, + 'Diaphragm curvature x': 0.0, + 'Diaphragm curvature y': 0.0, + 'Left lung ventral-medial rotation degrees': 0.0, + 'Right lung ventral-medial rotation degrees': 0.0, + 'Accessory lobe ventral-left rotation degrees': 0.0, + 'Refine': False, + 'Refine number of elements': 4, + 'Material Parameters': None + } + @staticmethod def getName(): return '3D Lung 2' @@ -56,45 +94,9 @@ def getParameterSetNames(): def getDefaultOptions(cls, parameterSetName='Default'): if parameterSetName == 'Default': parameterSetName = 'Human 1' - options = { - 'Base parameter set': parameterSetName, - 'Number of left lung lobes': 2, - 'Left-right lung spacing': 1.0, - 'Left-right apex medial shear displacement': 0.0, - 'Left-right apex ventral shear displacement': 0.0, - 'Left lung width': 0.5, - 'Left lung depth': 1.0, - 'Left lung height': 1.0, - 'Left lung ventral edge sharpness factor': 0.0, - 'Left lung dorsal-ventral medial curvature': 0.0, - 'Left lung base medial protrusion': 0.0, - 'Right lung width': 0.5, - 'Right lung depth': 1.0, - 'Right lung height': 1.0, - 'Right lung ventral edge sharpness factor': 0.0, - 'Right lung dorsal-ventral medial curvature': 0.0, - 'Right lung base medial protrusion': 0.0, - 'Open fissures': False, - 'Accessory lobe': True, - 'Accessory lobe medial curvature about z-axis': 0.0, - 'Accessory lobe length': 0.5, - 'Accessory lobe dorsal centre [x,y]': [0.0, 0.25], - 'Accessory lobe dorsal height': 0.5, - 'Accessory lobe dorsal width': 0.5, - 'Accessory lobe ventral height': 0.5, - 'Accessory lobe ventral width': 0.5, - 'Diaphragm centre x': 0.0, - 'Diaphragm centre y': 0.0, - 'Diaphragm curvature x': 0.0, - 'Diaphragm curvature y': 0.0, - 'Left lung ventral-medial rotation degrees': 0.0, - 'Right lung ventral-medial rotation degrees': 0.0, - 'Accessory lobe ventral-left rotation degrees': 0.0, - 'Refine': False, - 'Refine number of elements': 4, - 'Material Parameters': None - } - materialOptions = copy.deepcopy(options) + + options = copy.deepcopy(cls.materialOptions) + if 'Human 1' in parameterSetName: options['Left-right lung spacing'] = 0.8 options['Left-right apex medial shear displacement'] = 0.3 @@ -207,9 +209,7 @@ def getDefaultOptions(cls, parameterSetName='Default'): options['Diaphragm centre y'] = 0.1 options['Diaphragm curvature x'] = 1.0 options['Diaphragm curvature y'] = 1.0 - options['Material Parameters'] = materialOptions - options['Material Parameters']['Base parameter set'] = 'Material' - options['Material Parameters']['Accessory lobe'] = options['Accessory lobe'] + return options @staticmethod @@ -283,6 +283,7 @@ def generateBaseMesh(cls, region, options): fm = fm_0 coordinates = coordinates_0 region_temp = region + useOptions = options else: region_1 = region.createRegion() fm_1 = region_1.getFieldmodule() @@ -292,43 +293,43 @@ def generateBaseMesh(cls, region, options): fm = fm_1 coordinates = coordinates_1 # update material coordinates according to geometric parameters - options['Material Parameters']['Number of left lung lobes'] = options['Number of left lung lobes'] - options['Material Parameters']['Open fissures'] = options['Open fissures'] - options['Material Parameters']['Accessory lobe'] = options['Accessory lobe'] - options = options['Material Parameters'] - - numberOfLeftLung = options['Number of left lung lobes'] - spacingBetweenLeftRight = options['Left-right lung spacing'] / 2.0 - lungsApexDisplacement = options['Left-right apex medial shear displacement'] - forwardLeftRightApex = options['Left-right apex ventral shear displacement'] - leftWidth = options['Left lung width'] / 2.0 - leftDepth = options['Left lung depth'] / 2.0 - leftHeight = options['Left lung height'] - leftEdgeSharpFactor = options['Left lung ventral edge sharpness factor'] - leftLungMedialCurvature = options['Left lung dorsal-ventral medial curvature'] * 2.0 - leftLungMedialProtrusion = options['Left lung base medial protrusion'] - rightWidth = options['Right lung width'] / 2.0 - rightDepth = options['Right lung depth'] / 2.0 - rightHeight = options['Right lung height'] - rightEdgeSharpFactor = options['Right lung ventral edge sharpness factor'] - rightLungMedialCurvature = options['Right lung dorsal-ventral medial curvature'] * 2.0 - rightLungMedialProtrusion = options['Right lung base medial protrusion'] - isOpenfissure = options['Open fissures'] - hasAccessoryLobe = options['Accessory lobe'] - accessoryLobeDorsalCentre = options['Accessory lobe dorsal centre [x,y]'] - accessoryLobeLength = options['Accessory lobe length'] - accessoryLobeDorsalHeight = options['Accessory lobe dorsal height'] - accessoryLobeDorsalWidth = options['Accessory lobe dorsal width'] - accessoryLobeVentralHeight = options['Accessory lobe ventral height'] - accessoryLobeVentralWidth = options['Accessory lobe ventral width'] - accessoryLobeMedialCurve = options['Accessory lobe medial curvature about z-axis'] * 2.0 - diaphragmCentreX = options['Diaphragm centre x'] - diaphragmCentreY = options['Diaphragm centre y'] - diaphragmCurvatureX = options['Diaphragm curvature x'] - diaphragmCurvatureY = options['Diaphragm curvature y'] - rotateLeftLung = options['Left lung ventral-medial rotation degrees'] - rotateRightLung = options['Right lung ventral-medial rotation degrees'] - rotateAccessoryLobe = options['Accessory lobe ventral-left rotation degrees'] + useOptions = copy.deepcopy(cls.materialOptions) + useOptions['Number of left lung lobes'] = options['Number of left lung lobes'] + useOptions['Open fissures'] = options['Open fissures'] + useOptions['Accessory lobe'] = options['Accessory lobe'] + + numberOfLeftLung = useOptions['Number of left lung lobes'] + spacingBetweenLeftRight = useOptions['Left-right lung spacing'] / 2.0 + lungsApexDisplacement = useOptions['Left-right apex medial shear displacement'] + forwardLeftRightApex = useOptions['Left-right apex ventral shear displacement'] + leftWidth = useOptions['Left lung width'] / 2.0 + leftDepth = useOptions['Left lung depth'] / 2.0 + leftHeight = useOptions['Left lung height'] + leftEdgeSharpFactor = useOptions['Left lung ventral edge sharpness factor'] + leftLungMedialCurvature = useOptions['Left lung dorsal-ventral medial curvature'] * 2.0 + leftLungMedialProtrusion = useOptions['Left lung base medial protrusion'] + rightWidth = useOptions['Right lung width'] / 2.0 + rightDepth = useOptions['Right lung depth'] / 2.0 + rightHeight = useOptions['Right lung height'] + rightEdgeSharpFactor = useOptions['Right lung ventral edge sharpness factor'] + rightLungMedialCurvature = useOptions['Right lung dorsal-ventral medial curvature'] * 2.0 + rightLungMedialProtrusion = useOptions['Right lung base medial protrusion'] + isOpenfissure = useOptions['Open fissures'] + hasAccessoryLobe = useOptions['Accessory lobe'] + accessoryLobeDorsalCentre = useOptions['Accessory lobe dorsal centre [x,y]'] + accessoryLobeLength = useOptions['Accessory lobe length'] + accessoryLobeDorsalHeight = useOptions['Accessory lobe dorsal height'] + accessoryLobeDorsalWidth = useOptions['Accessory lobe dorsal width'] + accessoryLobeVentralHeight = useOptions['Accessory lobe ventral height'] + accessoryLobeVentralWidth = useOptions['Accessory lobe ventral width'] + accessoryLobeMedialCurve = useOptions['Accessory lobe medial curvature about z-axis'] * 2.0 + diaphragmCentreX = useOptions['Diaphragm centre x'] + diaphragmCentreY = useOptions['Diaphragm centre y'] + diaphragmCurvatureX = useOptions['Diaphragm curvature x'] + diaphragmCurvatureY = useOptions['Diaphragm curvature y'] + rotateLeftLung = useOptions['Left lung ventral-medial rotation degrees'] + rotateRightLung = useOptions['Right lung ventral-medial rotation degrees'] + rotateAccessoryLobe = useOptions['Accessory lobe ventral-left rotation degrees'] nodes = fm.findNodesetByFieldDomainType(Field.DOMAIN_TYPE_NODES) nodetemplate = nodes.createNodetemplate() From e9169032630d805745cbd50e75d0369879356dce Mon Sep 17 00:00:00 2001 From: arti-sukasem <51779010+arti-sukasem@users.noreply.github.com> Date: Tue, 14 Jun 2022 15:44:18 +1200 Subject: [PATCH 07/10] Fix the centre of the bendingAroundAxis and parameter sets --- .../meshtypes/meshtype_3d_lung2.py | 52 ++++++++++--------- 1 file changed, 28 insertions(+), 24 deletions(-) diff --git a/src/scaffoldmaker/meshtypes/meshtype_3d_lung2.py b/src/scaffoldmaker/meshtypes/meshtype_3d_lung2.py index 907da92e..643377c9 100644 --- a/src/scaffoldmaker/meshtypes/meshtype_3d_lung2.py +++ b/src/scaffoldmaker/meshtypes/meshtype_3d_lung2.py @@ -98,22 +98,22 @@ def getDefaultOptions(cls, parameterSetName='Default'): options = copy.deepcopy(cls.materialOptions) if 'Human 1' in parameterSetName: - options['Left-right lung spacing'] = 0.8 + options['Left-right lung spacing'] = 0.85 options['Left-right apex medial shear displacement'] = 0.3 options['Left-right apex ventral shear displacement'] = -0.3 options['Left lung width'] = 0.8 options['Left lung depth'] = 1.0 options['Left lung height'] = 1.0 options['Left lung ventral edge sharpness factor'] = 0.8 - options['Left lung dorsal-ventral medial curvature'] = 1.0 - options['Left lung base medial protrusion'] = 0.0 + options['Left lung dorsal-ventral medial curvature'] = 0.5 + options['Left lung base medial protrusion'] = 0.1 options['Left lung ventral-medial rotation degrees'] = 10.0 options['Right lung width'] = 0.8 options['Right lung depth'] = 1.0 options['Right lung height'] = 1.0 options['Right lung ventral edge sharpness factor'] = 0.8 - options['Right lung dorsal-ventral medial curvature'] = 1.0 - options['Right lung base medial protrusion'] = 0.0 + options['Right lung dorsal-ventral medial curvature'] = 0.5 + options['Right lung base medial protrusion'] = 0.1 options['Right lung ventral-medial rotation degrees'] = 10.0 options['Accessory lobe'] = False options['Diaphragm curvature x'] = 1.0 @@ -127,16 +127,16 @@ def getDefaultOptions(cls, parameterSetName='Default'): options['Left lung depth'] = 1.0 options['Left lung height'] = 0.7 options['Left lung ventral edge sharpness factor'] = 0.5 - options['Left lung dorsal-ventral medial curvature'] = 0.6 + options['Left lung dorsal-ventral medial curvature'] = 0.5 options['Left lung base medial protrusion'] = 0.4 - options['Left lung ventral-medial rotation degrees'] = 5.0 + options['Left lung ventral-medial rotation degrees'] = -10.0 options['Right lung width'] = 0.8 options['Right lung depth'] = 1.2 options['Right lung height'] = 0.85 options['Right lung ventral edge sharpness factor'] = 0.8 - options['Right lung dorsal-ventral medial curvature'] = 0.8 - options['Right lung base medial protrusion'] = 0.2 - options['Right lung ventral-medial rotation degrees'] = 15.0 + options['Right lung dorsal-ventral medial curvature'] = 0.4 + options['Right lung base medial protrusion'] = 0.3 + options['Right lung ventral-medial rotation degrees'] = 10.0 options['Accessory lobe dorsal centre [x,y]'] = [-0.09, 0.13] options['Accessory lobe length'] = 0.65 options['Accessory lobe dorsal height'] = 0.5 @@ -150,23 +150,23 @@ def getDefaultOptions(cls, parameterSetName='Default'): options['Diaphragm curvature y'] = 1.0 elif 'Rat 1' in parameterSetName: options['Number of left lung lobes'] = 1 - options['Left-right lung spacing'] = 1.1 + options['Left-right lung spacing'] = 1.2 options['Left-right apex medial shear displacement'] = 0.4 options['Left-right apex ventral shear displacement'] = 0.2 options['Left lung width'] = 0.5 options['Left lung depth'] = 1.4 options['Left lung height'] = 1.0 options['Left lung ventral edge sharpness factor'] = 0.6 - options['Left lung dorsal-ventral medial curvature'] = 0.8 + options['Left lung dorsal-ventral medial curvature'] = 0.52 options['Left lung base medial protrusion'] = 0.1 - options['Left lung ventral-medial rotation degrees'] = -5.0 + options['Left lung ventral-medial rotation degrees'] = -20.0 options['Right lung width'] = 0.5 options['Right lung depth'] = 1.8 options['Right lung height'] = 1.0 options['Right lung ventral edge sharpness factor'] = 0.0 - options['Right lung dorsal-ventral medial curvature'] = 0.7 - options['Right lung base medial protrusion'] = 0.35 - options['Right lung ventral-medial rotation degrees'] = 5.0 + options['Right lung dorsal-ventral medial curvature'] = 0.4 + options['Right lung base medial protrusion'] = 0.5 + options['Right lung ventral-medial rotation degrees'] = -10.0 options['Accessory lobe'] = True options['Accessory lobe dorsal centre [x,y]'] = [-0.15, 0.25] options['Accessory lobe length'] = 0.7 @@ -180,23 +180,23 @@ def getDefaultOptions(cls, parameterSetName='Default'): options['Diaphragm curvature y'] = 1.0 elif 'Pig 1' in parameterSetName: options['Number of left lung lobes'] = 1 - options['Left-right lung spacing'] = 1.2 + options['Left-right lung spacing'] = 1.3 options['Left-right apex medial shear displacement'] = 0.4 options['Left-right apex ventral shear displacement'] = 0.5 options['Left lung width'] = 0.8 options['Left lung depth'] = 1.6 options['Left lung height'] = 1.1 options['Left lung ventral edge sharpness factor'] = 0.7 - options['Left lung dorsal-ventral medial curvature'] = 0.55 + options['Left lung dorsal-ventral medial curvature'] = 0.42 options['Left lung base medial protrusion'] = 0.1 - options['Left lung ventral-medial rotation degrees'] = 10.0 + options['Left lung ventral-medial rotation degrees'] = -5.0 options['Right lung width'] = 0.8 options['Right lung depth'] = 1.5 options['Right lung height'] = 1.1 options['Right lung ventral edge sharpness factor'] = 0.7 - options['Right lung dorsal-ventral medial curvature'] = 0.7 + options['Right lung dorsal-ventral medial curvature'] = 0.4 options['Right lung base medial protrusion'] = 0.1 - options['Right lung ventral-medial rotation degrees'] = 10.0 + options['Right lung ventral-medial rotation degrees'] = 0.0 options['Accessory lobe'] = True options['Accessory lobe dorsal centre [x,y]'] = [-0.12, 0.15] options['Accessory lobe length'] = 1.0 @@ -595,7 +595,7 @@ def generateBaseMesh(cls, region, options): sharpeningRidge(edgeSharpFactor, fm, coordinates, lungNodeset, spacing, length) if lungMedialcurvature != 0.0: - bendingAroundZAxis(lungMedialcurvature, fm, coordinates, lungNodeset, spacing) + bendingAroundZAxis(lungMedialcurvature, fm, coordinates, lungNodeset, spacing, length) if apexMedialDisplacement != 0.0: medialShearRadian = math.atan(apexMedialDisplacement/height) @@ -2043,7 +2043,7 @@ def concavingDiaphragmaticSurface(diaphragmCurvatureX, diaphragmCurvatureY, fm, fieldassignment.setNodeset(lungNodesetGroup) fieldassignment.assign() -def bendingAroundZAxis(curvature, fm, coordinates, lungNodesetGroup, spaceFromCentre): +def bendingAroundZAxis(curvature, fm, coordinates, lungNodesetGroup, spaceFromCentre, length=0): """ :param bulgeRadius: the radius and the centre of curvature to transfrom the scaffold :param fm: @@ -2056,7 +2056,6 @@ def bendingAroundZAxis(curvature, fm, coordinates, lungNodesetGroup, spaceFromCe # theta = y / bulgeRadius # z = z - rotateAround = 0 radius = 1/curvature scale = fm.createFieldConstant([1.0, curvature, 1.0]) scaleCoordinates = fm.createFieldMultiply(coordinates, scale) @@ -2066,6 +2065,9 @@ def bendingAroundZAxis(curvature, fm, coordinates, lungNodesetGroup, spaceFromCe scaleCoordinates = fm.createFieldMultiply(coordinates_offsety, scale) offset = fm.createFieldConstant([radius - spaceFromCentre[0], 0.0, 0.0]) else: + offset_y = fm.createFieldConstant([0.0, length/2, 0.0]) + coordinates_offsety = fm.createFieldAdd(coordinates, offset_y) + scaleCoordinates = fm.createFieldMultiply(coordinates_offsety, scale) offset = fm.createFieldConstant([radius + spaceFromCentre, 0.0, 0.0]) polarCoordinates = fm.createFieldAdd(scaleCoordinates, offset) polarCoordinates.setCoordinateSystemType(Field.COORDINATE_SYSTEM_TYPE_CYLINDRICAL_POLAR) @@ -2074,6 +2076,8 @@ def bendingAroundZAxis(curvature, fm, coordinates, lungNodesetGroup, spaceFromCe newxyzCoordinates = fm.createFieldSubtract(rcCoordinates, offset) if isinstance(spaceFromCentre, list): newxyzCoordinates = fm.createFieldSubtract(newxyzCoordinates, offset_y) + else: + newxyzCoordinates = fm.createFieldSubtract(newxyzCoordinates, offset_y) fieldassignment = coordinates.createFieldassignment(newxyzCoordinates) fieldassignment.setNodeset(lungNodesetGroup) fieldassignment.assign() From f34b6f5123e6b0c8d7906eacf520cf3c08429018 Mon Sep 17 00:00:00 2001 From: arti-sukasem <51779010+arti-sukasem@users.noreply.github.com> Date: Tue, 14 Jun 2022 16:57:04 +1200 Subject: [PATCH 08/10] Add annotations and markers to right lung accessory lobe --- src/scaffoldmaker/annotation/lung_terms.py | 14 +++- .../meshtypes/meshtype_3d_lung2.py | 46 ++++++++--- tests/test_lung.py | 78 +++++++++---------- 3 files changed, 88 insertions(+), 50 deletions(-) diff --git a/src/scaffoldmaker/annotation/lung_terms.py b/src/scaffoldmaker/annotation/lung_terms.py index 13ac6737..ad505058 100644 --- a/src/scaffoldmaker/annotation/lung_terms.py +++ b/src/scaffoldmaker/annotation/lung_terms.py @@ -8,7 +8,6 @@ ("anterior border of right lung", "ILX:0793129"), ("anterior mediastinum of left lung", "ILX:0793183"), ("anterior mediastinum of right lung", "ILX:0793184"), - ("apex of right lung accessory lobe", "ILX:0778119"), ("apex of left lung", "ILX:0778112"), ("apex of right lung", "ILX:0778113"), ("base of left lung surface", "ILX:0793187"), @@ -18,9 +17,11 @@ ("base of right lung surface", "ILX:0793188"), ("base of lower lobe of right lung surface", "ILX:0793579"), ("base of middle lobe of right lung surface", "ILX:0793580"), + ("dorsal apex of right lung accessory lobe", "ILX:0793603"), ("dorsal base of right lung accessory lobe", "ILX:0778125"), ("dorsal base of left lung", "ILX:0778126"), ("dorsal base of right lung", "ILX:0778127"), + ("dorsal surface of right lung accessory lobe", "ILX:0793609"), ("horizontal fissure of right lung", "ILX:0746327"), ("horizontal fissure of lower lobe of right lung", "ILX:0793581"), ("horizontal fissure of middle lobe of right lung", "ILX:0793582"), @@ -33,8 +34,11 @@ ("lateral surface of middle lobe of right lung", "ILX:0793586"), ("lateral surface of upper lobe of left lung", "ILX:0793587"), ("lateral surface of upper lobe of right lung", "ILX:0793588"), + ("left dorsal base of right lung accessory lobe", "ILX:0793607"), ("left lung", "UBERON:0002168", "ILX:0733450"), ("left lung surface", "ILX:0793186"), + ("left surface of right lung accessory lobe", "ILX:0793611"), + ("left ventral base of right lung accessory lobe", "ILX:0793605"), ("lower lobe of left lung", "UBERON:0008953", "ILX:0735534"), ("lower lobe of left lung surface", "ILX:0793192"), ("lower lobe of right lung", "UBERON:0002171", "ILX:0725712"), @@ -60,17 +64,23 @@ ("oblique fissure of lower lobe of right lung", "ILX:0793596"), ("oblique fissure of middle lobe of right lung", "ILX:0793597"), ("oblique fissure of upper lobe of right lung", "ILX:0793598"), + ("right dorsal base of right lung accessory lobe", "ILX:0793608"), ("right lung", "UBERON:0002167", "ILX:0729582"), ("right lung surface", "ILX:0793185"), ("right lung accessory lobe", "UBERON:0004890", "ILX:0728696"), ("right lung accessory lobe surface", "ILX:0793190"), + ("right surface of right lung accessory lobe", "ILX:0793612"), + ("right ventral base of right lung accessory lobe", "ILX:0793606"), ("upper lobe of left lung", "UBERON:0008952", "ILX:0735339"), ("upper lobe of left lung surface", "ILX:0793194"), ("upper lobe of right lung", "UBERON:0002170", "ILX:0728821"), ("upper lobe of right lung surface", "ILX:0793195"), + ("ventral apex of right lung accessory lobe", "ILX:0793604"), ("ventral base of right lung accessory lobe", "ILX:0778123"), ("ventral base of left lung", "ILX:0778118"), - ("ventral base of right lung", "ILX:0778122") + ("ventral base of right lung", "ILX:0778122"), + ("ventral surface of right lung accessory lobe", "ILX:0793610") + ] def get_lung_term(name : str): diff --git a/src/scaffoldmaker/meshtypes/meshtype_3d_lung2.py b/src/scaffoldmaker/meshtypes/meshtype_3d_lung2.py index 643377c9..6f190e5d 100644 --- a/src/scaffoldmaker/meshtypes/meshtype_3d_lung2.py +++ b/src/scaffoldmaker/meshtypes/meshtype_3d_lung2.py @@ -394,12 +394,18 @@ def generateBaseMesh(cls, region, options): annotationGroups.append(rightLungAccessoryLobeGroup) rightLungAccessoryLobeNodesetGroup = rightLungAccessoryLobeGroup.getNodesetGroup(nodes) # Marker points - accessoryApexGroup = findOrCreateAnnotationGroupForTerm(annotationGroups, region, - get_lung_term("apex of right lung accessory lobe")) - accessoryVentralGroup = findOrCreateAnnotationGroupForTerm(annotationGroups, region, - get_lung_term("ventral base of right lung accessory lobe")) - accessoryDorsalGroup = findOrCreateAnnotationGroupForTerm(annotationGroups, region, - get_lung_term("dorsal base of right lung accessory lobe")) + accessoryDorsalApexGroup = findOrCreateAnnotationGroupForTerm(annotationGroups, region, + get_lung_term("dorsal apex of right lung accessory lobe")) + accessoryVentralApexGroup = findOrCreateAnnotationGroupForTerm(annotationGroups, region, + get_lung_term("ventral apex of right lung accessory lobe")) + accessoryVentralLeftGroup = findOrCreateAnnotationGroupForTerm(annotationGroups, region, + get_lung_term("left ventral base of right lung accessory lobe")) + accessoryVentralRightGroup = findOrCreateAnnotationGroupForTerm(annotationGroups, region, + get_lung_term("right ventral base of right lung accessory lobe")) + accessoryDorsalLeftGroup = findOrCreateAnnotationGroupForTerm(annotationGroups, region, + get_lung_term("left dorsal base of right lung accessory lobe")) + accessoryDorsalRightGroup = findOrCreateAnnotationGroupForTerm(annotationGroups, region, + get_lung_term("right dorsal base of right lung accessory lobe")) # Nodeset group leftLungNodesetGroup = leftLungGroup.getNodesetGroup(nodes) @@ -658,14 +664,25 @@ def generateBaseMesh(cls, region, options): idx_temp = accesssoryLobeElementsCount1 * accesssoryLobeElementsCount2 * (accesssoryLobeElementsCount3 - 1) + 1 idx = rightLungElementCount + leftLungElementCount + idx_temp - markerList.append({"group": accessoryApexGroup, "elementId": idx, "xi": [0.0, 0.0, 1.0]}) + markerList.append({"group": accessoryDorsalApexGroup, "elementId": idx, "xi": [0.0, 0.0, 1.0]}) + + idx_temp = accesssoryLobeElementsCount1 * accesssoryLobeElementsCount2 * (accesssoryLobeElementsCount3) + idx = rightLungElementCount + leftLungElementCount + idx_temp + markerList.append({"group": accessoryVentralApexGroup, "elementId": idx, "xi": [0.0, 1.0, 1.0]}) idx_temp = accesssoryLobeElementsCount1 * accesssoryLobeElementsCount2 * (accesssoryLobeElementsCount3 - 1) idx = rightLungElementCount + leftLungElementCount + idx_temp - markerList.append({"group": accessoryVentralGroup, "elementId": idx, "xi": [1.0, 1.0, 0.0]}) + markerList.append({"group": accessoryVentralRightGroup, "elementId": idx, "xi": [1.0, 1.0, 0.0]}) + + idx_temp = accesssoryLobeElementsCount1 * accesssoryLobeElementsCount2 * (accesssoryLobeElementsCount3 - 1) - 1 + idx = rightLungElementCount + leftLungElementCount + idx_temp + markerList.append({"group": accessoryVentralLeftGroup, "elementId": idx, "xi": [0.0, 1.0, 0.0]}) idx = rightLungElementCount + leftLungElementCount + 1 - markerList.append({"group": accessoryDorsalGroup, "elementId": idx, "xi": [0.0, 0.0, 0.0]}) + markerList.append({"group": accessoryDorsalLeftGroup, "elementId": idx, "xi": [0.0, 0.0, 0.0]}) + + idx = rightLungElementCount + leftLungElementCount + 2 + markerList.append({"group": accessoryDorsalRightGroup, "elementId": idx, "xi": [1.0, 0.0, 0.0]}) for marker in markerList: annotationGroup = marker["group"] @@ -801,6 +818,17 @@ def defineFaceAnnotations(cls, region, options, annotationGroups): surfaceGroup.getMeshGroup(mesh2d).addElementsConditional(fm.createFieldAnd(group2d_exterior, is_xi1_0)) elif (('medial' in sideTerm) and ('left' in term)) or (('lateral' in sideTerm) and ('right' in term)): surfaceGroup.getMeshGroup(mesh2d).addElementsConditional(fm.createFieldAnd(group2d_exterior, is_xi1_1)) + else: + for sideTerm in ['dorsal surface of ', 'ventral surface of ', 'left surface of ', 'right surface of ']: + surfaceGroup = findOrCreateAnnotationGroupForTerm(annotationGroups, region, get_lung_term(sideTerm + term)) + if sideTerm == 'dorsal surface of ': + surfaceGroup.getMeshGroup(mesh2d).addElementsConditional(fm.createFieldAnd(group2d_exterior, is_xi2_0)) + elif sideTerm == 'ventral surface of ': + surfaceGroup.getMeshGroup(mesh2d).addElementsConditional(fm.createFieldAnd(group2d_exterior, is_xi2_1)) + elif sideTerm == 'left surface of ': + surfaceGroup.getMeshGroup(mesh2d).addElementsConditional(fm.createFieldAnd(group2d_exterior, is_xi1_0)) + elif sideTerm == 'right surface of ': + surfaceGroup.getMeshGroup(mesh2d).addElementsConditional(fm.createFieldAnd(group2d_exterior, is_xi1_1)) # Base surface of lungs (incl. lobes) groupTerm = [] diff --git a/tests/test_lung.py b/tests/test_lung.py index 646c2af5..be1cd268 100644 --- a/tests/test_lung.py +++ b/tests/test_lung.py @@ -188,7 +188,7 @@ def test_lung2_human(self): parameterSetNames = scaffold.getParameterSetNames() self.assertEqual(parameterSetNames, [ "Default", "Human 1", "Mouse 1", "Rat 1", "Pig 1", "Material" ]) options = scaffold.getDefaultOptions(["Human 1"]) - self.assertEqual(36, len(options)) + self.assertEqual(35, len(options)) self.assertFalse(scaffold.checkOptions(options)) context = Context("Test") region = context.getDefaultRegion() @@ -211,8 +211,8 @@ def test_lung2_human(self): coordinates = fieldmodule.findFieldByName("coordinates").castFiniteElement() self.assertTrue(coordinates.isValid()) minimums, maximums = evaluateFieldNodesetRange(coordinates, nodes) - assertAlmostEqualList(self, minimums, [ -0.708377823030876, -0.590270294734182, -0.3219838508144919 ], 1.0E-6) - assertAlmostEqualList(self, maximums, [ 0.708377823030876, 0.3397959413350077, 0.9011820259396395 ], 1.0E-6) + assertAlmostEqualList(self, minimums, [ -0.6864802239394051, -0.5600434870831685, -0.3280939754022496 ], 1.0E-6) + assertAlmostEqualList(self, maximums, [ 0.6864802239394051, 0.37317277760023226, 0.9284326558109661 ], 1.0E-6) with ChangeManager(fieldmodule): one = fieldmodule.createFieldConstant(1.0) upperRightFissureGroup = fieldmodule.findFieldByName('upper lobe of right lung').castGroup() @@ -229,7 +229,7 @@ def test_lung2_human(self): self.assertAlmostEqual(surfaceArea, 2.1894323301334744, delta=1.0E-2) result, volume = volumeField.evaluateReal(fieldcache, 1) self.assertEqual(result, RESULT_OK) - self.assertAlmostEqual(volume, 0.5026277967433523, delta=1.0E-2) + self.assertAlmostEqual(volume, 0.5162640407858315, delta=1.0E-2) # check some annotationGroups: expectedSizes3d = { @@ -392,13 +392,13 @@ def test_lung2_mouse(self): parameterSetNames = scaffold.getParameterSetNames() self.assertEqual(parameterSetNames, [ "Default", "Human 1", "Mouse 1", "Rat 1", "Pig 1", "Material" ]) options = scaffold.getDefaultOptions(["Mouse 1"]) - self.assertEqual(36, len(options)) + self.assertEqual(35, len(options)) self.assertFalse(scaffold.checkOptions(options)) context = Context("Test") region = context.getDefaultRegion() self.assertTrue(region.isValid()) annotationGroups = scaffold.generateMesh(region, options) - self.assertEqual(47, len(annotationGroups)) + self.assertEqual(54, len(annotationGroups)) fieldmodule = region.getFieldmodule() mesh3d = fieldmodule.findMeshByDimension(3) self.assertEqual(108, mesh3d.getSize()) @@ -407,7 +407,7 @@ def test_lung2_mouse(self): mesh1d = fieldmodule.findMeshByDimension(1) self.assertEqual(429, mesh1d.getSize()) nodes = fieldmodule.findNodesetByFieldDomainType(Field.DOMAIN_TYPE_NODES) - self.assertEqual(184, nodes.getSize()) + self.assertEqual(187, nodes.getSize()) datapoints = fieldmodule.findNodesetByFieldDomainType(Field.DOMAIN_TYPE_DATAPOINTS) self.assertEqual(0, datapoints.getSize()) @@ -415,8 +415,8 @@ def test_lung2_mouse(self): coordinates = fieldmodule.findFieldByName("coordinates").castFiniteElement() self.assertTrue(coordinates.isValid()) minimums, maximums = evaluateFieldNodesetRange(coordinates, nodes) - assertAlmostEqualList(self, minimums, [ -0.648319962173382, -0.9031395107306447, -0.6868158569004461 ], 1.0E-6) - assertAlmostEqualList(self, maximums, [ 0.74645928674602, -0.05493567915525492, 0.632338342474981 ], 1.0E-6) + assertAlmostEqualList(self, minimums, [ -0.6222365476973273, -0.9094114466182137, -0.6670573701564442 ], 1.0E-6) + assertAlmostEqualList(self, maximums, [ 0.7502072635734625, -0.03132436483879404, 0.6421449359243596 ], 1.0E-6) with ChangeManager(fieldmodule): one = fieldmodule.createFieldConstant(1.0) upperRightFissureGroup = fieldmodule.findFieldByName('upper lobe of right lung').castGroup() @@ -430,10 +430,10 @@ def test_lung2_mouse(self): fieldcache = fieldmodule.createFieldcache() result, surfaceArea = surfaceAreaField.evaluateReal(fieldcache, 1) self.assertEqual(result, RESULT_OK) - self.assertAlmostEqual(surfaceArea, 1.9553254914411873, delta=1.0E-2) + self.assertAlmostEqual(surfaceArea, 1.9656809552709291, delta=1.0E-2) result, volume = volumeField.evaluateReal(fieldcache, 1) self.assertEqual(result, RESULT_OK) - self.assertAlmostEqual(volume, 0.36135705926365336, delta=1.0E-2) + self.assertAlmostEqual(volume, 0.3728701291680594, delta=1.0E-2) # check some annotationGroups: expectedSizes3d = { @@ -489,7 +489,7 @@ def test_lung2_mouse(self): # test finding a marker in scaffold markerGroup = fieldmodule.findFieldByName("marker").castGroup() markerNodes = markerGroup.getFieldNodeGroup(nodes).getNodesetGroup() - self.assertEqual(10, markerNodes.getSize()) + self.assertEqual(13, markerNodes.getSize()) markerName = fieldmodule.findFieldByName("marker_name") self.assertTrue(markerName.isValid()) markerLocation = fieldmodule.findFieldByName("marker_location") @@ -511,7 +511,7 @@ def test_lung2_mouse(self): removeAnnotationGroups.append(annotationGroup) for annotationGroup in removeAnnotationGroups: annotationGroups.remove(annotationGroup) - self.assertEqual(17, len(annotationGroups)) + self.assertEqual(20, len(annotationGroups)) refineRegion = region.createRegion() refineFieldmodule = refineRegion.getFieldmodule() @@ -530,7 +530,7 @@ def test_lung2_mouse(self): for annotation in annotationGroups: if annotation not in oldAnnotationGroups: annotationGroup.addSubelements() - self.assertEqual(47, len(annotationGroups)) + self.assertEqual(54, len(annotationGroups)) mesh3d = refineFieldmodule.findMeshByDimension(3) self.assertEqual(6912, mesh3d.getSize()) @@ -539,7 +539,7 @@ def test_lung2_mouse(self): mesh1d = refineFieldmodule.findMeshByDimension(1) self.assertEqual(22164, mesh1d.getSize()) nodes = refineFieldmodule.findNodesetByFieldDomainType(Field.DOMAIN_TYPE_NODES) - self.assertEqual(7681, nodes.getSize()) + self.assertEqual(7684, nodes.getSize()) datapoints = refineFieldmodule.findNodesetByFieldDomainType(Field.DOMAIN_TYPE_DATAPOINTS) self.assertEqual(0, datapoints.getSize()) @@ -562,7 +562,7 @@ def test_lung2_mouse(self): markerGroup = refineFieldmodule.findFieldByName("marker").castGroup() refinedNodes = refineFieldmodule.findNodesetByFieldDomainType(Field.DOMAIN_TYPE_NODES) markerNodes = markerGroup.getFieldNodeGroup(refinedNodes).getNodesetGroup() - self.assertEqual(10, markerNodes.getSize()) + self.assertEqual(13, markerNodes.getSize()) markerName = refineFieldmodule.findFieldByName("marker_name") self.assertTrue(markerName.isValid()) markerLocation = refineFieldmodule.findFieldByName("marker_location") @@ -585,7 +585,7 @@ def test_lung2_human_openFissures(self): self.assertEqual(parameterSetNames, [ "Default", "Human 1", "Mouse 1", "Rat 1", "Pig 1", "Material" ]) options = scaffold.getDefaultOptions(["Human 1"]) options['Open fissures'] = True - self.assertEqual(36, len(options)) + self.assertEqual(35, len(options)) self.assertFalse(scaffold.checkOptions(options)) context = Context("Test") region = context.getDefaultRegion() @@ -608,8 +608,8 @@ def test_lung2_human_openFissures(self): coordinates = fieldmodule.findFieldByName("coordinates").castFiniteElement() self.assertTrue(coordinates.isValid()) minimums, maximums = evaluateFieldNodesetRange(coordinates, nodes) - assertAlmostEqualList(self, minimums, [ -0.708377823030876, -0.590270294734182, -0.3219838508144919 ], 1.0E-6) - assertAlmostEqualList(self, maximums, [ 0.708377823030876, 0.3397959413350077, 0.9011820259396395 ], 1.0E-6) + assertAlmostEqualList(self, minimums, [ -0.6864802239394051, -0.5600434870831685, -0.3280939754022496 ], 1.0E-6) + assertAlmostEqualList(self, maximums, [ 0.6864802239394051, 0.37317277760023226, 0.9284326558109661 ], 1.0E-6) with ChangeManager(fieldmodule): one = fieldmodule.createFieldConstant(1.0) upperRightFissureGroup = fieldmodule.findFieldByName('upper lobe of right lung').castGroup() @@ -626,7 +626,7 @@ def test_lung2_human_openFissures(self): self.assertAlmostEqual(surfaceArea, 2.1894323301334744, delta=1.0E-2) result, volume = volumeField.evaluateReal(fieldcache, 1) self.assertEqual(result, RESULT_OK) - self.assertAlmostEqual(volume, 0.5026277967433523, delta=1.0E-2) + self.assertAlmostEqual(volume, 0.5162640407858315, delta=1.0E-2) # check some annotationGroups: expectedSizes3d = { @@ -671,13 +671,13 @@ def test_lung2_human_openFissures(self): "oblique fissure of lower lobe of right lung": 8, # "oblique fissure of middle lobe of right lung": 4, # "oblique fissure of upper lobe of right lung": 4, - "left lung surface": 72, - "lower lobe of left lung surface": 36, - "upper lobe of left lung surface": 36, - "right lung surface": 80, - "lower lobe of right lung surface": 36, - "middle lobe of right lung surface": 20, - "upper lobe of right lung surface": 24, + "left lung surface": 56, + "lower lobe of left lung surface": 28, + "upper lobe of left lung surface": 28, + "right lung surface": 56, + "lower lobe of right lung surface": 28, + "middle lobe of right lung surface": 12, + "upper lobe of right lung surface": 16, } for name in expectedSizes2d: group = getAnnotationGroupForTerm(annotationGroups, get_lung_term(name)) @@ -786,13 +786,13 @@ def test_lung2_mouse_openFissures(self): self.assertEqual(parameterSetNames, [ "Default", "Human 1", "Mouse 1", "Rat 1", "Pig 1", "Material" ]) options = scaffold.getDefaultOptions(["Mouse 1"]) options['Open fissures'] = True - self.assertEqual(36, len(options)) + self.assertEqual(35, len(options)) self.assertFalse(scaffold.checkOptions(options)) context = Context("Test") region = context.getDefaultRegion() self.assertTrue(region.isValid()) annotationGroups = scaffold.generateMesh(region, options) - self.assertEqual(47, len(annotationGroups)) + self.assertEqual(54, len(annotationGroups)) fieldmodule = region.getFieldmodule() mesh3d = fieldmodule.findMeshByDimension(3) self.assertEqual(108, mesh3d.getSize()) @@ -801,7 +801,7 @@ def test_lung2_mouse_openFissures(self): mesh1d = fieldmodule.findMeshByDimension(1) self.assertEqual(459, mesh1d.getSize()) nodes = fieldmodule.findNodesetByFieldDomainType(Field.DOMAIN_TYPE_NODES) - self.assertEqual(204, nodes.getSize()) + self.assertEqual(207, nodes.getSize()) datapoints = fieldmodule.findNodesetByFieldDomainType(Field.DOMAIN_TYPE_DATAPOINTS) self.assertEqual(0, datapoints.getSize()) @@ -809,8 +809,8 @@ def test_lung2_mouse_openFissures(self): coordinates = fieldmodule.findFieldByName("coordinates").castFiniteElement() self.assertTrue(coordinates.isValid()) minimums, maximums = evaluateFieldNodesetRange(coordinates, nodes) - assertAlmostEqualList(self, minimums, [ -0.648319962173382, -0.9031395107306447, -0.6868158569004461 ], 1.0E-6) - assertAlmostEqualList(self, maximums, [ 0.74645928674602, -0.05493567915525492, 0.632338342474981 ], 1.0E-6) + assertAlmostEqualList(self, minimums, [ -0.6222365476973273, -0.9094114466182137, -0.6670573701564442 ], 1.0E-6) + assertAlmostEqualList(self, maximums, [ 0.7502072635734625, -0.03132436483879404, 0.6421449359243596 ], 1.0E-6) with ChangeManager(fieldmodule): one = fieldmodule.createFieldConstant(1.0) upperRightFissureGroup = fieldmodule.findFieldByName('upper lobe of right lung').castGroup() @@ -824,10 +824,10 @@ def test_lung2_mouse_openFissures(self): fieldcache = fieldmodule.createFieldcache() result, surfaceArea = surfaceAreaField.evaluateReal(fieldcache, 1) self.assertEqual(result, RESULT_OK) - self.assertAlmostEqual(surfaceArea, 1.9553254914411873, delta=1.0E-2) + self.assertAlmostEqual(surfaceArea, 1.96568095527093, delta=1.0E-2) result, volume = volumeField.evaluateReal(fieldcache, 1) self.assertEqual(result, RESULT_OK) - self.assertAlmostEqual(volume, 0.36135705926365336, delta=1.0E-2) + self.assertAlmostEqual(volume, 0.3728701291680594, delta=1.0E-2) # check some annotationGroups: expectedSizes3d = { @@ -862,10 +862,10 @@ def test_lung2_mouse_openFissures(self): # "oblique fissure of middle lobe of right lung": 4, # "oblique fissure of upper lobe of right lung": 4, "left lung surface": 56, - "right lung surface": 80, - "lower lobe of right lung surface": 36, - "middle lobe of right lung surface": 20, - "upper lobe of right lung surface": 24, + "right lung surface": 56, + "lower lobe of right lung surface": 28, + "middle lobe of right lung surface": 12, + "upper lobe of right lung surface": 16, } for name in expectedSizes2d: group = getAnnotationGroupForTerm(annotationGroups, get_lung_term(name)) @@ -883,7 +883,7 @@ def test_lung2_mouse_openFissures(self): # test finding a marker in scaffold markerGroup = fieldmodule.findFieldByName("marker").castGroup() markerNodes = markerGroup.getFieldNodeGroup(nodes).getNodesetGroup() - self.assertEqual(10, markerNodes.getSize()) + self.assertEqual(13, markerNodes.getSize()) markerName = fieldmodule.findFieldByName("marker_name") self.assertTrue(markerName.isValid()) markerLocation = fieldmodule.findFieldByName("marker_location") From 474a315c372c8f624f4c03773411e2dd8deb1800 Mon Sep 17 00:00:00 2001 From: Richard Christie Date: Fri, 17 Jun 2022 21:39:26 +1200 Subject: [PATCH 09/10] Add ellipsoid polar/cartesian conversions --- src/scaffoldmaker/utils/geometry.py | 73 +++++++++++++++++++++++++++ tests/test_general.py | 77 +++++++++++++++++++++++++++++ 2 files changed, 150 insertions(+) diff --git a/src/scaffoldmaker/utils/geometry.py b/src/scaffoldmaker/utils/geometry.py index e0dc7fa3..7b7bf811 100644 --- a/src/scaffoldmaker/utils/geometry.py +++ b/src/scaffoldmaker/utils/geometry.py @@ -8,6 +8,7 @@ import math from scaffoldmaker.utils import vector +from scaffoldmaker.utils.tracksurface import calculate_surface_delta_xi def getApproximateEllipsePerimeter(a, b): @@ -185,6 +186,78 @@ def createEllipsoidPoints(centre, poleAxis, sideAxis, elementsCountAround, eleme radiansUp = updateEllipseAngleByArcLength(magPoleAxis, magSideAxis, radiansUp, elementLengthUp) return nx, nd1, nd2 + +def getEllipsoidPolarCoordinatesTangents(a: float, b: float, c: float, u: float, v: float): + """ + Get rate of change of x, y, z with u and v at current u, v. + Given parametric equation for ellipsoid: + x = a*cos(u)*sin(v) + y = b*sin(u)*sin(v) + z = c*cos(v) + Fails at apex. + :param a, b, c: Axis length in x, y, z directions + :param u: Polar coordinate (radians from positive x towards y) from -pi to + pi + :param v: Polar coordinate (radians from positive z downwards) from 0 to pi + :return: 3 lists (x, y, z), d(x, y, z)/du d(x, y, z)/dv. + """ + cos_u = math.cos(u) + sin_u = math.sin(u) + cos_v = math.cos(v) + sin_v = math.sin(v) + x = [ + a * cos_u * sin_v, + b * sin_u * sin_v, + c * cos_v + ] + dx_du = [ + a * -sin_u * sin_v, + b * cos_u * sin_v, + 0.0 + ] + dx_dv = [ + a * cos_u * cos_v, + b * sin_u * cos_v, + c * -sin_v + ] + return x, dx_du, dx_dv + + +def getEllipsoidPolarCoordinatesFromPosition(a: float, b: float, c: float, pos: list): + """ + Convert position in x, y, z to polar coordinates u, v at nearest location on ellipsoid centred at origin. + Given parametric equation for ellipsoid: + x = a*cos(u)*sin(v) + y = b*sin(u)*sin(v) + z = c*cos(v) + Fails at apex. + :param a, b, c: Axis length in x, y, z directions + :param pos: Position of points, list of 3 coordinates in x, y, z. + :return: Polar coordinates u, v in radians. + """ + # initial guess + rx = pos[0] / a + ry = pos[1] / b + rz = pos[2] / c + u = math.atan2(ry, rx) + v = math.atan2(math.sqrt(rx*rx + ry*ry), rz) + # move along tangents + TOL = 1.0E-6 + iters = 0 + while True: + iters += 1 + x, dx_du, dx_dv = getEllipsoidPolarCoordinatesTangents(a, b, c, u, v) + deltax = [pos[c] - x[c] for c in range(3)] + du, dv = calculate_surface_delta_xi(dx_du, dx_dv, deltax) + u += du + v += dv + if (abs(du) < TOL) and (abs(dv) < TOL): + break + if iters == 100: + print('getEllipsoidPolarCoordinatesFromPosition: did not converge!') + break + return u, v + + def getCircleProjectionAxes(ax, ad1, ad2, ad3, length, angle1radians, angle2radians, angle3radians = None): ''' Project coordinates and orthogonal unit axes ax, ad1, ad2, ad3 by length in diff --git a/tests/test_general.py b/tests/test_general.py index 17f0e57a..42c05b83 100644 --- a/tests/test_general.py +++ b/tests/test_general.py @@ -1,3 +1,4 @@ +import math import unittest from opencmiss.maths.vectorops import magnitude @@ -12,6 +13,8 @@ from scaffoldmaker.meshtypes.meshtype_3d_heartatria1 import MeshType_3d_heartatria1 from scaffoldmaker.scaffoldpackage import ScaffoldPackage from scaffoldmaker.scaffolds import Scaffolds +from scaffoldmaker.utils.geometry import getEllipsoidPolarCoordinatesFromPosition, \ + getEllipsoidPolarCoordinatesTangents from scaffoldmaker.utils.zinc_utils import identifier_ranges_from_string, identifier_ranges_to_string, \ mesh_group_add_identifier_ranges, mesh_group_to_identifier_ranges, \ nodeset_group_add_identifier_ranges, nodeset_group_to_identifier_ranges @@ -351,6 +354,80 @@ def test_user_marker_points(self): self.assertEqual(105, elementOut.getIdentifier()) assertAlmostEqualList(self, [0.346095, 1, 0.66399], xiOut, delta=TOL) + def test_utils_ellipsoid(self): + """ + Test ellipsoid functions converting between coor + """ + a = 1.0 + b = 0.75 + c = 2.0 + TOL = 1.0E-7 + u, v = getEllipsoidPolarCoordinatesFromPosition(a, b, c, [1.0, 0.0, 0.0]) + self.assertAlmostEqual(u, 0.0, delta=TOL) + self.assertAlmostEqual(v, 0.5 * math.pi, delta=TOL) + x, dx_du, dx_dv = getEllipsoidPolarCoordinatesTangents(a, b, c, u, v) + assertAlmostEqualList(self, x, [1.0, 0.0, 0.0], delta=TOL) + assertAlmostEqualList(self, dx_du, [0.0, 0.75, 0.0], delta=TOL) + assertAlmostEqualList(self, dx_dv, [0.0, 0.0, -2.0], delta=TOL) + + # test a point not on the surface + u, v = getEllipsoidPolarCoordinatesFromPosition(a, b, c, [1.0, 0.75, 1.0]) + self.assertAlmostEqual(u, 0.6795888593403792, delta=TOL) + self.assertAlmostEqual(v, 1.1035255937433424, delta=TOL) + x = getEllipsoidPolarCoordinatesTangents(a, b, c, u, v)[0] + assertAlmostEqualList(self, x, [0.694448461834745, 0.42082618548100487, 0.9009025475758113], delta=TOL) + mag = (x[0] / a) * (x[0] / a) + (x[1] / b) * (x[1] / b) + (x[2] / c) * (x[2] / c) + self.assertAlmostEqual(mag, 1.0, delta=TOL) + # test the nearest point found on the surface has same polar coordinates + u, v = getEllipsoidPolarCoordinatesFromPosition(a, b, c, x) + self.assertAlmostEqual(u, 0.6795888593403792, delta=TOL) + self.assertAlmostEqual(v, 1.1035255937433424, delta=TOL) + + # test a point not on the surface + u, v = getEllipsoidPolarCoordinatesFromPosition(a, b, c, [-0.9, -0.25, -1.2]) + self.assertAlmostEqual(u, -2.8190485195549204, delta=TOL) + self.assertAlmostEqual(v, 2.185488763727226, delta=TOL) + x = getEllipsoidPolarCoordinatesTangents(a, b, c, u, v)[0] + assertAlmostEqualList(self, x, [-0.7748223762344193, -0.19421813246373332, -1.1534145713074584], delta=TOL) + mag = (x[0] / a) * (x[0] / a) + (x[1] / b) * (x[1] / b) + (x[2] / c) * (x[2] / c) + self.assertAlmostEqual(mag, 1.0, delta=TOL) + # test the nearest point found on the surface has same polar coordinates + u, v = getEllipsoidPolarCoordinatesFromPosition(a, b, c, x) + self.assertAlmostEqual(u, -2.8190485195549204, delta=TOL) + self.assertAlmostEqual(v, 2.185488763727226, delta=TOL) + + u_in = math.pi / 3.0 + v_in = math.pi / 2.0 + x, dx_du, dx_dv = getEllipsoidPolarCoordinatesTangents(a, b, c, u_in, v_in) + assertAlmostEqualList(self, x, [0.5, 0.649519052838329, 0.0], delta=TOL) + assertAlmostEqualList(self, dx_du, [-0.8660254037844386, 0.375, 0.0], delta=TOL) + assertAlmostEqualList(self, dx_dv, [0.0, 0.0, -2.0], delta=TOL) + u, v = getEllipsoidPolarCoordinatesFromPosition(a, b, c, x) + self.assertAlmostEqual(u, u_in, delta=TOL) + self.assertAlmostEqual(v, v_in, delta=TOL) + + u_in = -0.7 * math.pi + v_in = 0.3 * math.pi + x, dx_du, dx_dv = getEllipsoidPolarCoordinatesTangents(a, b, c, u_in, v_in) + assertAlmostEqualList(self, x, [-0.4755282581475769, -0.49088137289060524, 1.1755705045849463], delta=TOL) + assertAlmostEqualList(self, dx_du, [0.6545084971874736, -0.35664619361068267, 0.0], delta=TOL) + assertAlmostEqualList(self, dx_dv, [-0.3454915028125264, -0.35664619361068256, -1.618033988749895], delta=TOL) + u, v = getEllipsoidPolarCoordinatesFromPosition(a, b, c, x) + self.assertAlmostEqual(u, u_in, delta=TOL) + self.assertAlmostEqual(v, v_in, delta=TOL) + + u_in = 0.35 * math.pi + v_in = 0.65 * math.pi + x, dx_du, dx_dv = getEllipsoidPolarCoordinatesTangents(a, b, c, u_in, v_in) + assertAlmostEqualList(self, x, [0.4045084971874737, 0.5954194696096774, -0.9079809994790935], delta=TOL) + assertAlmostEqualList(self, dx_du, [-0.7938926261462366, 0.3033813728906053, 0.0], delta=TOL) + assertAlmostEqualList(self, dx_dv, [-0.20610737385376343, -0.30338137289060524, -1.7820130483767358], delta=TOL) + u, v = getEllipsoidPolarCoordinatesFromPosition(a, b, c, x) + self.assertAlmostEqual(u, u_in, delta=TOL) + self.assertAlmostEqual(v, v_in, delta=TOL) + + scaffoldPackage = ScaffoldPackage(MeshType_3d_brainstem1) + if __name__ == "__main__": unittest.main() From df43ddcfb5f6a636466f53a33012987bffeca916 Mon Sep 17 00:00:00 2001 From: arti-sukasem <51779010+arti-sukasem@users.noreply.github.com> Date: Tue, 21 Jun 2022 16:05:35 +1200 Subject: [PATCH 10/10] Minimise displacement of lower lobe --- src/scaffoldmaker/annotation/lung_terms.py | 1 + .../meshtypes/meshtype_3d_lung2.py | 62 +++++++++++++++---- 2 files changed, 51 insertions(+), 12 deletions(-) diff --git a/src/scaffoldmaker/annotation/lung_terms.py b/src/scaffoldmaker/annotation/lung_terms.py index ad505058..bc496aee 100644 --- a/src/scaffoldmaker/annotation/lung_terms.py +++ b/src/scaffoldmaker/annotation/lung_terms.py @@ -10,6 +10,7 @@ ("anterior mediastinum of right lung", "ILX:0793184"), ("apex of left lung", "ILX:0778112"), ("apex of right lung", "ILX:0778113"), + ("apex of right lung accessory lobe", "ILX:0778119"), ("base of left lung surface", "ILX:0793187"), ("base of lower lobe of left lung surface", "ILX:0793577"), ("base of upper lobe of left lung surface", "ILX:0793578"), diff --git a/src/scaffoldmaker/meshtypes/meshtype_3d_lung2.py b/src/scaffoldmaker/meshtypes/meshtype_3d_lung2.py index 6f190e5d..8196a749 100644 --- a/src/scaffoldmaker/meshtypes/meshtype_3d_lung2.py +++ b/src/scaffoldmaker/meshtypes/meshtype_3d_lung2.py @@ -14,11 +14,12 @@ from scaffoldmaker.utils.eftfactory_tricubichermite import eftfactory_tricubichermite from opencmiss.utils.zinc.general import ChangeManager from scaffoldmaker.utils.geometry import createEllipsoidPoints, getEllipseRadiansToX, getEllipseArcLength, getApproximateEllipsePerimeter, \ - updateEllipseAngleByArcLength + updateEllipseAngleByArcLength, getEllipsoidPolarCoordinatesFromPosition, getEllipsoidPolarCoordinatesTangents from scaffoldmaker.utils.interpolation import DerivativeScalingMode from scaffoldmaker.utils.derivativemoothing import DerivativeSmoothing from scaffoldmaker.utils.meshrefinement import MeshRefinement from scaffoldmaker.utils.vector import magnitude, setMagnitude, crossproduct3, normalise +from opencmiss.maths.vectorops import add, cross, dot, normalize, sub from scaffoldmaker.utils.zinc_utils import disconnectFieldMeshGroupBoundaryNodes from opencmiss.utils.zinc.field import Field, findOrCreateFieldCoordinates, findOrCreateFieldGroup, \ findOrCreateFieldNodeGroup, findOrCreateFieldStoredMeshLocation, findOrCreateFieldStoredString, createFieldEulerAnglesRotationMatrix @@ -1172,23 +1173,49 @@ def createLungNodes(spaceFromCentre, lengthUnit, widthUnit, heightUnit, fissureA px, pd1, pe, pxi, psf = sampleCubicHermiteCurves(x_temp, nd2_temp, len(x_temp)-1, elementLengthStartEndRatio = 1, arcLengthDerivatives = True) elif n2 == 2: # lower lobe - px, pd1, pe, pxi, psf = sampleCubicHermiteCurves(x_temp, nd2_temp, len(x_temp)-1, elementLengthStartEndRatio = 2, lengthFractionStart = 0.3, - lengthFractionEnd = 1, arcLengthDerivatives = True) + px, pd1, pe, pxi, psf = sampleCubicHermiteCurves(x_temp, nd2_temp, len(x_temp)-1, elementLengthStartEndRatio = 1, arcLengthDerivatives = True) elif n2 == 3: # 2nd oblique - px, pd1, pe, pxi, psf = sampleCubicHermiteCurves(x_temp, nd2_temp, len(x_temp)-1, elementLengthStartEndRatio = 2, arcLengthDerivatives = True) + x_temp.append(lower_row1[-2]) + nd2_temp.append(lower_row1_d2[-2]) + px, pd1, pe, pxi, psf = sampleCubicHermiteCurves(x_temp, nd2_temp, len(x_temp)-1, lengthFractionStart = 1, lengthFractionEnd = 1, arcLengthDerivatives = True) elif n2 == 4: # rows 2 - px, pd1, pe, pxi, psf = sampleCubicHermiteCurves(x_temp, nd2_temp, len(x_temp)-1, elementLengthStartEndRatio = 2, arcLengthDerivatives = True) + px, pd1, pe, pxi, psf = sampleCubicHermiteCurves(x_temp, nd2_temp, len(x_temp)-1, elementLengthStartEndRatio = 1, arcLengthDerivatives = True) + # sampling the node vertically on row 2 + for col in range(2): + startEndRatio = 1 if col == 0 else 1.1 + x_col_temp = [obl[1+col], lowerObl[1+col], lower_row1[1+col]] + nd2_col_temp = [obl_d2[1+col], lowerObl_d2[1+col], lower_row1_d2[1+col]] + px_temp, pd1_temp, pe, pxi, psf = sampleCubicHermiteCurves(x_col_temp, nd2_col_temp, len(x_col_temp), elementLengthStartEndRatio = startEndRatio, arcLengthDerivatives=True) + + u, v = getEllipsoidPolarCoordinatesFromPosition(majorXAxis[0], minorYAxis[1], minorZAxis[2], px_temp[-2]) + px[1 + col], dx_du, dx_dv = getEllipsoidPolarCoordinatesTangents(majorXAxis[0], minorYAxis[1], minorZAxis[2], u, v) + unit_normal = normalize(cross(dx_du, dx_dv)) + pd1[1 + col] = sub(pd1_temp[-2], unit_normal) # now tangential to surface of ellipsoid + + # print('px[1 + col]', px[1 + col]) + # print('pd1[1 + col]', pd1[1 + col]) + # print(px_temp[-2]) + # print(pd1_temp[-2]) + # px[1 + col] = px_temp[-2] + # pd1[1 + col] = pd1_temp[-2] [x.append(px[i]) for i in range(len(px))] [nd2.append(pd1[i]) for i in range(len(pd1))] + # # move the apex node of the triangular element + # px = lowerObl + # pd1 = lowerObl_d2 + # px.pop(-1) + # pd1.pop(-1) + # px, pd1, pe, pxi, psf = sampleCubicHermiteCurves(px, pd1, len(px)-1, lengthFractionStart = 1.0, lengthFractionEnd = 1.0, arcLengthDerivatives = True) + # lowerObl[-2] = px[-2] + # lowerObl_d2[-2] = pd1[-2] + # complete lower_row2 and lowerObl list lower_row2.append(obl[3]) lower_row2_d2.append(obl_d2[3]) - lowerObl.append(lower_row1[3]) - lowerObl_d2.append(lower_row1[3]) # smooth derivatives tx_d2 = [] @@ -1213,11 +1240,15 @@ def createLungNodes(spaceFromCentre, lengthUnit, widthUnit, heightUnit, fissureA elif n3 == 3: x = obl d2 = obl - for n2 in range(lElementsCount2 + 1): + for n2 in range(len(x)): tx_d2[n3].append(x[n2]) td2[n3].append(d2[n2]) if n3 == 0: - if n2 == 4: + if n2 == 3: + reverse_lowerObl = [lowerObl[-i] for i in range(1, len(lowerObl)+1)] + tx_d3.append(reverse_lowerObl) + td3.append(reverse_lowerObl) + elif n2 == 4: reverse_obl = [obl[-i] for i in range(1, len(obl)+1)] tx_d3.append(reverse_obl) td3.append(reverse_obl) @@ -1246,17 +1277,24 @@ def createLungNodes(spaceFromCentre, lengthUnit, widthUnit, heightUnit, fissureA x = copy.deepcopy(lower_row2[n2]) if n2 < 4: next_xd2 = copy.deepcopy(lower_row2[n2 + 1]) + else: + d2 = [d2[0], d2[1], d2[2]*0.6] + elif (n3 == 2) and (n2 < 3): x = copy.deepcopy(lowerObl[n2]) - if n2 < 4: + if n2 < 3: next_xd2 = copy.deepcopy(lowerObl[n2 + 1]) elif (n3 == 3) and (n2 < 3): x = copy.deepcopy(obl[n2]) if n2 < 2: next_xd2 = copy.deepcopy(obl[n2 + 1]) - else: + + # lower oblique - 2nd from the dorsal + if n2 == 1: + d3 = [d3[0]*0.5, d3[1]*0.5, d3[2]/0.75] + elif n2 == 2: # 3-way point - d2 = [md3[n2][n3][0], md3[n2][n3][1], md3[n2][n3][2]] + d2 = [md3[n2][n3][0], md3[n2][n3][1], md3[n2][n3][2]*0.7] d3 = [-md2[n3][n2][0], -md2[n3][n2][1], -md2[n3][n2][2]] else: continue