From 00c052d05d69630ee32a5d91eee09161274887f3 Mon Sep 17 00:00:00 2001 From: Mabelle Lin Date: Fri, 29 Oct 2021 16:11:13 +1300 Subject: [PATCH 01/22] Improve central path param to get closer fit to average rat stomach --- .../meshtypes/meshtype_3d_stomach1.py | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/scaffoldmaker/meshtypes/meshtype_3d_stomach1.py b/src/scaffoldmaker/meshtypes/meshtype_3d_stomach1.py index cdea05d9..180c6e1a 100644 --- a/src/scaffoldmaker/meshtypes/meshtype_3d_stomach1.py +++ b/src/scaffoldmaker/meshtypes/meshtype_3d_stomach1.py @@ -167,15 +167,15 @@ class MeshType_3d_stomach1(Scaffold_base): }, 'meshEdits': exnodeStringFromNodeValues( [Node.VALUE_LABEL_VALUE, Node.VALUE_LABEL_D_DS1, Node.VALUE_LABEL_D_DS2, Node.VALUE_LABEL_D2_DS1DS2, Node.VALUE_LABEL_D_DS3, Node.VALUE_LABEL_D2_DS1DS3], [ - [ [ 12.4, 11.9, 0.0 ], [ -0.1, -14.6, 0.0 ], [ 8.7, -1.4, 0.0 ], [ -0.7, -3.5, 0.0 ], [ 0.0, 0.0, 7.4 ], [ 0.0, 0.0, 1.5] ], - [ [ 9.5, -0.2, 0.0 ], [ -5.7, -9.0, 0.0 ], [ 6.6, -5.1, 0.0 ], [ -3.5, -3.9, 0.0 ], [ 0.0, 0.0, 8.5 ], [ 0.0, 0.0, 0.7] ], - [ [ 2.8, -5.5, 0.0 ], [ -6.6, -2.6, 0.0 ], [ 2.1, -9.1, 0.0 ], [ -4.0, -1.6, 0.0 ], [ 0.0, 0.0, 9.0 ], [ 0.0, 0.0, 0.1] ], - [ [ -2.6, -6.0, 0.0 ], [ -5.7, 0.7, 0.0 ], [ -1.6, -9.2, 0.0 ], [ -4.0, 0.5, 0.0 ], [ 0.0, 0.0, 8.8 ], [ 0.0, 0.0, -0.4] ], - [ [ -8.2, -4.1, 0.0 ], [ -5.3, 3.9, 0.0 ], [ -6.0, -8.0, 0.0 ], [ -2.6, 2.7, 0.0 ], [ 0.0, 0.0, 8.1 ], [ 0.0, 0.0, -1.2] ], - [ [ -12.3, 1.5, 0.0 ], [ -1.5, 7.0, 0.0 ], [ -6.4, -3.5, 0.0 ], [ 1.5, 4.0, 0.0 ], [ 0.0, 0.0, 6.2 ], [ 0.0, 0.0, -2.8] ], - [ [ -10.8, 8.5, 0.0 ], [ 0.6, 5.4, 0.0 ], [ -2.9, 0.0, 0.0 ], [ 0.8, 1.0, 0.0 ], [ 0.0, 0.0, 2.4 ], [ 0.0, 0.0, -0.6] ], - [ [ -10.7, 12.2, 0.0 ], [ -0.3, 3.2, 0.0 ], [ -3.5, -0.3, 0.0 ], [ -0.3, -0.1, 0.0 ], [ 0.0, 0.0, 3.4 ], [ 0.0, 0.0, 0.4] ], - [ [ -11.3, 14.8, 0.0 ], [ -0.9, 2.0, 0.0 ], [ -3.5, -0.3, 0.0 ], [ 0.3, 0.1, 0.0 ], [ 0.0, 0.0, 3.4 ], [ 0.0, 0.0, -0.4] ] ] ), + [ [ 11.3, 13.4, 0.0 ], [ 0.3, -13.4, 0.0 ], [ 9.4, -1.3, 0.0 ], [ -1.0, -5.6, 0.0 ], [ 0.0, 0.0, 7.4 ], [ 0.0, 0.0, 1.5 ] ], + [ [ 9.3, 2.1, 0.0 ], [ -4.4, -8.7, 0.0 ], [ 7.4, -6.1, 0.0 ], [ -3.0, -3.9, 0.0 ], [ 0.0, 0.0, 8.5 ], [ 0.0, 0.0, 0.7 ] ], + [ [ 4.0, -3.6, 0.0 ], [ -6.8, -3.8, 0.0 ], [ 3.7, -9.4, 0.0 ], [ -4.9, -2.4, 0.0 ], [ 0.0, 0.0, 9.0 ], [ 0.0, 0.0, 0.1 ] ], + [ [ -3.4, -5.1, 0.0 ], [ -6.4, 0.6, 0.0 ], [ -2.4, -10.9, 0.0 ], [ -5.0, 0.9, 0.0 ], [ 0.0, 0.0, 8.8 ], [ 0.0, 0.0, -0.5 ] ], + [ [ -8.1, -3.2, 0.0 ], [ -4.4, 3.8, 0.0 ], [ -6.7, -8.3, 0.0 ], [ -2.5, 3.4, 0.0 ], [ 0.0, 0.0, 8.1 ], [ 0.0, 0.0, -1.2 ] ], + [ [ -11.4, 2.3, 0.0 ], [ -1.4, 6.4, 0.0 ], [ -6.9, -4.0, 0.0 ], [ 1.9, 4.2, 0.0 ], [ 0.0, 0.0, 6.2 ], [ 0.0, 0.0, -2.8 ] ], + [ [ -10.7, 8.9, 0.0 ], [ 0.3, 5.0, 0.0 ], [ -2.9, 0.0, 0.0 ], [ 0.9, 1.1, 0.0 ], [ 0.0, 0.0, 2.4 ], [ 0.0, 0.0, -0.6 ] ], + [ [ -10.7, 12.2, 0.0 ], [ -0.3, 3.0, 0.0 ], [ -3.5, -0.3, 0.0 ], [ -0.3, -0.1, 0.0 ], [ 0.0, 0.0, 3.4 ], [ 0.0, 0.0, 0.4 ] ], + [ [ -11.3, 14.8, 0.0 ], [ -0.9, 2.2, 0.0 ], [ -3.5, -0.3, 0.0 ], [ 0.3, 0.1, 0.0 ], [ 0.0, 0.0, 3.4 ], [ 0.0, 0.0, -0.4 ] ] ] ), 'userAnnotationGroups': [ { @@ -284,14 +284,14 @@ class MeshType_3d_stomach1(Scaffold_base): 'Number of elements through wall': 1, # not implemented for > 1 'Unit scale': 1.0, 'Outlet': False, - 'Ostium diameter': 4.0, - 'Ostium length': 3.5, + 'Ostium diameter': 5.0, + 'Ostium length': 5.0, 'Ostium wall thickness': 0.5, 'Ostium inter-vessel distance': 0.0, 'Ostium inter-vessel height': 0.0, 'Use linear through ostium wall': True, 'Vessel end length factor': 1.0, - 'Vessel inner diameter': 1.25, + 'Vessel inner diameter': 2.0, 'Vessel wall thickness': 0.5, 'Vessel angle 1 degrees': 0.0, 'Vessel angle 1 spread degrees': 0.0, From caafed7030a44c4475155978e95c5a3e4d14f27a Mon Sep 17 00:00:00 2001 From: Mabelle Lin Date: Fri, 29 Oct 2021 17:10:03 +1300 Subject: [PATCH 02/22] Create and annotate evenly spaced elememts through stomach wall --- src/scaffoldmaker/annotation/stomach_terms.py | 3 ++ .../meshtypes/meshtype_3d_stomach1.py | 42 +++++++++++++++---- 2 files changed, 36 insertions(+), 9 deletions(-) diff --git a/src/scaffoldmaker/annotation/stomach_terms.py b/src/scaffoldmaker/annotation/stomach_terms.py index b3f3bf48..1b813a80 100644 --- a/src/scaffoldmaker/annotation/stomach_terms.py +++ b/src/scaffoldmaker/annotation/stomach_terms.py @@ -8,6 +8,7 @@ ( "body of stomach", "UBERON:0001161", " FMA:14560", "ILX:0724929"), ( "body on serosa split margin", "None"), ( "cardia of stomach", "UBERON:0001162", " FMA:14561", "ILX:0729096"), + ( "circular muscle layer of stomach", "ILX:0774731"), ( "dorsal stomach", "None"), ( "duodenum", "UBERON:0002114", " FMA:7206", "ILX:0726125"), ( "duodenum on greater curvature", "None"), @@ -24,6 +25,7 @@ ( "junction between fundus and body on greater curvature", "None"), # ( "lesser curvature of stomach", "UBERON:0001163", "FMA: 14572", "ILX:0733753"), ( "limiting ridge on greater curvature", "None"), + ( "longitudinal muscle layer of stomach", "ILX:0772619"), ( "mucosa of stomach", "UBERON:0001199", "FMA:14907", "ILX:0736669"), ( "pylorus", "UBERON:0001166", " FMA:14581", "ILX:0734150"), ( "pyloric antrum", "UBERON:0001165", " FMA:14579", "ILX:0728672"), @@ -34,6 +36,7 @@ ( "serosa split margin", "None"), ( "serosa of ventral stomach", "None"), ( "stomach", "UBERON:0000945", "FMA:7148", "ILX:0736697"), + ( "submucosa of stomach", "UBERON:0001200", "FMA:14908", "ILX:0732950"), ( "ventral stomach", "None"), ] diff --git a/src/scaffoldmaker/meshtypes/meshtype_3d_stomach1.py b/src/scaffoldmaker/meshtypes/meshtype_3d_stomach1.py index 180c6e1a..668109f1 100644 --- a/src/scaffoldmaker/meshtypes/meshtype_3d_stomach1.py +++ b/src/scaffoldmaker/meshtypes/meshtype_3d_stomach1.py @@ -28,7 +28,7 @@ remapEftNodeValueLabelsVersion from scaffoldmaker.utils.geometry import createEllipsePoints from scaffoldmaker.utils.tracksurface import TrackSurface -from scaffoldmaker.utils.zinc_utils import exnodeStringFromNodeValues +from scaffoldmaker.utils.zinc_utils import exnodeStringFromNodeValues, mesh_destroy_elements_and_nodes_by_identifiers from scaffoldmaker.utils import interpolation as interp from scaffoldmaker.utils import matrix from scaffoldmaker.utils import vector @@ -336,6 +336,7 @@ def getDefaultOptions(cls, parameterSetName='Default'): 'Number of elements around duodenum': 12, 'Number of elements between cardia and duodenum': 6, 'Number of elements across cardia': 1, + 'Number of elements through wall': 4, 'Wall thickness': 5.0, 'Limiting ridge': False, 'Gastro-esophagal junction': copy.deepcopy(ostiumOption), @@ -374,6 +375,7 @@ def getOrderedOptionNames(): 'Number of elements around duodenum', 'Number of elements between cardia and duodenum', 'Number of elements across cardia', + 'Number of elements through wall', 'Wall thickness', 'Limiting ridge', 'Gastro-esophagal junction', @@ -441,6 +443,8 @@ def checkOptions(cls, options): options['Number of elements around duodenum'] += 1 if options['Number of elements between cardia and duodenum'] < 2: options['Number of elements between cardia and duodenum'] = 2 + if options['Number of elements through wall'] != (1 or 4): + options['Number of elements through wall'] = 4 if options['Cardia derivative factor'] <= 0.0: options['Cardia derivative factor'] = 0.1 for key in [ @@ -474,7 +478,7 @@ def generateBaseMesh(cls, region, options): elementsCountAroundEso = options['Number of elements around esophagus'] elementsCountAroundDuod = options['Number of elements around duodenum'] elementsAlongCardiaToDuod = options['Number of elements between cardia and duodenum'] - elementsCountThroughWall = 1 + elementsCountThroughWall = options['Number of elements through wall'] wallThickness = options['Wall thickness'] useCrossDerivatives = False useCubicHermiteThroughWall = not (options['Use linear through wall']) @@ -573,6 +577,16 @@ def generateBaseMesh(cls, region, options): [stomachGroup, pylorusGroup], [stomachGroup, duodenumGroup]] + longitudinalMuscleGroup = AnnotationGroup(region, get_stomach_term("longitudinal muscle layer of stomach")) + circularMuscleGroup = AnnotationGroup(region, get_stomach_term("circular muscle layer of stomach")) + submucosaGroup = AnnotationGroup(region, get_stomach_term("submucosa of stomach")) + mucosaGroup = AnnotationGroup(region, get_stomach_term("mucosa of stomach")) + + annotationGroupsThroughWall = [[mucosaGroup], + [submucosaGroup], + [circularMuscleGroup], + [longitudinalMuscleGroup]] + # annotation fiducial points markerGroup = findOrCreateFieldGroup(fm, "marker") markerName = findOrCreateFieldStoredString(fm, name="marker_name") @@ -1941,6 +1955,7 @@ def generateBaseMesh(cls, region, options): nodeIdentifier += 1 # Create element + fundusMucosaElementIdentifiers = [] elementIdxMat = [] n = 0 for n2 in range(elementsAlongEsophagus): @@ -2029,9 +2044,11 @@ def generateBaseMesh(cls, region, options): result2 = element.setNodesByIdentifier(eft1, nodeIdentifiers) if scaleFactors: result3 = element.setScaleFactors(eft1, scaleFactors) + if limitingRidge and elementsCountThroughWall > 1 and e3 == 0: + fundusMucosaElementIdentifiers.append(elementIdentifier) elementIdxAround.append(elementIdentifier) elementIdentifier += 1 - annotationGroups = annotationGroupsAlong[e2] + annotationGroups = annotationGroupsAlong[e2] + annotationGroupsThroughWall[e3] if annotationGroups: allAnnotationGroups = mergeAnnotationGroups(allAnnotationGroups, annotationGroups) for annotationGroup in annotationGroups: @@ -2117,8 +2134,10 @@ def generateBaseMesh(cls, region, options): if scaleFactors: result3 = element.setScaleFactors(eft1, scaleFactors) elementIdxAround.append(elementIdentifier) + if limitingRidge and elementsCountThroughWall> 1 and e3 == 0: + fundusMucosaElementIdentifiers.append(elementIdentifier) elementIdentifier += 1 - annotationGroups = annotationGroupsAlong[e2] + annotationGroups = annotationGroupsAlong[e2] + annotationGroupsThroughWall[e3] if annotationGroups: allAnnotationGroups = mergeAnnotationGroups(allAnnotationGroups, annotationGroups) for annotationGroup in annotationGroups: @@ -2149,9 +2168,11 @@ def generateBaseMesh(cls, region, options): result2 = element.setNodesByIdentifier(eft1, nodeIdentifiers) if scaleFactors: result3 = element.setScaleFactors(eft1, scaleFactors) + if limitingRidge and elementsCountThroughWall> 1 and e3 == 0: + fundusMucosaElementIdentifiers.append(elementIdentifier) elementIdxAround.append(elementIdentifier) elementIdentifier += 1 - annotationGroups = annotationGroupsAlong[e2] + annotationGroups = annotationGroupsAlong[e2] + annotationGroupsThroughWall[e3] if annotationGroups: allAnnotationGroups = mergeAnnotationGroups(allAnnotationGroups, annotationGroups) for annotationGroup in annotationGroups: @@ -2212,7 +2233,7 @@ def generateBaseMesh(cls, region, options): fundusBodyJunctionElementIdentifier = elementIdentifier elementIdxAround.append(elementIdentifier) elementIdentifier += 1 - annotationGroups = annotationGroupsAlong[e2] + annotationGroups = annotationGroupsAlong[e2] + annotationGroupsThroughWall[e3] if annotationGroups: allAnnotationGroups = mergeAnnotationGroups(allAnnotationGroups, annotationGroups) for annotationGroup in annotationGroups: @@ -2280,7 +2301,7 @@ def generateBaseMesh(cls, region, options): result3 = element.setScaleFactors(eft1, scaleFactors) elementIdxAround.append(elementIdentifier) elementIdentifier += 1 - annotationGroups = annotationGroupsAlong[e2] + annotationGroups = annotationGroupsAlong[e2] + annotationGroupsThroughWall[e3] if annotationGroups: allAnnotationGroups = mergeAnnotationGroups(allAnnotationGroups, annotationGroups) for annotationGroup in annotationGroups: @@ -2306,7 +2327,7 @@ def generateBaseMesh(cls, region, options): result = element.setNodesByIdentifier(eftStandard, nodeIdentifiers) elementIdxAround.append(elementIdentifier) elementIdentifier = elementIdentifier + 1 - annotationGroups = annotationGroupsAlong[e2] + annotationGroups = annotationGroupsAlong[e2] + annotationGroupsThroughWall[e3] if annotationGroups: allAnnotationGroups = mergeAnnotationGroups(allAnnotationGroups, annotationGroups) for annotationGroup in annotationGroups: @@ -2369,7 +2390,7 @@ def generateBaseMesh(cls, region, options): result3 = element.setScaleFactors(eft1, scaleFactors) elementIdxAround.append(elementIdentifier) elementIdentifier += 1 - annotationGroups = annotationGroupsAlong[e2] + annotationGroups = annotationGroupsAlong[e2] + annotationGroupsThroughWall[e3] if annotationGroups: allAnnotationGroups = mergeAnnotationGroups(allAnnotationGroups, annotationGroups) for annotationGroup in annotationGroups: @@ -2454,6 +2475,9 @@ def generateBaseMesh(cls, region, options): nodeIdentifier = nextNodeIdentifier + # delete mucosa layer in fundus when there is a limiting ridge + mesh_destroy_elements_and_nodes_by_identifiers(mesh, fundusMucosaElementIdentifiers) + # annotation fiducial points for embedding in whole body GEJLCGroup = findOrCreateAnnotationGroupForTerm(allAnnotationGroups, region, get_stomach_term("gastro-esophagal junction on lesser curvature")) GEJLCElement = mesh.findElementByIdentifier(stomachStartElement - elementsAroundHalfEso - 1) From d51be451bbe42caf3d9088fcdaacb599957b8385 Mon Sep 17 00:00:00 2001 From: Mabelle Lin Date: Fri, 29 Oct 2021 17:17:12 +1300 Subject: [PATCH 03/22] Remove mucosa group in surface annotations --- .../meshtypes/meshtype_3d_stomach1.py | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/src/scaffoldmaker/meshtypes/meshtype_3d_stomach1.py b/src/scaffoldmaker/meshtypes/meshtype_3d_stomach1.py index 668109f1..2e446074 100644 --- a/src/scaffoldmaker/meshtypes/meshtype_3d_stomach1.py +++ b/src/scaffoldmaker/meshtypes/meshtype_3d_stomach1.py @@ -582,10 +582,13 @@ def generateBaseMesh(cls, region, options): submucosaGroup = AnnotationGroup(region, get_stomach_term("submucosa of stomach")) mucosaGroup = AnnotationGroup(region, get_stomach_term("mucosa of stomach")) - annotationGroupsThroughWall = [[mucosaGroup], - [submucosaGroup], - [circularMuscleGroup], - [longitudinalMuscleGroup]] + if elementsCountThroughWall == 1: + annotationGroupsThroughWall = [[]] + else: + annotationGroupsThroughWall = [[mucosaGroup], + [submucosaGroup], + [circularMuscleGroup], + [longitudinalMuscleGroup]] # annotation fiducial points markerGroup = findOrCreateFieldGroup(fm, "marker") @@ -2755,8 +2758,6 @@ def defineFaceAnnotations(cls, region, options, annotationGroups): serosaGroup = findOrCreateAnnotationGroupForTerm(annotationGroups, region, get_stomach_term("serosa of stomach")) - mucosaGroup = findOrCreateAnnotationGroupForTerm(annotationGroups, region, - get_stomach_term("mucosa of stomach")) outerSplitMarginGroup = findOrCreateAnnotationGroupForTerm(annotationGroups, region, get_stomach_term("serosa split margin")) @@ -2792,11 +2793,9 @@ def defineFaceAnnotations(cls, region, options, annotationGroups): is_stomach = stomachGroup.getGroup() is_exterior = fm.createFieldIsExterior() is_exterior_face_outer = fm.createFieldAnd(is_exterior, fm.createFieldIsOnFace(Element.FACE_TYPE_XI3_1)) - is_exterior_face_inner = fm.createFieldAnd(is_exterior, fm.createFieldIsOnFace(Element.FACE_TYPE_XI3_0)) is_serosa = fm.createFieldAnd(is_stomach, is_exterior_face_outer) - is_mucosa = fm.createFieldAnd(is_stomach, is_exterior_face_inner) serosaGroup.getMeshGroup(mesh2d).addElementsConditional(is_serosa) - mucosaGroup.getMeshGroup(mesh2d).addElementsConditional(is_mucosa) + is_dorsal = dorsalGroup.getGroup() is_ventral = ventralGroup.getGroup() is_dorsalSerosa = fm.createFieldAnd(is_dorsal, is_serosa) From b23d82a27d701525a001460b8a38a31e3b417e7e Mon Sep 17 00:00:00 2001 From: Mabelle Lin Date: Fri, 29 Oct 2021 17:54:55 +1300 Subject: [PATCH 04/22] Allow varying thickness across elements through stomach wall --- .../meshtypes/meshtype_3d_stomach1.py | 25 +++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/src/scaffoldmaker/meshtypes/meshtype_3d_stomach1.py b/src/scaffoldmaker/meshtypes/meshtype_3d_stomach1.py index 2e446074..eb4f5ad7 100644 --- a/src/scaffoldmaker/meshtypes/meshtype_3d_stomach1.py +++ b/src/scaffoldmaker/meshtypes/meshtype_3d_stomach1.py @@ -338,6 +338,10 @@ def getDefaultOptions(cls, parameterSetName='Default'): 'Number of elements across cardia': 1, 'Number of elements through wall': 4, 'Wall thickness': 5.0, + 'Mucosa relative thickness': 0.65, + 'Submucosa relative thickness': 0.12, + 'Circular muscle layer relative thickness': 0.18, + 'Longitudinal muscle layer relative thickness': 0.05, 'Limiting ridge': False, 'Gastro-esophagal junction': copy.deepcopy(ostiumOption), 'Gastro-esophagal junction position along factor': 0.35, @@ -377,6 +381,10 @@ def getOrderedOptionNames(): 'Number of elements across cardia', 'Number of elements through wall', 'Wall thickness', + 'Mucosa relative thickness', + 'Submucosa relative thickness', + 'Circular muscle layer relative thickness', + 'Longitudinal muscle layer relative thickness', 'Limiting ridge', 'Gastro-esophagal junction', 'Gastro-esophagal junction position along factor', @@ -480,6 +488,10 @@ def generateBaseMesh(cls, region, options): elementsAlongCardiaToDuod = options['Number of elements between cardia and duodenum'] elementsCountThroughWall = options['Number of elements through wall'] wallThickness = options['Wall thickness'] + mucosaRelThickness = options['Mucosa relative thickness'] + submucosaRelThickness = options['Submucosa relative thickness'] + circularRelThickness = options['Circular muscle layer relative thickness'] + longitudinalRelThickness = options['Longitudinal muscle layer relative thickness'] useCrossDerivatives = False useCubicHermiteThroughWall = not (options['Use linear through wall']) @@ -1895,10 +1907,19 @@ def generateBaseMesh(cls, region, options): nodeIdx = stomachStartNode idxMat = [] + if elementsCountThroughWall > 1: + thicknessProportions = [0.0, mucosaRelThickness, submucosaRelThickness, circularRelThickness, + longitudinalRelThickness, longitudinalRelThickness] + xi3List = [] + xi3 = 0.0 + for i in range(len(thicknessProportions) - 1): + xi3 += thicknessProportions[i] + xi3List.append(xi3) + for n2 in range(elementsCountAlong + 1): idxThroughWall = [] for n3 in range(elementsCountThroughWall + 1): - xi3 = 1 / elementsCountThroughWall * n3 + xi3 = xi3List[n3] if elementsCountThroughWall > 1 else 1.0/elementsCountThroughWall * n3 idxAround = [] for n1 in range(len(xOuter[n2])): # Coordinates @@ -1920,7 +1941,7 @@ def generateBaseMesh(cls, region, options): d2List.append(d2) # d3 - d3 = [c * wallThickness / elementsCountThroughWall for c in norm] + d3 = [c * wallThickness * (thicknessProportions[n3+1] if elementsCountThroughWall > 1 else 1.0) for c in norm] d3List.append(d3) idxAround.append(nodeIdx) From 2186bdbb3a63671878ac02cfd639f0cb3656b3f1 Mon Sep 17 00:00:00 2001 From: Mabelle Lin Date: Sat, 30 Oct 2021 18:04:33 +1300 Subject: [PATCH 05/22] Allow uniform elements through ostium wall --- .../meshtypes/meshtype_3d_ostium1.py | 119 +++--- src/scaffoldmaker/utils/annulusmesh.py | 371 +++++++++--------- 2 files changed, 248 insertions(+), 242 deletions(-) diff --git a/src/scaffoldmaker/meshtypes/meshtype_3d_ostium1.py b/src/scaffoldmaker/meshtypes/meshtype_3d_ostium1.py index 5780136f..e3fb2e22 100644 --- a/src/scaffoldmaker/meshtypes/meshtype_3d_ostium1.py +++ b/src/scaffoldmaker/meshtypes/meshtype_3d_ostium1.py @@ -32,7 +32,7 @@ def getDefaultOptions(parameterSetName='Default'): 'Number of elements across common' : 2, 'Number of elements around ostium' : 10, 'Number of elements along' : 1, - 'Number of elements through wall' : 1, # not implemented for > 1 + 'Number of elements through wall' : 1, 'Unit scale' : 1.0, 'Outlet' : False, 'Ostium diameter' : 1.0, @@ -62,7 +62,7 @@ def getOrderedOptionNames(): 'Number of elements across common', 'Number of elements around ostium', 'Number of elements along', - 'Number of elements through wall', # not implemented for > 1 + 'Number of elements through wall', 'Unit scale', 'Outlet', 'Ostium diameter', @@ -109,8 +109,6 @@ def checkOptions(options): if (vesselsCount > 1) and (options['Number of elements around ostium'] % 2): options['Number of elements around ostium'] += 1 dependentChanges = True # because can happen by changing number of vessels - if options['Number of elements through wall'] > 1: - options['Number of elements through wall'] = 1 for key in [ 'Unit scale', 'Ostium length', @@ -296,10 +294,10 @@ def generateOstiumMesh(region, options, trackSurface, centrePosition, axis1, sta ocd3.append(d3) # coordinates around ostium - ox = [ [], [] ] - od1 = [ [], [] ] - od2 = [ [], [] ] - od3 = [ [], [] ] + ox = [[] for n3 in range(elementsCountThroughWall + 1)] + od1 = [[] for n3 in range(elementsCountThroughWall + 1)] + od2 = [[] for n3 in range(elementsCountThroughWall + 1)] + od3 = [[] for n3 in range(elementsCountThroughWall + 1)] oPositions = [] for n1 in range(elementsCountAroundOstium): elementLength = elementLengthAroundOstiumEnd @@ -337,18 +335,16 @@ def generateOstiumMesh(region, options, trackSurface, centrePosition, axis1, sta opd1 = vector.setMagnitude([ -d for d in pd1 ], elementLengthAroundOstiumEnd) opd2 = vector.setMagnitude(pd2, elementLengthAroundOstiumEnd) # smoothed later opd3 = vector.setMagnitude(pd3, ostiumWallThickness) - # set inner and outer coordinates (use copy to avoid references to same list later) - ox [0].append([ (opx[c] - opd3[c]) for c in range(3) ]) - od1[0].append(copy.copy(opd1)) - od2[0].append(copy.copy(opd2)) - ox [1].append(opx) - od1[1].append(opd1) - od2[1].append(opd2) - if useCubicHermiteThroughOstiumWall: - od3[0].append(copy.copy(opd3)) - od3[1].append(opd3) + # set coordinates through wall (use copy to avoid references to same list later) + for n3 in range(elementsCountThroughWall + 1): + xi3 = 1 - 1 / elementsCountThroughWall * n3 + ox[n3].append([(opx[c] - opd3[c] * xi3) for c in range(3)]) + od1[n3].append(copy.copy(opd1)) + od2[n3].append(copy.copy(opd2)) + if useCubicHermiteThroughOstiumWall: + od3[n3].append([opd3[c] * 1 / elementsCountThroughWall for c in range(3)]) distance += elementLength - for n3 in range(2): + for n3 in range(elementsCountThroughWall + 1): od1[n3] = interp.smoothCubicHermiteDerivativesLoop(ox[n3], od1[n3], fixAllDirections = True) xx = [] @@ -364,10 +360,10 @@ def generateOstiumMesh(region, options, trackSurface, centrePosition, axis1, sta nodesCountFreeEnd = elementsCountsAroundVessels[0] + 1 - elementsCountAcross oinc = 0 if (vesselsCount <= 2) else elementsCountAroundMid//(vesselsCount - 2) for iv in range(vesselsCount - 1): - xx .append([ None, None ]) - xd1.append([ None, None ]) - xd2.append([ None, None ]) - xd3.append([ None, None ]) + xx .append([ None for n3 in range(elementsCountThroughWall + 1)]) + xd1.append([ None for n3 in range(elementsCountThroughWall + 1)]) + xd2.append([ None for n3 in range(elementsCountThroughWall + 1)]) + xd3.append([ None for n3 in range(elementsCountThroughWall + 1)]) oa = elementsCountAroundMid - iv*oinc ob = elementsCountAroundMid + nodesCountFreeEnd - 1 + iv*oinc nx = [ ox[1][oa], ox[1][ob] ] @@ -388,22 +384,21 @@ def generateOstiumMesh(region, options, trackSurface, centrePosition, axis1, sta px, pd2, pe, pxi = interp.sampleCubicHermiteCurves(nx, nd2, elementsCountAcross)[0:4] pd1 = interp.interpolateSampleLinear(nd1, pe, pxi) pd3 = [ vector.setMagnitude(vector.crossproduct3(pd1[n2], pd2[n2]), commonOstiumWallThickness) for n2 in range(elementsCountAcross + 1) ] - lx = [ ([ (px[n2][c] - pd3[n2][c]) for c in range(3) ]) for n2 in range(elementsCountAcross + 1) ] - ld2 = interp.smoothCubicHermiteDerivativesLine(lx, pd2, fixAllDirections = True) - xx [iv][0] = lx [1:elementsCountAcross] - xd1[iv][0] = copy.deepcopy(pd1[1:elementsCountAcross]) # to be smoothed later - xd2[iv][0] = ld2[1:elementsCountAcross] - xx [iv][1] = px [1:elementsCountAcross] - xd1[iv][1] = pd1[1:elementsCountAcross] # to be smoothed later - xd2[iv][1] = pd2[1:elementsCountAcross] + for n3 in range(elementsCountThroughWall + 1): + xi3 = 1 - 1/elementsCountThroughWall * n3 + lx = [([(px[n2][c] - xi3 * pd3[n2][c]) for c in range(3)]) for n2 in range(elementsCountAcross + 1)] + ld2 = interp.smoothCubicHermiteDerivativesLine(lx, pd2, fixAllDirections=True) + xx [iv][n3] = lx [1:elementsCountAcross] + xd1[iv][n3] = copy.deepcopy(pd1[1:elementsCountAcross]) # to be smoothed later + xd2[iv][n3] = ld2[1:elementsCountAcross] + # set smoothed d2 on ostium circumference + od2[n3][oa] = [-d for d in ld2[0]] + od2[n3][ob] = ld2[-1] + + pd3PerElement = [vector.setMagnitude(vector.crossproduct3(pd1[n2], pd2[n2]), commonOstiumWallThickness/elementsCountThroughWall) for n2 in range(elementsCountAcross + 1)] if useCubicHermiteThroughOstiumWall: - xd3[iv][0] = copy.deepcopy(pd3[1:elementsCountAcross]) - xd3[iv][1] = pd3[1:elementsCountAcross] - # set smoothed d2 on ostium circumference - od2[0][oa] = [ -d for d in ld2[0] ] - od2[1][oa] = [ -d for d in pd2[0] ] - od2[0][ob] = ld2[-1] - od2[1][ob] = pd2[-1] + for n3 in range(elementsCountThroughWall + 1): + xd3[iv][n3] = copy.deepcopy(pd3PerElement[1:elementsCountAcross]) # get positions of vessel end centres and rings vcx = [] @@ -432,8 +427,8 @@ def generateOstiumMesh(region, options, trackSurface, centrePosition, axis1, sta vod1.append([]) vod2.append([]) vod3.append([]) - for n3 in range(2): - radius = vesselInnerRadius if (n3 == 0) else vesselOuterRadius + for n3 in range(elementsCountThroughWall + 1): + radius = vesselInnerRadius + n3 * vesselWallThickness / elementsCountThroughWall vAxis1 = vector.setMagnitude(vd1, radius) vAxis2 = vector.setMagnitude(vd2, radius) if vesselsCount == 1: @@ -447,7 +442,7 @@ def generateOstiumMesh(region, options, trackSurface, centrePosition, axis1, sta vod1[-1].append(pd1) vod2[-1].append([ vd3 ]*elementsCountAroundVessel) if useCubicHermiteThroughVesselWall: - vod3[-1].append([ vector.setMagnitude(vector.crossproduct3(d1, vd3), vesselWallThickness) for d1 in pd1 ]) + vod3[-1].append([ vector.setMagnitude(vector.crossproduct3(d1, vd3), vesselWallThickness/elementsCountThroughWall) for d1 in pd1 ]) # calculate common ostium vessel node derivatives map mvPointsx = [ None ]*vesselsCount @@ -470,7 +465,7 @@ def generateOstiumMesh(region, options, trackSurface, centrePosition, axis1, sta mvPointsd2[v] = [] mvPointsd3[v] = [] if useCubicHermiteThroughOstiumWall else None mvDerivativesMap[v] = [] - for n3 in range(2): + for n3 in range(elementsCountThroughWall + 1): mvPointsd1[v].append([]) mvPointsd2[v].append([]) mvPointsx [v].append([]) @@ -557,7 +552,7 @@ def generateOstiumMesh(region, options, trackSurface, centrePosition, axis1, sta # calculate derivative 2 around free sides of inlets to fit vessel derivatives for v in range(vesselsCount): - for n3 in [1]: # was range(2), now using curvature for inside: + for n3 in [elementsCountThroughWall]: # was range(2), now using curvature for inside: #print('v',v,'n3',n3,'elementsAround',elementsCountsAroundVessels[v]) #print('mvPointsx [v][n3]', mvPointsx [v][n3]) #print('mvPointsd1[v][n3]', mvPointsd1[v][n3]) @@ -590,11 +585,11 @@ def generateOstiumMesh(region, options, trackSurface, centrePosition, axis1, sta # calculate inner d2 derivatives around ostium from outer using track surface curvature factor = 1.0 for n1 in range(elementsCountAroundOstium): - trackDirection = vector.normalise(od2[1][n1]) - trackDistance = factor*vector.magnitude(od2[1][n1]) - tx = [ None, ox[1][n1], None ] - td1 = [ None, vector.setMagnitude(od2[1][n1], trackDistance), None ] - td2 = [ None, vector.setMagnitude(od1[1][n1], -trackDistance), None ] + trackDirection = vector.normalise(od2[elementsCountThroughWall][n1]) + trackDistance = factor * vector.magnitude(od2[elementsCountThroughWall][n1]) + tx = [None, ox[elementsCountThroughWall][n1], None] + td1 = [None, vector.setMagnitude(od2[elementsCountThroughWall][n1], trackDistance), None] + td2 = [None, vector.setMagnitude(od1[elementsCountThroughWall][n1], -trackDistance), None] positionBackward = trackSurface.trackVector(oPositions[n1], trackDirection, -trackDistance) tx[0], d1, d2 = trackSurface.evaluateCoordinates(positionBackward, derivatives=True) sd1, sd2, sd3 = calculate_surface_axes(d1, d2, trackDirection) @@ -605,10 +600,12 @@ def generateOstiumMesh(region, options, trackSurface, centrePosition, axis1, sta sd1, sd2, sd3 = calculate_surface_axes(d1, d2, trackDirection) td1[2] = vector.setMagnitude(sd1, trackDistance) td2[2] = vector.setMagnitude(sd2, trackDistance) - newd2 = interp.projectHermiteCurvesThroughWall(tx, td1, td2, 1, -ostiumWallThickness)[1] - # assign components to set in all lists: - for c in range(3): - od2[0][n1][c] = newd2[c]/factor + for n3 in range(elementsCountThroughWall): + xi3 = 1 - 1 / elementsCountThroughWall * n3 + newd2 = interp.projectHermiteCurvesThroughWall(tx, td1, td2, 1, -ostiumWallThickness * xi3)[1] + # assign components to set in all lists: + for c in range(3): + od2[n3][n1][c] = newd2[c] / factor #for n in [ 0, 1, 2 ]: # node = nodes.createNode(nodeIdentifier, nodetemplateLinearS3) # cache.setNode(node) @@ -620,7 +617,7 @@ def generateOstiumMesh(region, options, trackSurface, centrePosition, axis1, sta if isOutlet: # reverse directions of d1 and d2 on vessels and ostium base for c in range(3): - for n3 in range(2): + for n3 in range(elementsCountThroughWall + 1): for n1 in range(elementsCountAroundOstium): od1[n3][n1][c] = -od1[n3][n1][c] od2[n3][n1][c] = -od2[n3][n1][c] @@ -640,7 +637,7 @@ def generateOstiumMesh(region, options, trackSurface, centrePosition, axis1, sta ############## oNodeId = [] - for n3 in range(2): + for n3 in range(elementsCountThroughWall + 1): oNodeId.append([]) for n1 in range(elementsCountAroundOstium): node = nodes.createNode(nodeIdentifier, nodetemplate if useCubicHermiteThroughOstiumWall else nodetemplateLinearS3) @@ -656,7 +653,7 @@ def generateOstiumMesh(region, options, trackSurface, centrePosition, axis1, sta xNodeId = [] for iv in range(vesselsCount - 1): xNodeId.append([]) - for n3 in range(2): + for n3 in range(elementsCountThroughWall + 1): xNodeId[iv].append([]) for n2 in range(elementsCountAcross - 1): node = nodes.createNode(nodeIdentifier, nodetemplate if useCubicHermiteThroughOstiumWall else nodetemplateLinearS3) @@ -677,7 +674,7 @@ def generateOstiumMesh(region, options, trackSurface, centrePosition, axis1, sta # coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS2, 1, vcd2[v]) # coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS3, 1, vcd3[v]) # nodeIdentifier += 1 - # for n3 in range(2): + # for n3 in range(elementsCountThroughWall + 1): # for n1 in range(elementsCountsAroundVessels[v]): # node = nodes.createNode(nodeIdentifier, nodetemplate if useCubicHermiteThroughVesselWall else nodetemplateLinearS3) # cache.setNode(node) @@ -696,10 +693,10 @@ def generateOstiumMesh(region, options, trackSurface, centrePosition, axis1, sta mvNodeId[v] = oNodeId else: iv = max(0, v - 1) - mvNodeId[v] = [ None, None ] + mvNodeId[v] = [ None for n3 in range(elementsCountThroughWall + 1)] oa = elementsCountAroundMid - iv*oinc ob = elementsCountAroundMid + nodesCountFreeEnd - 1 + iv*oinc - for n3 in range(2): + for n3 in range(elementsCountThroughWall + 1): if v == 0: # first end vessel mvNodeId[v][n3] = oNodeId[n3][oa:ob + 1] + (list(reversed(xNodeId[iv][n3])) if (v == 0) else xNodeId[iv][n3]) elif v == (vesselsCount - 1): # last end vessels @@ -708,7 +705,7 @@ def generateOstiumMesh(region, options, trackSurface, centrePosition, axis1, sta mvNodeId[v][n3] = oNodeId[n3][oa - oinc:oa + 1] + xNodeId[iv][n3] + oNodeId[n3][ob:ob + oinc + 1] + list(reversed(xNodeId[iv + 1][n3])) ################# - # Create elementss + # Create elements ################# tricubichermite = eftfactory_tricubichermite(mesh, useCrossDerivatives) @@ -738,11 +735,11 @@ def generateOstiumMesh(region, options, trackSurface, centrePosition, axis1, sta for px in [ startPointsx, startPointsd1, startPointsd2, startPointsd3, startNodeId, startDerivativesMap, \ endPointsx, endPointsd1, endPointsd2, endPointsd3, endNodeId, endDerivativesMap ]: if px: - for n3 in range(2): + for n3 in range(elementsCountThroughWall + 1): px[n3] = [ px[n3][0] ] + px[n3][len(px[n3]) - 1:0:-1] if vesselsCount > 1: # must switch in and out xi1 maps around corners in startDerivativesMap - for n3 in range(2): + for n3 in range(elementsCountThroughWall + 1): for n1 in range(elementsCountsAroundVessels[v]): derivativesMap = startDerivativesMap[n3][n1] if len(derivativesMap) == 4: diff --git a/src/scaffoldmaker/utils/annulusmesh.py b/src/scaffoldmaker/utils/annulusmesh.py index d3271432..5c52a1cd 100644 --- a/src/scaffoldmaker/utils/annulusmesh.py +++ b/src/scaffoldmaker/utils/annulusmesh.py @@ -136,10 +136,9 @@ def createAnnulusMesh3d(nodes, mesh, nextNodeIdentifier, nextElementIdentifier, rowLinearXi3 = [ startLinearXi3 ] + [ midLinearXi3 ]*(elementsCountRadial - 1) + [ endLinearXi3 ] assert (not useCrossDerivatives) or ((not startDerivativesMap) and (not endDerivativesMap)), \ 'createAnnulusMesh3d: Cannot use cross derivatives with derivatives map' - elementsCountWall = 1 - nodesCountWall = elementsCountWall + 1 - assert (len(startPointsx) == nodesCountWall) and (len(startPointsd1) == nodesCountWall) and (len(startPointsd2) == nodesCountWall) and \ - (startLinearXi3 or (len(startPointsd3) == nodesCountWall)) and \ + nodesCountWall = len(startPointsx) + assert (len(startPointsd1) == nodesCountWall) and (len(startPointsd2) == nodesCountWall) and \ + (startLinearXi3 or (len(startPointsd3) == nodesCountWall)) and \ (len(endPointsx) == nodesCountWall) and (len(endPointsd1) == nodesCountWall) and (len(endPointsd2) == nodesCountWall) and \ (endLinearXi3 or (len(endPointsd3) == nodesCountWall)) and \ ((startNodeId is None) or (len(startNodeId) == nodesCountWall)) and \ @@ -177,11 +176,12 @@ def createAnnulusMesh3d(nodes, mesh, nextNodeIdentifier, nextElementIdentifier, coordinates = findOrCreateFieldCoordinates(fm) # Build arrays of points from start to end - px = [ [], [] ] - pd1 = [ [], [] ] - pd2 = [ [], [] ] - pd3 = [ [], [] ] - for n3 in range(2): + px = [[] for n3 in range(nodesCountWall)] + pd1 = [[] for n3 in range(nodesCountWall)] + pd2 = [[] for n3 in range(nodesCountWall)] + pd3 = [[] for n3 in range(nodesCountWall)] + + for n3 in range(nodesCountWall): px [n3] = [ startPointsx [n3], endPointsx [n3] ] pd1[n3] = [ startPointsd1[n3], endPointsd1[n3] ] pd2[n3] = [ startPointsd2[n3], endPointsd2[n3] ] @@ -189,12 +189,12 @@ def createAnnulusMesh3d(nodes, mesh, nextNodeIdentifier, nextElementIdentifier, endPointsd3[n3] if (endPointsd3 is not None) else None ] if rescaleStartDerivatives: - scaleFactorMapStart = [ [] for n2 in range(2) ] + scaleFactorMapStart = [ [] for n3 in range(nodesCountWall) ] if rescaleEndDerivatives: - scaleFactorMapEnd = [ [] for n2 in range(2) ] + scaleFactorMapEnd = [ [] for n3 in range(nodesCountWall) ] # following code adds in-between points, but also handles rescaling for 1 radial element - for n3 in range(2): + for n3 in range(nodesCountWall): for n2 in range(1, elementsCountRadial): px [n3].insert(n2, [ None ]*nodesCountAround) pd1[n3].insert(n2, [ None ]*nodesCountAround) @@ -202,17 +202,17 @@ def createAnnulusMesh3d(nodes, mesh, nextNodeIdentifier, nextElementIdentifier, pd3[n3].insert(n2, None if midLinearXi3 else [ None ]*nodesCountAround) # compute on outside / n3 = 1, then map to inside using thickness thicknesses = [] - thicknesses.append([ vector.magnitude([ (startPointsx[1][n1][c] - startPointsx[0][n1][c]) for c in range(3) ]) for n1 in range(nodesCountAround) ]) + thicknesses.append([ vector.magnitude([ (startPointsx[nodesCountWall - 1][n1][c] - startPointsx[0][n1][c]) for c in range(3) ]) for n1 in range(nodesCountAround) ]) if maxStartThickness: for n1 in range(nodesCountAround): thicknesses[0][n1] = min(thicknesses[0][n1], maxStartThickness) for n2 in range(1, elementsCountRadial): thicknesses.append([ None ]*nodesCountAround) - thicknesses.append([ vector.magnitude([ (endPointsx[1][n1][c] - endPointsx[0][n1][c]) for c in range(3) ]) for n1 in range(nodesCountAround) ]) + thicknesses.append([ vector.magnitude([ (endPointsx[nodesCountWall - 1][n1][c] - endPointsx[0][n1][c]) for c in range(3) ]) for n1 in range(nodesCountAround) ]) if maxEndThickness: for n1 in range(nodesCountAround): thicknesses[-1][n1] = min(thicknesses[-1][n1], maxEndThickness) - n3 == 1 + n3 = nodesCountWall - 1 for n1 in range(nodesCountAround): ax = startPointsx[n3][n1] ad1, ad2 = getMappedD1D2([ startPointsd1[n3][n1], startPointsd2[n3][n1] ] + ([ startPointsd3[n3][n1] ] if startPointsd3 else []), @@ -270,39 +270,41 @@ def createAnnulusMesh3d(nodes, mesh, nextNodeIdentifier, nextElementIdentifier, # now get inner positions from normal and thickness, derivatives from curvature for n2 in range(1, elementsCountRadial): # first smooth derivative 1 around outer loop - pd1[1][n2] = interp.smoothCubicHermiteDerivativesLoop(px[1][n2], pd1[1][n2], magnitudeScalingMode = interp.DerivativeScalingMode.HARMONIC_MEAN) - - for n1 in range(nodesCountAround): - normal = vector.normalise(vector.crossproduct3(pd1[1][n2][n1], pd2[1][n2][n1])) - thickness = thicknesses[n2][n1] - d3 = [ d*thickness for d in normal ] - px [0][n2][n1] = [ (px [1][n2][n1][c] - d3[c]) for c in range(3) ] - # calculate inner d1 from curvature around - n1m = n1 - 1 - n1p = (n1 + 1)%nodesCountAround - curvature = 0.5*( - interp.getCubicHermiteCurvature(px[1][n2][n1m], pd1[1][n2][n1m], px[1][n2][n1 ], pd1[1][n2][n1 ], normal, 1.0) + - interp.getCubicHermiteCurvature(px[1][n2][n1 ], pd1[1][n2][n1 ], px[1][n2][n1p], pd1[1][n2][n1p], normal, 0.0)) - factor = 1.0 + curvature*thickness - pd1[0][n2][n1] = [ factor*d for d in pd1[1][n2][n1] ] - # calculate inner d2 from curvature radially - n2m = n2 - 1 - n2p = n2 + 1 - curvature = 0.5*( - interp.getCubicHermiteCurvature(px[1][n2m][n1], pd2[1][n2m][n1], px[1][n2 ][n1], pd2[1][n2 ][n1], normal, 1.0) + - interp.getCubicHermiteCurvature(px[1][n2 ][n1], pd2[1][n2 ][n1], px[1][n2p][n1], pd2[1][n2p][n1], normal, 0.0)) - factor = 1.0 + curvature*thickness - pd2[0][n2][n1] = [ factor*d for d in pd2[1][n2][n1] ] - d2Scaled = [factor*d for d in pd2[1][n2][n1]] - if vector.dotproduct(vector.normalise(pd2[1][n2][n1]), vector.normalise(d2Scaled)) == -1: - pd2[0][n2][n1] = [-factor * d for d in pd2[1][n2][n1]] - if not midLinearXi3: - pd3[0][n2][n1] = pd3[1][n2][n1] = d3 - - # smooth derivative 1 around inner loop - pd1[0][n2] = interp.smoothCubicHermiteDerivativesLoop(px[0][n2], pd1[0][n2], magnitudeScalingMode = interp.DerivativeScalingMode.HARMONIC_MEAN) - - for n3 in range(0, 1): # was (0, nodesCountWall) + pd1[-1][n2] = interp.smoothCubicHermiteDerivativesLoop(px[-1][n2], pd1[-1][n2], magnitudeScalingMode=interp.DerivativeScalingMode.HARMONIC_MEAN) + + for n3 in range(0, nodesCountWall - 1): + xi3 = 1 - 1 / (nodesCountWall - 1) * n3 + for n1 in range(nodesCountAround): + normal = vector.normalise(vector.crossproduct3(pd1[-1][n2][n1], pd2[-1][n2][n1])) + thickness = thicknesses[n2][n1] * xi3 + d3 = [d * thickness for d in normal] + px[n3][n2][n1] = [(px[-1][n2][n1][c] - d3[c]) for c in range(3)] + # calculate inner d1 from curvature around + n1m = n1 - 1 + n1p = (n1 + 1) % nodesCountAround + curvature = 0.5 * ( + interp.getCubicHermiteCurvature(px[-1][n2][n1m], pd1[-1][n2][n1m], px[-1][n2][n1], pd1[-1][n2][n1], normal, 1.0) + + interp.getCubicHermiteCurvature(px[-1][n2][n1], pd1[-1][n2][n1], px[-1][n2][n1p], pd1[-1][n2][n1p], normal, 0.0)) + factor = 1.0 + curvature * thickness + pd1[n3][n2][n1] = [factor * d for d in pd1[-1][n2][n1]] + # calculate inner d2 from curvature radially + n2m = n2 - 1 + n2p = n2 + 1 + curvature = 0.5 * ( + interp.getCubicHermiteCurvature(px[-1][n2m][n1], pd2[-1][n2m][n1], px[-1][n2][n1], pd2[-1][n2][n1], normal, 1.0) + + interp.getCubicHermiteCurvature(px[-1][n2][n1], pd2[-1][n2][n1], px[-1][n2p][n1], pd2[-1][n2p][n1], normal, 0.0)) + factor = 1.0 + curvature * thickness + pd2[n3][n2][n1] = [factor * d for d in pd2[-1][n2][n1]] + d2Scaled = [factor * d for d in pd2[-1][n2][n1]] + if vector.dotproduct(vector.normalise(pd2[-1][n2][n1]), vector.normalise(d2Scaled)) == -1: + pd2[n3][n2][n1] = [-factor * d for d in pd2[-1][n2][n1]] + if not midLinearXi3: + pd3[n3][n2][n1] = pd3[-1][n2][n1] = [d * thicknesses[n2][n1] / (nodesCountWall - 1) for d in normal] + + # smooth derivative 1 around inner loop + pd1[n3][n2] = interp.smoothCubicHermiteDerivativesLoop(px[n3][n2], pd1[n3][n2], magnitudeScalingMode=interp.DerivativeScalingMode.HARMONIC_MEAN) + + for n3 in range(0, nodesCountWall): # smooth derivative 2 radially/along annulus for n1 in range(nodesCountAround): mx = [ px [n3][n2][n1] for n2 in range(elementsCountRadial + 1) ] @@ -354,9 +356,9 @@ def createAnnulusMesh3d(nodes, mesh, nextNodeIdentifier, nextElementIdentifier, nodetemplateLinearS3.setValueNumberOfVersions(coordinates, -1, Node.VALUE_LABEL_D2_DS1DS2, 1) nodeIdentifier = nextNodeIdentifier - nodeId = [ [], [] ] - for n3 in range(2): - for n2 in range(elementsCountRadial + 1): + nodeId = [[] for n3 in range(nodesCountWall)] + for n2 in range(elementsCountRadial + 1): + for n3 in range(nodesCountWall): if (n2 == 0) and (startNodeId is not None): rowNodeId = copy.deepcopy(startNodeId[n3]) elif (n2 == elementsCountRadial) and (endNodeId is not None): @@ -389,6 +391,7 @@ def createAnnulusMesh3d(nodes, mesh, nextNodeIdentifier, nextElementIdentifier, elementtemplateStandard.setElementShapeType(Element.SHAPE_TYPE_CUBE) elementtemplateX = mesh.createElementtemplate() elementtemplateX.setElementShapeType(Element.SHAPE_TYPE_CUBE) + elementsCountWall = nodesCountWall - 1 for e2 in range(elementsCountRadial): nonlinearXi3 = (not rowLinearXi3[e2]) or (not rowLinearXi3[e2 + 1]) @@ -400,140 +403,146 @@ def createAnnulusMesh3d(nodes, mesh, nextNodeIdentifier, nextElementIdentifier, mapEndDerivatives = (e2 == (elementsCountRadial - 1)) and (endDerivativesMap or rescaleEndDerivatives) mapEndLinearDerivativeXi3 = nonlinearXi3 and rowLinearXi3[e2 + 1] mapDerivatives = mapStartDerivatives or mapStartLinearDerivativeXi3 or mapEndDerivatives or mapEndLinearDerivativeXi3 - for e1 in range(elementsCountAround): - en = (e1 + 1)%elementsCountAround - nids = [ nodeId[0][e2][e1], nodeId[0][e2][en], nodeId[0][e2 + 1][e1], nodeId[0][e2 + 1][en], - nodeId[1][e2][e1], nodeId[1][e2][en], nodeId[1][e2 + 1][e1], nodeId[1][e2 + 1][en] ] - scaleFactors = [] - - if mapDerivatives: - eft1 = eftFactory.createEftNoCrossDerivatives() - # work out if scaling by global -1 - scaleMinus1 = mapStartLinearDerivativeXi3 or mapEndLinearDerivativeXi3 - if (not scaleMinus1) and mapStartDerivatives and startDerivativesMap: + for e3 in range(elementsCountWall): + for e1 in range(elementsCountAround): + en = (e1 + 1) % elementsCountAround + nids = [nodeId[e3][e2][e1], nodeId[e3][e2][en], nodeId[e3][e2 + 1][e1], nodeId[e3][e2 + 1][en], + nodeId[e3 + 1][e2][e1], nodeId[e3 + 1][e2][en], nodeId[e3 + 1][e2 + 1][e1], + nodeId[e3 + 1][e2 + 1][en]] + scaleFactors = [] + + if mapDerivatives: + eft1 = eftFactory.createEftNoCrossDerivatives() + # work out if scaling by global -1 + scaleMinus1 = mapStartLinearDerivativeXi3 or mapEndLinearDerivativeXi3 + if (not scaleMinus1) and mapStartDerivatives and startDerivativesMap: + for n3 in range(2): + n3Idx = n3 + e3 + # need to handle 3 or 4 maps (e1 uses last 3, en uses first 3) + for map in startDerivativesMap[n3Idx][e1][-3:]: + if map and (-1 in map): + scaleMinus1 = True + break + for map in startDerivativesMap[n3Idx][en][:3]: + if map and (-1 in map): + scaleMinus1 = True + break + if (not scaleMinus1) and mapEndDerivatives and endDerivativesMap: + for n3 in range(2): + n3Idx = n3 + e3 + # need to handle 3 or 4 maps (e1 uses last 3, en uses first 3) + for map in endDerivativesMap[n3Idx][e1][-3:]: + if map and (-1 in map): + scaleMinus1 = True + break + for map in endDerivativesMap[n3Idx][en][:3]: + if map and (-1 in map): + scaleMinus1 = True + break + # make node scale factors vary fastest by local node varying across lower xi + nodeScaleFactorIds = [] for n3 in range(2): - # need to handle 3 or 4 maps (e1 uses last 3, en uses first 3) - for map in startDerivativesMap[n3][e1][-3:]: - if map and (-1 in map): - scaleMinus1 = True - break - for map in startDerivativesMap[n3][en][:3]: - if map and (-1 in map): - scaleMinus1 = True - break - if (not scaleMinus1) and mapEndDerivatives and endDerivativesMap: + n3Idx = n3 + e3 + if mapStartDerivatives and rescaleStartDerivatives: + for i in range(2): + derivativesMap = (startDerivativesMap[n3Idx][e1][1] if (i == 0) else startDerivativesMap[n3Idx][en][1]) if startDerivativesMap else None + nodeScaleFactorIds.append(getQuadrantID(derivativesMap if derivativesMap else (0, 1, 0))) + if mapEndDerivatives and rescaleEndDerivatives: + for i in range(2): + derivativesMap = (endDerivativesMap[n3Idx][e1][1] if (i == 0) else endDerivativesMap[n3Idx][en][1]) if endDerivativesMap else None + nodeScaleFactorIds.append(getQuadrantID(derivativesMap if derivativesMap else (0, 1, 0))) + setEftScaleFactorIds(eft1, [1] if scaleMinus1 else [], nodeScaleFactorIds) + firstNodeScaleFactorIndex = 2 if scaleMinus1 else 1 + firstStartNodeScaleFactorIndex = firstNodeScaleFactorIndex if (mapStartDerivatives and rescaleStartDerivatives) else None + firstEndNodeScaleFactorIndex = (firstNodeScaleFactorIndex + (2 if firstStartNodeScaleFactorIndex else 0)) if (mapEndDerivatives and rescaleEndDerivatives) else None + layerNodeScaleFactorIndexOffset = 4 if (firstStartNodeScaleFactorIndex and firstEndNodeScaleFactorIndex) else 2 + if scaleMinus1: + scaleFactors.append(-1.0) for n3 in range(2): - # need to handle 3 or 4 maps (e1 uses last 3, en uses first 3) - for map in endDerivativesMap[n3][e1][-3:]: - if map and (-1 in map): - scaleMinus1 = True - break - for map in endDerivativesMap[n3][en][:3]: - if map and (-1 in map): - scaleMinus1 = True - break - # make node scale factors vary fastest by local node varying across lower xi - nodeScaleFactorIds = [] - for n3 in range(2): - if mapStartDerivatives and rescaleStartDerivatives: + n3Idx = n3 + e3 + if firstStartNodeScaleFactorIndex: + scaleFactors.append(scaleFactorMapStart[n3Idx][e1]) + scaleFactors.append(scaleFactorMapStart[n3Idx][en]) + if firstEndNodeScaleFactorIndex: + scaleFactors.append(scaleFactorMapEnd[n3Idx][e1]) + scaleFactors.append(scaleFactorMapEnd[n3Idx][en]) + + if mapStartLinearDerivativeXi3: + eftFactory.setEftLinearDerivative2(eft1, [1, 5, 2, 6], Node.VALUE_LABEL_D_DS3, [Node.VALUE_LABEL_D2_DS1DS3]) + if mapStartDerivatives: for i in range(2): - derivativesMap = (startDerivativesMap[n3][e1][1] if (i == 0) else startDerivativesMap[n3][en][1]) if startDerivativesMap else None - nodeScaleFactorIds.append(getQuadrantID(derivativesMap if derivativesMap else (0, 1, 0))) - if mapEndDerivatives and rescaleEndDerivatives: + lns = [1, 5] if (i == 0) else [2, 6] + for n3 in range(2): + n3Idx = n3 + e3 + derivativesMap = (startDerivativesMap[n3Idx][e1] if (i == 0) else startDerivativesMap[n3Idx][en]) if startDerivativesMap else (None, None, None) + # handle different d1 on each side of node + d1Map = derivativesMap[0] if ((i == 1) or (len(derivativesMap) < 4)) else derivativesMap[3] + d2Map = derivativesMap[1] if derivativesMap[1] else (0, 1, 0) + d3Map = derivativesMap[2] + # use temporary to safely swap DS1 and DS2: + ln = [lns[n3]] + if d1Map: + remapEftNodeValueLabel(eft1, ln, Node.VALUE_LABEL_D_DS1, [(Node.VALUE_LABEL_D2_DS1DS2, [])]) + if d3Map: + remapEftNodeValueLabel(eft1, ln, Node.VALUE_LABEL_D_DS3, [(Node.VALUE_LABEL_D2_DS2DS3, [])]) + if d2Map: + remapEftNodeValueLabel(eft1, ln, Node.VALUE_LABEL_D_DS2, + derivativeSignsToExpressionTerms((Node.VALUE_LABEL_D_DS1, + Node.VALUE_LABEL_D_DS2, + Node.VALUE_LABEL_D_DS3), d2Map, + (firstStartNodeScaleFactorIndex + i + n3 * layerNodeScaleFactorIndexOffset) if rescaleStartDerivatives else None)) + if d1Map: + remapEftNodeValueLabel(eft1, ln, Node.VALUE_LABEL_D2_DS1DS2, \ + derivativeSignsToExpressionTerms((Node.VALUE_LABEL_D_DS1, Node.VALUE_LABEL_D_DS2, Node.VALUE_LABEL_D_DS3), d1Map)) + if d3Map: + remapEftNodeValueLabel(eft1, ln, Node.VALUE_LABEL_D2_DS2DS3, \ + derivativeSignsToExpressionTerms((Node.VALUE_LABEL_D_DS1, Node.VALUE_LABEL_D_DS2, Node.VALUE_LABEL_D_DS3), d3Map)) + if mapEndLinearDerivativeXi3: + eftFactory.setEftLinearDerivative2(eft1, [3, 7, 4, 8], Node.VALUE_LABEL_D_DS3, [Node.VALUE_LABEL_D2_DS1DS3]) + if mapEndDerivatives: for i in range(2): - derivativesMap = (endDerivativesMap[n3][e1][1] if (i == 0) else endDerivativesMap[n3][en][1]) if endDerivativesMap else None - nodeScaleFactorIds.append(getQuadrantID(derivativesMap if derivativesMap else (0, 1, 0))) - setEftScaleFactorIds(eft1, [1] if scaleMinus1 else [], nodeScaleFactorIds) - firstNodeScaleFactorIndex = 2 if scaleMinus1 else 1 - firstStartNodeScaleFactorIndex = firstNodeScaleFactorIndex if (mapStartDerivatives and rescaleStartDerivatives) else None - firstEndNodeScaleFactorIndex = (firstNodeScaleFactorIndex + (2 if firstStartNodeScaleFactorIndex else 0)) if (mapEndDerivatives and rescaleEndDerivatives) else None - layerNodeScaleFactorIndexOffset = 4 if (firstStartNodeScaleFactorIndex and firstEndNodeScaleFactorIndex) else 2 - if scaleMinus1: - scaleFactors.append(-1.0) - for n3 in range(2): - if firstStartNodeScaleFactorIndex: - scaleFactors.append(scaleFactorMapStart[n3][e1]) - scaleFactors.append(scaleFactorMapStart[n3][en]) - if firstEndNodeScaleFactorIndex: - scaleFactors.append(scaleFactorMapEnd[n3][e1]) - scaleFactors.append(scaleFactorMapEnd[n3][en]) - - if mapStartLinearDerivativeXi3: - eftFactory.setEftLinearDerivative2(eft1, [ 1, 5, 2, 6 ], Node.VALUE_LABEL_D_DS3, [ Node.VALUE_LABEL_D2_DS1DS3 ]) - if mapStartDerivatives: - for i in range(2): - lns = [ 1, 5 ] if (i == 0) else [ 2, 6 ] - for n3 in range(2): - derivativesMap = (startDerivativesMap[n3][e1] if (i == 0) else startDerivativesMap[n3][en]) if startDerivativesMap else (None, None, None) - # handle different d1 on each side of node - d1Map = derivativesMap[0] if ((i == 1) or (len(derivativesMap) < 4)) else derivativesMap[3] - d2Map = derivativesMap[1] if derivativesMap[1] else (0, 1, 0) - d3Map = derivativesMap[2] - # use temporary to safely swap DS1 and DS2: - ln = [ lns[n3] ] - if d1Map: - remapEftNodeValueLabel(eft1, ln, Node.VALUE_LABEL_D_DS1, [ ( Node.VALUE_LABEL_D2_DS1DS2, [] ) ]) - if d3Map: - remapEftNodeValueLabel(eft1, ln, Node.VALUE_LABEL_D_DS3, [ ( Node.VALUE_LABEL_D2_DS2DS3, [] ) ]) - if d2Map: - remapEftNodeValueLabel(eft1, ln, Node.VALUE_LABEL_D_DS2, \ - derivativeSignsToExpressionTerms( ( Node.VALUE_LABEL_D_DS1, - Node.VALUE_LABEL_D_DS2, - Node.VALUE_LABEL_D_DS3 ), d2Map, - (firstStartNodeScaleFactorIndex + i + n3*layerNodeScaleFactorIndexOffset) if rescaleStartDerivatives else None) ) - if d1Map: - remapEftNodeValueLabel(eft1, ln, Node.VALUE_LABEL_D2_DS1DS2, \ - derivativeSignsToExpressionTerms( ( Node.VALUE_LABEL_D_DS1, Node.VALUE_LABEL_D_DS2, Node.VALUE_LABEL_D_DS3 ), d1Map)) - if d3Map: - remapEftNodeValueLabel(eft1, ln, Node.VALUE_LABEL_D2_DS2DS3, \ - derivativeSignsToExpressionTerms( ( Node.VALUE_LABEL_D_DS1, Node.VALUE_LABEL_D_DS2, Node.VALUE_LABEL_D_DS3 ), d3Map)) - if mapEndLinearDerivativeXi3: - eftFactory.setEftLinearDerivative2(eft1, [ 3, 7, 4, 8 ], Node.VALUE_LABEL_D_DS3, [ Node.VALUE_LABEL_D2_DS1DS3 ]) - if mapEndDerivatives: - for i in range(2): - lns = [ 3, 7 ] if (i == 0) else [ 4, 8 ] - for n3 in range(2): - derivativesMap = (endDerivativesMap[n3][e1] if (i == 0) else endDerivativesMap[n3][en]) if endDerivativesMap else (None, None, None) - # handle different d1 on each side of node - d1Map = derivativesMap[0] if ((i == 1) or (len(derivativesMap) < 4)) else derivativesMap[3] - d2Map = derivativesMap[1] if derivativesMap[1] else (0, 1, 0) - d3Map = derivativesMap[2] - - # use temporary to safely swap DS1 and DS2: - ln = [ lns[n3] ] - if d1Map: - remapEftNodeValueLabel(eft1, ln, Node.VALUE_LABEL_D_DS1, [ ( Node.VALUE_LABEL_D2_DS1DS2, [] ) ]) - if d3Map: - remapEftNodeValueLabel(eft1, ln, Node.VALUE_LABEL_D_DS3, [ ( Node.VALUE_LABEL_D2_DS2DS3, [] ) ]) - if d2Map: - remapEftNodeValueLabel(eft1, ln, Node.VALUE_LABEL_D_DS2, \ - derivativeSignsToExpressionTerms( ( Node.VALUE_LABEL_D_DS1, - Node.VALUE_LABEL_D_DS2, - Node.VALUE_LABEL_D_DS3), d2Map, - (firstEndNodeScaleFactorIndex + i + n3*layerNodeScaleFactorIndexOffset) if rescaleEndDerivatives else None) ) - if d1Map: - remapEftNodeValueLabel(eft1, ln, Node.VALUE_LABEL_D2_DS1DS2, \ - derivativeSignsToExpressionTerms( ( Node.VALUE_LABEL_D_DS1, Node.VALUE_LABEL_D_DS2, Node.VALUE_LABEL_D_DS3 ), d1Map)) - if d3Map: - remapEftNodeValueLabel(eft1, ln, Node.VALUE_LABEL_D2_DS2DS3, \ - derivativeSignsToExpressionTerms( ( Node.VALUE_LABEL_D_DS1, Node.VALUE_LABEL_D_DS2, Node.VALUE_LABEL_D_DS3 ), d3Map)) - - elementtemplateX.defineField(coordinates, -1, eft1) - elementtemplate1 = elementtemplateX - else: - eft1 = eftStandard - elementtemplate1 = elementtemplateStandard - - element = mesh.createElement(elementIdentifier, elementtemplate1) - result2 = element.setNodesByIdentifier(eft1, nids) - if scaleFactors: - result3 = element.setScaleFactors(eft1, scaleFactors); - #print('create element annulus', element.isValid(), elementIdentifier, eft1.validate(), result2, result3 if scaleFactors else None, nids) - elementIdentifier += 1 - - if rowMeshGroups: - for meshGroup in rowMeshGroups[e2]: - meshGroup.addElement(element) + lns = [3, 7] if (i == 0) else [4, 8] + for n3 in range(2): + n3Idx = n3 + e3 + derivativesMap = (endDerivativesMap[n3Idx][e1] if (i == 0) else endDerivativesMap[n3Idx][en]) if endDerivativesMap else (None, None, None) + # handle different d1 on each side of node + d1Map = derivativesMap[0] if ((i == 1) or (len(derivativesMap) < 4)) else derivativesMap[3] + d2Map = derivativesMap[1] if derivativesMap[1] else (0, 1, 0) + d3Map = derivativesMap[2] + + # use temporary to safely swap DS1 and DS2: + ln = [lns[n3]] + if d1Map: + remapEftNodeValueLabel(eft1, ln, Node.VALUE_LABEL_D_DS1, [(Node.VALUE_LABEL_D2_DS1DS2, [])]) + if d3Map: + remapEftNodeValueLabel(eft1, ln, Node.VALUE_LABEL_D_DS3, [(Node.VALUE_LABEL_D2_DS2DS3, [])]) + if d2Map: + remapEftNodeValueLabel(eft1, ln, Node.VALUE_LABEL_D_DS2, + derivativeSignsToExpressionTerms((Node.VALUE_LABEL_D_DS1, + Node.VALUE_LABEL_D_DS2, + Node.VALUE_LABEL_D_DS3), d2Map, + (firstEndNodeScaleFactorIndex + i + n3 * layerNodeScaleFactorIndexOffset) if rescaleEndDerivatives else None)) + if d1Map: + remapEftNodeValueLabel(eft1, ln, Node.VALUE_LABEL_D2_DS1DS2, derivativeSignsToExpressionTerms((Node.VALUE_LABEL_D_DS1, Node.VALUE_LABEL_D_DS2, Node.VALUE_LABEL_D_DS3), d1Map)) + if d3Map: + remapEftNodeValueLabel(eft1, ln, Node.VALUE_LABEL_D2_DS2DS3, derivativeSignsToExpressionTerms((Node.VALUE_LABEL_D_DS1, Node.VALUE_LABEL_D_DS2, Node.VALUE_LABEL_D_DS3), d3Map)) + + elementtemplateX.defineField(coordinates, -1, eft1) + elementtemplate1 = elementtemplateX + else: + eft1 = eftStandard + elementtemplate1 = elementtemplateStandard + + element = mesh.createElement(elementIdentifier, elementtemplate1) + result2 = element.setNodesByIdentifier(eft1, nids) + if scaleFactors: + result3 = element.setScaleFactors(eft1, scaleFactors) + # print('create element annulus', element.isValid(), elementIdentifier, eft1.validate(), result2, result3 if scaleFactors else None, nids) + elementIdentifier += 1 + + if rowMeshGroups: + for meshGroup in rowMeshGroups[e2]: + meshGroup.addElement(element) fm.endChange() From 0280345b39dc6f7a464f983809ee090272e9324f Mon Sep 17 00:00:00 2001 From: Mabelle Lin Date: Sat, 30 Oct 2021 18:23:37 +1300 Subject: [PATCH 06/22] Allow uniform elements through bladder ureter wall --- .../meshtypes/meshtype_3d_bladderurethra1.py | 125 +++++++++--------- 1 file changed, 62 insertions(+), 63 deletions(-) diff --git a/src/scaffoldmaker/meshtypes/meshtype_3d_bladderurethra1.py b/src/scaffoldmaker/meshtypes/meshtype_3d_bladderurethra1.py index cfe8f1b1..a888fbb2 100644 --- a/src/scaffoldmaker/meshtypes/meshtype_3d_bladderurethra1.py +++ b/src/scaffoldmaker/meshtypes/meshtype_3d_bladderurethra1.py @@ -546,9 +546,6 @@ def checkOptions(cls, options): options[key] = 1 if options['Number of elements around'] % 2 != 0: options['Number of elements around'] += 1 - if options['Number of elements through wall'] > 1: - if options['Include ureter']: - options['Number of elements through wall'] = 1 if options['Ureter position around'] < 0.5: options['Ureter position around'] = 0.5 # ureters are on the dorsal part of the bladder elif options['Ureter position around'] > 0.9: @@ -564,9 +561,11 @@ def updateSubScaffoldOptions(cls, options): Update ostium sub-scaffold options which depend on parent options. ''' bladderWallThickness = options['Wall thickness'] + elementsCountThroughWall = options['Number of elements through wall'] ureterOptions = options['Ureter'] ureterDefaultOptions = ureterOptions.getScaffoldSettings() ureterDefaultOptions['Ostium wall thickness'] = bladderWallThickness + ureterDefaultOptions['Number of elements through wall'] = elementsCountThroughWall @classmethod def generateBaseMesh(cls, region, options): @@ -917,9 +916,11 @@ def generateBaseMesh(cls, region, options): outerNodes_d2 = [] for n2 in range(elementsCountAlongBladder + 1): for n1 in range(elementsCountAround): - outerNodes_x.append(xList[(2 * n2 + 1) * elementsCountAround + n1]) - outerNodes_d1.append(d1List[(2 * n2 + 1) * elementsCountAround + n1]) - outerNodes_d2.append(d2List[(2 * n2 + 1) * elementsCountAround + n1]) + idx = n2 * (elementsCountThroughWall + 1) * elementsCountAround + \ + elementsCountThroughWall * elementsCountAround + n1 + outerNodes_x.append(xList[idx]) + outerNodes_d1.append(d1List[idx]) + outerNodes_d2.append(d2List[idx]) elementsCount1 = elementsCountAround // 2 elementsCount2 = elementsCountAlongBladder @@ -1005,7 +1006,7 @@ def generateBaseMesh(cls, region, options): ureterMeshGroup = ureterGroup. getMeshGroup(mesh) # Create nodes and elements - nextNodeIdentifier, nextElementIdentifier, annotationGroups = tubemesh.createNodesAndElements( + nodeIdentifier, elementIdentifier, annotationGroups = tubemesh.createNodesAndElements( region, xFinal, d1Final, d2Final, d3Final, xFlat, d1Flat, d2Flat, xOrgan, d1Organ, d2Organ, None, elementsCountAround, elementsCountAlong, elementsCountThroughWall, annotationGroupsAround, annotationGroupsAlong, annotationGroupsThroughWall, @@ -1051,11 +1052,15 @@ def generateBaseMesh(cls, region, options): if includeUreter: bladderMeshGroup = [neckMeshGroup, urinaryBladderMeshGroup, dorsalBladderMeshGroup] - generateUreterInlets(region, nodes, mesh, ureterDefaultOptions, elementsCountAround, elementsCountThroughWall, - elementsCountAroundUreter, trackSurfaceUreter1, ureter1Position, trackSurfaceUreter2, - ureter2Position, ureterElementPositionDown, ureterElementPositionAround, xFinal, d1Final, - d2Final, nextNodeIdentifier, nextElementIdentifier, elementsCountUreterRadial, - ureterMeshGroup, bladderMeshGroup) + nodeIdentifier = generateUreterInlets(region, nodes, mesh, ureterDefaultOptions, elementsCountAround, + elementsCountThroughWall, + elementsCountAroundUreter, trackSurfaceUreter1, ureter1Position, + trackSurfaceUreter2, + ureter2Position, ureterElementPositionDown, + ureterElementPositionAround, xFinal, d1Final, + d2Final, nodeIdentifier, elementIdentifier, + elementsCountUreterRadial, + ureterMeshGroup, bladderMeshGroup) # Define markers for apex, ureter and urethra junctions with bladder apexGroup = findOrCreateAnnotationGroupForTerm(annotationGroups, region, get_bladder_term("apex of urinary bladder")) @@ -1069,10 +1074,11 @@ def generateBaseMesh(cls, region, options): markerList = [] markerList.append({"group": apexGroup, "elementId": idx1, "xi": xi1}) if includeUreter: - idx2 = elementsCountAlong * elementsCountAround + elementsCountAroundUreter + idx2 = elementsCountAlong * elementsCountAround * elementsCountThroughWall + elementsCountAroundUreter xi2 = [0.0, 1.0, 0.0] markerList.append({"group": leftUreterGroup, "elementId": idx2, "xi": xi2}) - idx3 = elementsCountAlong * elementsCountAround + 2 * elementsCountAroundUreter + idx3 = elementsCountAlong * elementsCountAround * elementsCountThroughWall + \ + elementsCountThroughWall * elementsCountAroundUreter + elementsCountAroundUreter xi3 = [0.0, 1.0, 0.0] markerList.append({"group": rightUreterGroup, "elementId": idx3, "xi": xi3}) else: @@ -1089,7 +1095,6 @@ def generateBaseMesh(cls, region, options): xi5 = [0.0, 1.0, 0.0] markerList.append({"group": ventralUrethraGroup, "elementId": idx5, "xi": xi5}) - nodeIdentifier = nextNodeIdentifier + elementsCountAroundUreter * 4 * 2 + elementsCountAroundUreter * 2 * 2 * elementsCountUreterRadial bladderNodesetGroup = bladderGroup.getNodesetGroup(nodes) for marker in markerList: annotationGroup = marker["group"] @@ -1293,15 +1298,15 @@ def generateUreterInlets(region, nodes, mesh, ureterDefaultOptions,elementsCount vesselMeshGroups=[[ureterMeshGroup]], ostiumMeshGroups=bladderMeshGroup) # Create annulus mesh around ureters - endPoints1_x = [[None] * elementsCountAroundUreter, [None] * elementsCountAroundUreter] - endPoints1_d1 = [[None] * elementsCountAroundUreter, [None] * elementsCountAroundUreter] - endPoints1_d2 = [[None] * elementsCountAroundUreter, [None] * elementsCountAroundUreter] - endNode1_Id = [[None] * elementsCountAroundUreter, [None] * elementsCountAroundUreter] - endDerivativesMap = [[None] * elementsCountAroundUreter, [None] * elementsCountAroundUreter] - endPoints2_x = [[None] * elementsCountAroundUreter, [None] * elementsCountAroundUreter] - endPoints2_d1 = [[None] * elementsCountAroundUreter, [None] * elementsCountAroundUreter] - endPoints2_d2 = [[None] * elementsCountAroundUreter, [None] * elementsCountAroundUreter] - endNode2_Id = [[None] * elementsCountAroundUreter, [None] * elementsCountAroundUreter] + endPoints1_x = [[None] * elementsCountAroundUreter for n3 in range(elementsCountThroughWall + 1)] + endPoints1_d1 = [[None] * elementsCountAroundUreter for n3 in range(elementsCountThroughWall + 1)] + endPoints1_d2 = [[None] * elementsCountAroundUreter for n3 in range(elementsCountThroughWall + 1)] + endNode1_Id = [[None] * elementsCountAroundUreter for n3 in range(elementsCountThroughWall + 1)] + endDerivativesMap = [[None] * elementsCountAroundUreter for n3 in range(elementsCountThroughWall + 1)] + endPoints2_x = [[None] * elementsCountAroundUreter for n3 in range(elementsCountThroughWall + 1)] + endPoints2_d1 = [[None] * elementsCountAroundUreter for n3 in range(elementsCountThroughWall + 1)] + endPoints2_d2 = [[None] * elementsCountAroundUreter for n3 in range(elementsCountThroughWall + 1)] + endNode2_Id = [[None] * elementsCountAroundUreter for n3 in range(elementsCountThroughWall + 1)] count = 0 for n2 in range(3): @@ -1310,23 +1315,21 @@ def generateUreterInlets(region, nodes, mesh, ureterDefaultOptions,elementsCount count += 1 for n1 in range(2): endNode1_Id[0][count] = endNode1_Id[0][count - 1] + 1 - endNode2_Id[0][count] = endNode2_Id[0][count - 1] + 1 - \ - (0 if (endNode2_Id[0][count - 1] - 2) % elementsCountAround > 0 - else elementsCountAround) + endNode2_Id[0][count] = endNode2_Id[0][count - 1] + 1 count += 1 for n2 in range(2): endNode1_Id[0][count] = endNode1_Id[0][count - 1] - (elementsCountThroughWall + 1) * elementsCountAround endNode2_Id[0][count] = endNode2_Id[0][count - 1] - (elementsCountThroughWall + 1) * elementsCountAround count += 1 endNode1_Id[0][count] = endNode1_Id[0][count - 1] - 1 - endNode2_Id[0][count] = endNode2_Id[0][count - 1] - 1 + \ - (0 if (endNode2_Id[0][count - 1] - 3) % elementsCountAround > 0 else elementsCountAround) + endNode2_Id[0][count] = endNode2_Id[0][count - 1] - 1 - for n in range(len(endNode1_Id[0])): - endNode1_Id[1][n] = endNode1_Id[0][n] + elementsCountAround - endNode2_Id[1][n] = endNode2_Id[0][n] + elementsCountAround + for n3 in range(1, elementsCountThroughWall + 1): + for n in range(len(endNode1_Id[0])): + endNode1_Id[n3][n] = endNode1_Id[0][n] + elementsCountAround * n3 + endNode2_Id[n3][n] = endNode2_Id[0][n] + elementsCountAround * n3 - for n3 in range(2): + for n3 in range(elementsCountThroughWall + 1): for n1 in range(elementsCountAroundUreter): nc1 = endNode1_Id[n3][n1] - 1 endPoints1_x[n3][n1] = xBladder[nc1] @@ -1337,31 +1340,24 @@ def generateUreterInlets(region, nodes, mesh, ureterDefaultOptions,elementsCount endPoints2_d1[n3][n1] = d1Bladder[nc2] endPoints2_d2[n3][n1] = d2Bladder[nc2] - for n1 in range(elementsCountAroundUreter): - if n1 == 0: - endDerivativesMap[0][n1] = ((-1, 0, 0), (-1, -1, 0), None, (0, 1, 0)) - endDerivativesMap[1][n1] = ((-1, 0, 0), (-1, -1, 0), None, (0, 1, 0)) - elif n1 == 1: - endDerivativesMap[0][n1] = ((0, 1, 0), (-1, 0, 0), None) - endDerivativesMap[1][n1] = ((0, 1, 0), (-1, 0, 0), None) - elif n1 == 2: - endDerivativesMap[0][n1] = ((0, 1, 0), (-1, 1, 0), None, (1, 0, 0)) - endDerivativesMap[1][n1] = ((0, 1, 0), (-1, 1, 0), None, (1, 0, 0)) - elif n1 == 3: - endDerivativesMap[0][n1] = ((1, 0, 0), (0, 1, 0), None) - endDerivativesMap[1][n1] = ((1, 0, 0), (0, 1, 0), None) - elif n1 == 4: - endDerivativesMap[0][n1] = ((1, 0, 0), (1, 1, 0), None, (0, -1, 0)) - endDerivativesMap[1][n1] = ((1, 0, 0), (1, 1, 0), None, (0, -1, 0)) - elif n1 == 5: - endDerivativesMap[0][n1] = ((0, -1, 0), (1, 0, 0), None) - endDerivativesMap[1][n1] = ((0, -1, 0), (1, 0, 0), None) - elif n1 == 6: - endDerivativesMap[0][n1] = ((0, -1, 0), (1, -1, 0), None, (-1, 0, 0)) - endDerivativesMap[1][n1] = ((0, -1, 0), (1, -1, 0), None, (-1, 0, 0)) - else: - endDerivativesMap[0][n1] = ((-1, 0, 0), (0, -1, 0), None) - endDerivativesMap[1][n1] = ((-1, 0, 0), (0, -1, 0), None) + for n3 in range(elementsCountThroughWall + 1): + for n1 in range(elementsCountAroundUreter): + if n1 == 0: + endDerivativesMap[n3][n1] = ((-1, 0, 0), (-1, -1, 0), None, (0, 1, 0)) + elif n1 == 1: + endDerivativesMap[n3][n1] = ((0, 1, 0), (-1, 0, 0), None) + elif n1 == 2: + endDerivativesMap[n3][n1] = ((0, 1, 0), (-1, 1, 0), None, (1, 0, 0)) + elif n1 == 3: + endDerivativesMap[n3][n1] = ((1, 0, 0), (0, 1, 0), None) + elif n1 == 4: + endDerivativesMap[n3][n1] = ((1, 0, 0), (1, 1, 0), None, (0, -1, 0)) + elif n1 == 5: + endDerivativesMap[n3][n1] = ((0, -1, 0), (1, 0, 0), None) + elif n1 == 6: + endDerivativesMap[n3][n1] = ((0, -1, 0), (1, -1, 0), None, (-1, 0, 0)) + else: + endDerivativesMap[n3][n1] = ((-1, 0, 0), (0, -1, 0), None) startProportions1 = [] for n in range(len(o1_Positions)): @@ -1428,11 +1424,14 @@ def generateUreterInlets(region, nodes, mesh, ureterDefaultOptions,elementsCount elementToDeleteStartIdxList = [elementToDeleteStartIdx1, elementToDeleteStartIdx2] for i in range(2): elementToDeleteStart = elementToDeleteStartIdxList[i] - elementsToDelete = [elementToDeleteStart, elementToDeleteStart + 1, - elementToDeleteStart + elementsCountAround, - elementToDeleteStart + 1 + elementsCountAround] - element_identifiers += elementsToDelete + baseElementsToDelete = [elementToDeleteStart, + elementToDeleteStart + 1, + elementToDeleteStart + elementsCountAround * elementsCountThroughWall, + elementToDeleteStart + 1 + elementsCountAround * elementsCountThroughWall] + for e3 in range(elementsCountThroughWall): + elementsToDelete = [c + elementsCountAround * e3 for c in baseElementsToDelete] + element_identifiers += elementsToDelete mesh_destroy_elements_and_nodes_by_identifiers(mesh, element_identifiers) - return + return nodeIdentifier From 3a789a60aef81d9ec53348c5a2a7dfe54ba60a85 Mon Sep 17 00:00:00 2001 From: Mabelle Lin Date: Sat, 30 Oct 2021 19:29:14 +1300 Subject: [PATCH 07/22] Allow variable thickness for elements through ostium wall --- .../meshtypes/meshtype_3d_ostium1.py | 86 +++++++++++++++---- src/scaffoldmaker/utils/annulusmesh.py | 53 +++++++++--- 2 files changed, 111 insertions(+), 28 deletions(-) diff --git a/src/scaffoldmaker/meshtypes/meshtype_3d_ostium1.py b/src/scaffoldmaker/meshtypes/meshtype_3d_ostium1.py index e3fb2e22..d606805f 100644 --- a/src/scaffoldmaker/meshtypes/meshtype_3d_ostium1.py +++ b/src/scaffoldmaker/meshtypes/meshtype_3d_ostium1.py @@ -25,8 +25,8 @@ class MeshType_3d_ostium1(Scaffold_base): def getName(): return '3D Ostium 1' - @staticmethod - def getDefaultOptions(parameterSetName='Default'): + @classmethod + def getDefaultOptions(cls, parameterSetName='Default'): return { 'Number of vessels' : 2, 'Number of elements across common' : 2, @@ -38,12 +38,14 @@ def getDefaultOptions(parameterSetName='Default'): 'Ostium diameter' : 1.0, 'Ostium length' : 0.4, 'Ostium wall thickness' : 0.08, + 'Ostium wall relative thickness proportions': [1.0], 'Ostium inter-vessel distance' : 0.8, 'Ostium inter-vessel height' : 0.0, 'Use linear through ostium wall' : False, 'Vessel end length factor' : 1.0, 'Vessel inner diameter' : 0.6, 'Vessel wall thickness' : 0.04, + 'Vessel wall relative thickness proportions': [1.0], 'Vessel angle 1 degrees' : 0.0, 'Vessel angle 1 spread degrees' : 0.0, 'Vessel angle 2 degrees' : 0.0, @@ -68,12 +70,14 @@ def getOrderedOptionNames(): 'Ostium diameter', 'Ostium length', 'Ostium wall thickness', + 'Ostium wall relative thickness proportions', 'Ostium inter-vessel distance', 'Ostium inter-vessel height', 'Use linear through ostium wall', 'Vessel end length factor', 'Vessel inner diameter', 'Vessel wall thickness', + 'Vessel wall relative thickness proportions', 'Vessel angle 1 degrees', 'Vessel angle 1 spread degrees', 'Vessel angle 2 degrees', @@ -85,8 +89,8 @@ def getOrderedOptionNames(): 'Refine number of elements through wall' ] - @staticmethod - def checkOptions(options): + @classmethod + def checkOptions(cls, options): dependentChanges = False vesselsCount = options['Number of vessels'] if vesselsCount < 1: @@ -120,6 +124,25 @@ def checkOptions(options): options[key] = 0.0 if options['Ostium diameter'] <= 0.0: options['Ostium diameter'] = 0.000001 # avoid division by zero + elementsThroughWall = options['Number of elements through wall'] + ostiumThicknessProportionsCountKey = 'Ostium wall relative thickness proportions' + vesselThicknessProportionsCountKey = 'Vessel wall relative thickness proportions' + ostiumWallCount = len(options[ostiumThicknessProportionsCountKey]) + vesselWallCount = len(options[vesselThicknessProportionsCountKey]) + if elementsThroughWall == 1: + options[ostiumThicknessProportionsCountKey] = options[vesselThicknessProportionsCountKey] = [1.0] + if ostiumWallCount < elementsThroughWall: + options[ostiumThicknessProportionsCountKey] += [options[ostiumThicknessProportionsCountKey][-1] for i in range(elementsThroughWall - ostiumWallCount)] + dependentChanges = True + elif ostiumWallCount > elementsThroughWall: + options[ostiumThicknessProportionsCountKey] = options[ostiumThicknessProportionsCountKey][:elementsThroughWall] + dependentChanges = True + if vesselWallCount < elementsThroughWall: + options[vesselThicknessProportionsCountKey] += [options[vesselThicknessProportionsCountKey][-1] for i in range(elementsThroughWall - vesselWallCount)] + dependentChanges = True + elif vesselWallCount > elementsThroughWall: + options[vesselThicknessProportionsCountKey] = options[vesselThicknessProportionsCountKey][:elementsThroughWall] + dependentChanges = True return dependentChanges @classmethod @@ -218,6 +241,7 @@ def generateOstiumMesh(region, options, trackSurface, centrePosition, axis1, sta ostiumRadius = 0.5*unitScale*options['Ostium diameter'] ostiumLength = unitScale*options['Ostium length'] ostiumWallThickness = unitScale*options['Ostium wall thickness'] + ostiumWallThicknessProportions = copy.deepcopy(options['Ostium wall relative thickness proportions']) interVesselHeight = unitScale*options['Ostium inter-vessel height'] interVesselDistance = unitScale*options['Ostium inter-vessel distance'] if (vesselsCount > 1) else 0.0 halfInterVesselDistance = 0.5*interVesselDistance @@ -225,6 +249,7 @@ def generateOstiumMesh(region, options, trackSurface, centrePosition, axis1, sta vesselEndDerivative = ostiumLength*options['Vessel end length factor']/elementsCountAlong vesselInnerRadius = 0.5*unitScale*options['Vessel inner diameter'] vesselWallThickness = unitScale*options['Vessel wall thickness'] + vesselWallThicknessProportions = copy.deepcopy(options['Vessel wall relative thickness proportions']) vesselOuterRadius = vesselInnerRadius + vesselWallThickness vesselAngle1Radians = math.radians(options['Vessel angle 1 degrees']) vesselAngle1SpreadRadians = math.radians(options['Vessel angle 1 spread degrees']) @@ -299,6 +324,9 @@ def generateOstiumMesh(region, options, trackSurface, centrePosition, axis1, sta od2 = [[] for n3 in range(elementsCountThroughWall + 1)] od3 = [[] for n3 in range(elementsCountThroughWall + 1)] oPositions = [] + ostiumWallThicknessProportions.append(ostiumWallThicknessProportions[-1]) + vesselWallThicknessProportions.append(vesselWallThicknessProportions[-1]) + for n1 in range(elementsCountAroundOstium): elementLength = elementLengthAroundOstiumEnd if distance <= (vesselsSpanMid + halfInterVesselDistance): @@ -335,14 +363,26 @@ def generateOstiumMesh(region, options, trackSurface, centrePosition, axis1, sta opd1 = vector.setMagnitude([ -d for d in pd1 ], elementLengthAroundOstiumEnd) opd2 = vector.setMagnitude(pd2, elementLengthAroundOstiumEnd) # smoothed later opd3 = vector.setMagnitude(pd3, ostiumWallThickness) + + ostiumTotalXi3 = 0.0 + vesselTotalXi3 = 0.0 + ostiumWallThicknessXi3List = [0.0] + vesselWallThicknessXi3List = [0.0] + + for n3 in range(elementsCountThroughWall): + ostiumTotalXi3 += ostiumWallThicknessProportions[n3] + vesselTotalXi3 += vesselWallThicknessProportions[n3] + ostiumWallThicknessXi3List.append(ostiumTotalXi3) + vesselWallThicknessXi3List.append(vesselTotalXi3) + # set coordinates through wall (use copy to avoid references to same list later) for n3 in range(elementsCountThroughWall + 1): - xi3 = 1 - 1 / elementsCountThroughWall * n3 + xi3 = 1 - ostiumWallThicknessXi3List[n3] ox[n3].append([(opx[c] - opd3[c] * xi3) for c in range(3)]) od1[n3].append(copy.copy(opd1)) od2[n3].append(copy.copy(opd2)) if useCubicHermiteThroughOstiumWall: - od3[n3].append([opd3[c] * 1 / elementsCountThroughWall for c in range(3)]) + od3[n3].append([opd3[c] * ostiumWallThicknessProportions[n3] for c in range(3)]) distance += elementLength for n3 in range(elementsCountThroughWall + 1): od1[n3] = interp.smoothCubicHermiteDerivativesLoop(ox[n3], od1[n3], fixAllDirections = True) @@ -353,8 +393,19 @@ def generateOstiumMesh(region, options, trackSurface, centrePosition, axis1, sta xd3 = [] if (vesselWallThickness > 0.0) and (ostiumWallThickness > 0.0): commonOstiumWallThickness = 2.0/(1.0/vesselWallThickness + 1.0/ostiumWallThickness) + commonOstiumWallThicknessProportions = [] + commonOstiumWallTotalXi3 = 0.0 + commonOstiumWallThicknessXi3List = [0.0] + for c in range(elementsCountThroughWall + 1): + commonOstiumWallThicknessProportions.append( + (vesselWallThicknessProportions[c] + ostiumWallThicknessProportions[c]) * 0.5) + for n3 in range(elementsCountThroughWall): + commonOstiumWallTotalXi3 += commonOstiumWallThicknessProportions[n3] + commonOstiumWallThicknessXi3List.append(commonOstiumWallTotalXi3) else: commonOstiumWallThickness = vesselWallThickness + commonOstiumWallThicknessProportions = vesselWallThicknessProportions + commonOstiumWallThicknessXi3List = vesselWallThicknessXi3List # coordinates across common ostium, between vessels nodesCountFreeEnd = elementsCountsAroundVessels[0] + 1 - elementsCountAcross @@ -366,9 +417,10 @@ def generateOstiumMesh(region, options, trackSurface, centrePosition, axis1, sta xd3.append([ None for n3 in range(elementsCountThroughWall + 1)]) oa = elementsCountAroundMid - iv*oinc ob = elementsCountAroundMid + nodesCountFreeEnd - 1 + iv*oinc - nx = [ ox[1][oa], ox[1][ob] ] - nd1 = [ [ -d for d in od1[1][oa] ], od1[1][ob] ] - nd2 = [ [ -d for d in od2[1][oa] ], od2[1][ob] ] + nx = [ox[elementsCountThroughWall][oa], ox[elementsCountThroughWall][ob]] + nd1 = [[-d for d in od1[elementsCountThroughWall][oa]], od1[elementsCountThroughWall][ob]] + nd2 = [[-d for d in od2[elementsCountThroughWall][oa]], od2[elementsCountThroughWall][ob]] + if elementsCountAcross > 1: # add centre point, displaced by interVesselHeight if vesselsCount == 2: @@ -385,7 +437,7 @@ def generateOstiumMesh(region, options, trackSurface, centrePosition, axis1, sta pd1 = interp.interpolateSampleLinear(nd1, pe, pxi) pd3 = [ vector.setMagnitude(vector.crossproduct3(pd1[n2], pd2[n2]), commonOstiumWallThickness) for n2 in range(elementsCountAcross + 1) ] for n3 in range(elementsCountThroughWall + 1): - xi3 = 1 - 1/elementsCountThroughWall * n3 + xi3 = 1 - commonOstiumWallThicknessXi3List[n3] lx = [([(px[n2][c] - xi3 * pd3[n2][c]) for c in range(3)]) for n2 in range(elementsCountAcross + 1)] ld2 = interp.smoothCubicHermiteDerivativesLine(lx, pd2, fixAllDirections=True) xx [iv][n3] = lx [1:elementsCountAcross] @@ -394,11 +446,9 @@ def generateOstiumMesh(region, options, trackSurface, centrePosition, axis1, sta # set smoothed d2 on ostium circumference od2[n3][oa] = [-d for d in ld2[0]] od2[n3][ob] = ld2[-1] - - pd3PerElement = [vector.setMagnitude(vector.crossproduct3(pd1[n2], pd2[n2]), commonOstiumWallThickness/elementsCountThroughWall) for n2 in range(elementsCountAcross + 1)] - if useCubicHermiteThroughOstiumWall: - for n3 in range(elementsCountThroughWall + 1): - xd3[iv][n3] = copy.deepcopy(pd3PerElement[1:elementsCountAcross]) + if useCubicHermiteThroughOstiumWall: + pd3Element = [vector.setMagnitude(vector.crossproduct3(pd1[n2], pd2[n2]), commonOstiumWallThickness * commonOstiumWallThicknessProportions[n3]) for n2 in range(elementsCountAcross + 1)] + xd3[iv][n3] = copy.deepcopy(pd3Element[1:elementsCountAcross]) # get positions of vessel end centres and rings vcx = [] @@ -428,7 +478,7 @@ def generateOstiumMesh(region, options, trackSurface, centrePosition, axis1, sta vod2.append([]) vod3.append([]) for n3 in range(elementsCountThroughWall + 1): - radius = vesselInnerRadius + n3 * vesselWallThickness / elementsCountThroughWall + radius = vesselInnerRadius + vesselWallThicknessXi3List[n3] * vesselWallThickness vAxis1 = vector.setMagnitude(vd1, radius) vAxis2 = vector.setMagnitude(vd2, radius) if vesselsCount == 1: @@ -442,7 +492,7 @@ def generateOstiumMesh(region, options, trackSurface, centrePosition, axis1, sta vod1[-1].append(pd1) vod2[-1].append([ vd3 ]*elementsCountAroundVessel) if useCubicHermiteThroughVesselWall: - vod3[-1].append([ vector.setMagnitude(vector.crossproduct3(d1, vd3), vesselWallThickness/elementsCountThroughWall) for d1 in pd1 ]) + vod3[-1].append([ vector.setMagnitude(vector.crossproduct3(d1, vd3), vesselWallThickness * vesselWallThicknessProportions[n3]) for d1 in pd1 ]) # calculate common ostium vessel node derivatives map mvPointsx = [ None ]*vesselsCount @@ -601,7 +651,7 @@ def generateOstiumMesh(region, options, trackSurface, centrePosition, axis1, sta td1[2] = vector.setMagnitude(sd1, trackDistance) td2[2] = vector.setMagnitude(sd2, trackDistance) for n3 in range(elementsCountThroughWall): - xi3 = 1 - 1 / elementsCountThroughWall * n3 + xi3 = 1 - ostiumWallThicknessXi3List[n3] newd2 = interp.projectHermiteCurvesThroughWall(tx, td1, td2, 1, -ostiumWallThickness * xi3)[1] # assign components to set in all lists: for c in range(3): diff --git a/src/scaffoldmaker/utils/annulusmesh.py b/src/scaffoldmaker/utils/annulusmesh.py index 5c52a1cd..501233e3 100644 --- a/src/scaffoldmaker/utils/annulusmesh.py +++ b/src/scaffoldmaker/utils/annulusmesh.py @@ -181,6 +181,14 @@ def createAnnulusMesh3d(nodes, mesh, nextNodeIdentifier, nextElementIdentifier, pd2 = [[] for n3 in range(nodesCountWall)] pd3 = [[] for n3 in range(nodesCountWall)] + # Find total wall thickness + thicknessProportions = [] + thicknesses = [] + thicknesses.append([vector.magnitude([(startPointsx[nodesCountWall - 1][n1][c] - startPointsx[0][n1][c]) for c in range(3)]) for n1 in range(nodesCountAround)]) + for n2 in range(1, elementsCountRadial): + thicknesses.append([None] * nodesCountAround) + thicknesses.append([vector.magnitude([(endPointsx[nodesCountWall - 1][n1][c] - endPointsx[0][n1][c]) for c in range(3)]) for n1 in range(nodesCountAround)]) + for n3 in range(nodesCountWall): px [n3] = [ startPointsx [n3], endPointsx [n3] ] pd1[n3] = [ startPointsd1[n3], endPointsd1[n3] ] @@ -188,6 +196,14 @@ def createAnnulusMesh3d(nodes, mesh, nextNodeIdentifier, nextElementIdentifier, pd3[n3] = [ startPointsd3[n3] if (startPointsd3 is not None) else None, \ endPointsd3[n3] if (endPointsd3 is not None) else None ] + startThicknessList = [vector.magnitude([(startPointsx[n3][n1][c] - startPointsx[n3 - (1 if n3 > 0 else 0)][n1][c]) for c in range(3)]) for n1 in range(len(startPointsx[n3]))] + endThicknessList = [vector.magnitude([(endPointsx[n3][n1][c] - endPointsx[n3 - (1 if n3 > 0 else 0)][n1][c]) for c in range(3)]) for n1 in range(len(endPointsx[n3]))] + thicknessList = [startThicknessList, endThicknessList] # thickness of each layer + + startThicknessProportions = [thicknessList[0][c] / thicknesses[0][c] for c in range(nodesCountAround)] + endThicknessProportions = [thicknessList[1][c] / thicknesses[-1][c] for c in range(nodesCountAround)] + thicknessProportions.append([startThicknessProportions, endThicknessProportions]) + if rescaleStartDerivatives: scaleFactorMapStart = [ [] for n3 in range(nodesCountWall) ] if rescaleEndDerivatives: @@ -200,15 +216,11 @@ def createAnnulusMesh3d(nodes, mesh, nextNodeIdentifier, nextElementIdentifier, pd1[n3].insert(n2, [ None ]*nodesCountAround) pd2[n3].insert(n2, [ None ]*nodesCountAround) pd3[n3].insert(n2, None if midLinearXi3 else [ None ]*nodesCountAround) - # compute on outside / n3 = 1, then map to inside using thickness - thicknesses = [] - thicknesses.append([ vector.magnitude([ (startPointsx[nodesCountWall - 1][n1][c] - startPointsx[0][n1][c]) for c in range(3) ]) for n1 in range(nodesCountAround) ]) + thicknessProportions[n3].insert(n2, [None] * nodesCountAround) + if maxStartThickness: for n1 in range(nodesCountAround): thicknesses[0][n1] = min(thicknesses[0][n1], maxStartThickness) - for n2 in range(1, elementsCountRadial): - thicknesses.append([ None ]*nodesCountAround) - thicknesses.append([ vector.magnitude([ (endPointsx[nodesCountWall - 1][n1][c] - endPointsx[0][n1][c]) for c in range(3) ]) for n1 in range(nodesCountAround) ]) if maxEndThickness: for n1 in range(nodesCountAround): thicknesses[-1][n1] = min(thicknesses[-1][n1], maxEndThickness) @@ -245,13 +257,23 @@ def createAnnulusMesh3d(nodes, mesh, nextNodeIdentifier, nextElementIdentifier, arclengthInsideToOutside = sum(arcLengthInsideToRadialPoint) thi = [] for n2 in range(elementsCountRadial + 1): - xi = arcLengthInsideToRadialPoint[n2 - 1]/arclengthInsideToOutside - thi.append(thicknesses[0][n1]*xi + thicknesses[-1][n1]*(1.0 - xi)) + xi2 = arcLengthInsideToRadialPoint[n2 - 1] / arclengthInsideToOutside + thi.append(thicknesses[-1][n1] * xi2 + thicknesses[0][n1] * (1.0 - xi2)) + thiProportion = [] + for m3 in range(nodesCountWall): + thiProportionRadial = [] + for n2 in range(elementsCountRadial + 1): + xi2 = arcLengthInsideToRadialPoint[n2 - 1] / arclengthInsideToOutside + thiProportionRadial.append(thicknessProportions[m3][-1][n1] * xi2 + thicknessProportions[m3][0][n1] * (1.0 - xi2)) + thiProportion.append(thiProportionRadial) else: mx, md2, me, mxi = interp.sampleCubicHermiteCurvesSmooth([ ax, bx ], [ ad2Scaled, bd2Scaled ], elementsCountRadial, derivativeMagnitudeStart, derivativeMagnitudeEnd)[0:4] md1 = interp.interpolateSampleLinear([ ad1, bd1 ], me, mxi) thi = interp.interpolateSampleLinear([ thicknesses[0][n1], thicknesses[-1][n1] ], me, mxi) + thiProportion = [] + for m3 in range(nodesCountWall): + thiProportion.append(interp.interpolateSampleLinear([thicknessProportions[m3][0][n1], thicknessProportions[m3][-1][n1]], me, mxi)) # set scalefactors if rescaling, make same on inside for now if rescaleStartDerivatives: @@ -266,6 +288,17 @@ def createAnnulusMesh3d(nodes, mesh, nextNodeIdentifier, nextElementIdentifier, pd1[n3][n2][n1] = md1[n2] pd2[n3][n2][n1] = md2[n2] thicknesses[n2][n1] = thi[n2] + for m3 in range(nodesCountWall): + thicknessProportions[m3][n2][n1] = thiProportion[m3][n2] + + xi3List = [[[[] for n1 in range(nodesCountAround)] for n2 in range(elementsCountRadial + 1)] for n3 in + range(nodesCountWall)] + for n1 in range(nodesCountAround): + for n2 in range(elementsCountRadial + 1): + xi3 = 0.0 + for n3 in range(nodesCountWall): + xi3 += thicknessProportions[n3][n2][n1] + xi3List[n3][n2][n1] = xi3 # now get inner positions from normal and thickness, derivatives from curvature for n2 in range(1, elementsCountRadial): @@ -273,8 +306,8 @@ def createAnnulusMesh3d(nodes, mesh, nextNodeIdentifier, nextElementIdentifier, pd1[-1][n2] = interp.smoothCubicHermiteDerivativesLoop(px[-1][n2], pd1[-1][n2], magnitudeScalingMode=interp.DerivativeScalingMode.HARMONIC_MEAN) for n3 in range(0, nodesCountWall - 1): - xi3 = 1 - 1 / (nodesCountWall - 1) * n3 for n1 in range(nodesCountAround): + xi3 = 1 - xi3List[n3][n2][n1] normal = vector.normalise(vector.crossproduct3(pd1[-1][n2][n1], pd2[-1][n2][n1])) thickness = thicknesses[n2][n1] * xi3 d3 = [d * thickness for d in normal] @@ -299,7 +332,7 @@ def createAnnulusMesh3d(nodes, mesh, nextNodeIdentifier, nextElementIdentifier, if vector.dotproduct(vector.normalise(pd2[-1][n2][n1]), vector.normalise(d2Scaled)) == -1: pd2[n3][n2][n1] = [-factor * d for d in pd2[-1][n2][n1]] if not midLinearXi3: - pd3[n3][n2][n1] = pd3[-1][n2][n1] = [d * thicknesses[n2][n1] / (nodesCountWall - 1) for d in normal] + pd3[n3][n2][n1] = pd3[-1][n2][n1] = [d * thicknesses[n2][n1] * thicknessProportions[n3 + 1][n2][n1] for d in normal] # smooth derivative 1 around inner loop pd1[n3][n2] = interp.smoothCubicHermiteDerivativesLoop(px[n3][n2], pd1[n3][n2], magnitudeScalingMode=interp.DerivativeScalingMode.HARMONIC_MEAN) From 9b18db98dee52bcbe596734bd9ab3a48662fe58d Mon Sep 17 00:00:00 2001 From: Mabelle Lin Date: Sat, 30 Oct 2021 19:50:39 +1300 Subject: [PATCH 08/22] Update bladder mesh with ostium and annulus changes --- .../meshtypes/meshtype_3d_bladderurethra1.py | 15 +++++++++++++++ src/scaffoldmaker/utils/annulusmesh.py | 6 +++--- 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/src/scaffoldmaker/meshtypes/meshtype_3d_bladderurethra1.py b/src/scaffoldmaker/meshtypes/meshtype_3d_bladderurethra1.py index a888fbb2..0107d833 100644 --- a/src/scaffoldmaker/meshtypes/meshtype_3d_bladderurethra1.py +++ b/src/scaffoldmaker/meshtypes/meshtype_3d_bladderurethra1.py @@ -220,10 +220,12 @@ class MeshType_3d_bladderurethra1(Scaffold_base): 'Ostium diameter': 2.2, 'Ostium length': 0.5, 'Ostium wall thickness': 0.5, + 'Ostium wall relative thickness proportions': [1.0], 'Use linear through ostium wall': True, 'Vessel end length factor': 2.0, 'Vessel inner diameter': 0.8, 'Vessel wall thickness': 0.25, + 'Vessel wall relative thickness proportions': [1.0], 'Vessel angle 1 degrees': 0.0, 'Vessel angle 1 spread degrees': 0.0, 'Vessel angle 2 degrees': 0.0, @@ -246,10 +248,12 @@ class MeshType_3d_bladderurethra1(Scaffold_base): 'Ostium diameter': 3.0, 'Ostium length': 0.5, 'Ostium wall thickness': 0.5, + 'Ostium wall relative thickness proportions': [1.0], 'Use linear through ostium wall': True, 'Vessel end length factor': 2.0, 'Vessel inner diameter': 1.0, 'Vessel wall thickness': 0.1, + 'Vessel wall relative thickness proportions': [1.0], 'Vessel angle 1 degrees': 0.0, 'Vessel angle 1 spread degrees': 0.0, 'Vessel angle 2 degrees': 0.0, @@ -272,10 +276,12 @@ class MeshType_3d_bladderurethra1(Scaffold_base): 'Ostium diameter': 1.3, 'Ostium length': 0.25, 'Ostium wall thickness': 0.3, + 'Ostium wall relative thickness proportions': [1.0], 'Use linear through ostium wall': True, 'Vessel end length factor': 2.0, 'Vessel inner diameter': 0.45, 'Vessel wall thickness': 0.1, + 'Vessel wall relative thickness proportions': [1.0], 'Vessel angle 1 degrees': 0.0, 'Vessel angle 1 spread degrees': 0.0, 'Vessel angle 2 degrees': 0.0, @@ -298,10 +304,12 @@ class MeshType_3d_bladderurethra1(Scaffold_base): 'Ostium diameter': 2.0, 'Ostium length': 0.2, 'Ostium wall thickness': 0.4, + 'Ostium wall relative thickness proportions': [1.0], 'Use linear through ostium wall': True, 'Vessel end length factor': 2.0, 'Vessel inner diameter': 0.9, 'Vessel wall thickness': 0.2, + 'Ostium wall relative thickness proportions': [1.0], 'Vessel angle 1 degrees': 0.0, 'Vessel angle 1 spread degrees': 0.0, 'Vessel angle 2 degrees': 0.0, @@ -324,10 +332,12 @@ class MeshType_3d_bladderurethra1(Scaffold_base): 'Ostium diameter': 1.0, 'Ostium length': 0.25, 'Ostium wall thickness': 0.02, + 'Ostium wall relative thickness proportions': [1.0], 'Use linear through ostium wall': True, 'Vessel end length factor': 2.0, 'Vessel inner diameter': 0.3, 'Vessel wall thickness': 0.1, + 'Vessel wall relative thickness proportions': [1.0], 'Vessel angle 1 degrees': 0.0, 'Vessel angle 1 spread degrees': 0.0, 'Vessel angle 2 degrees': 0.0, @@ -1265,6 +1275,11 @@ def generateUreterInlets(region, nodes, mesh, ureterDefaultOptions,elementsCount xBladder, d1Bladder, d2Bladder, nextNodeIdentifier, nextElementIdentifier, elementsCountUreterRadial, ureterMeshGroup, bladderMeshGroup): + # Update ostium and vessel wall relative thickness with elementsCountThroughWall + # Set to uniform layer thicknesses now, can be changed to varying thickness later + ureterDefaultOptions['Ostium wall relative thickness proportions'] = [1.0 / elementsCountThroughWall] * elementsCountThroughWall + ureterDefaultOptions['Vessel wall relative thickness proportions'] = [1.0 / elementsCountThroughWall] * elementsCountThroughWall + # Create ureters on the surface # Ureter 1 centerUreter1_x, centerUreter1_d1, centerUreter1_d2 = trackSurfaceUreter1.evaluateCoordinates(ureter1Position, diff --git a/src/scaffoldmaker/utils/annulusmesh.py b/src/scaffoldmaker/utils/annulusmesh.py index 501233e3..d9d6ad96 100644 --- a/src/scaffoldmaker/utils/annulusmesh.py +++ b/src/scaffoldmaker/utils/annulusmesh.py @@ -271,9 +271,9 @@ def createAnnulusMesh3d(nodes, mesh, nextNodeIdentifier, nextElementIdentifier, derivativeMagnitudeStart, derivativeMagnitudeEnd)[0:4] md1 = interp.interpolateSampleLinear([ ad1, bd1 ], me, mxi) thi = interp.interpolateSampleLinear([ thicknesses[0][n1], thicknesses[-1][n1] ], me, mxi) - thiProportion = [] - for m3 in range(nodesCountWall): - thiProportion.append(interp.interpolateSampleLinear([thicknessProportions[m3][0][n1], thicknessProportions[m3][-1][n1]], me, mxi)) + thiProportion = [] + for m3 in range(nodesCountWall): + thiProportion.append(interp.interpolateSampleLinear([thicknessProportions[m3][0][n1], thicknessProportions[m3][-1][n1]], me, mxi)) # set scalefactors if rescaling, make same on inside for now if rescaleStartDerivatives: From a2164fae2536abaad2adf2f0493c7232722f57af Mon Sep 17 00:00:00 2001 From: Mabelle Lin Date: Sat, 30 Oct 2021 20:39:14 +1300 Subject: [PATCH 09/22] Allow varying element thicknesses through annulus wall in stomach --- .../meshtypes/meshtype_3d_stomach1.py | 75 ++++++++++++------- 1 file changed, 47 insertions(+), 28 deletions(-) diff --git a/src/scaffoldmaker/meshtypes/meshtype_3d_stomach1.py b/src/scaffoldmaker/meshtypes/meshtype_3d_stomach1.py index eb4f5ad7..8bf18100 100644 --- a/src/scaffoldmaker/meshtypes/meshtype_3d_stomach1.py +++ b/src/scaffoldmaker/meshtypes/meshtype_3d_stomach1.py @@ -223,18 +223,20 @@ class MeshType_3d_stomach1(Scaffold_base): 'Number of elements across common': 2, 'Number of elements around ostium': 8, 'Number of elements along': 2, - 'Number of elements through wall': 1, # not implemented for > 1 + 'Number of elements through wall': 4, 'Unit scale': 1.0, 'Outlet': False, 'Ostium diameter': 25.0, 'Ostium length': 15.0, 'Ostium wall thickness': 5.0, + 'Ostium wall relative thickness proportions': [0.65, 0.12, 0.18, 0.05], # update later with human data 'Ostium inter-vessel distance': 0.0, 'Ostium inter-vessel height': 0.0, 'Use linear through ostium wall': True, 'Vessel end length factor': 1.0, 'Vessel inner diameter': 5.0, 'Vessel wall thickness': 5.0, + 'Vessel wall relative thickness proportions': [0.65, 0.12, 0.18, 0.05], # update later with human data 'Vessel angle 1 degrees': 0.0, 'Vessel angle 1 spread degrees': 0.0, 'Vessel angle 2 degrees': 0.0, @@ -252,18 +254,20 @@ class MeshType_3d_stomach1(Scaffold_base): 'Number of elements across common': 2, 'Number of elements around ostium': 12, 'Number of elements along': 2, - 'Number of elements through wall': 1, # not implemented for > 1 + 'Number of elements through wall': 4, 'Unit scale': 1.0, 'Outlet': False, 'Ostium diameter': 1.5, 'Ostium length': 1.5, 'Ostium wall thickness': 0.25, + 'Ostium wall relative thickness proportions': [0.65, 0.12, 0.18, 0.05], # update later with mouse data 'Ostium inter-vessel distance': 0.0, 'Ostium inter-vessel height': 0.0, 'Use linear through ostium wall': True, 'Vessel end length factor': 1.0, 'Vessel inner diameter': 0.5, 'Vessel wall thickness': 0.2, + 'Vessel wall relative thickness proportions': [0.65, 0.12, 0.18, 0.05], # update later with mouse data 'Vessel angle 1 degrees': 0.0, 'Vessel angle 1 spread degrees': 0.0, 'Vessel angle 2 degrees': 0.0, @@ -281,18 +285,20 @@ class MeshType_3d_stomach1(Scaffold_base): 'Number of elements across common': 2, 'Number of elements around ostium': 12, 'Number of elements along': 2, - 'Number of elements through wall': 1, # not implemented for > 1 + 'Number of elements through wall': 4, 'Unit scale': 1.0, 'Outlet': False, 'Ostium diameter': 5.0, 'Ostium length': 5.0, 'Ostium wall thickness': 0.5, + 'Ostium wall relative thickness proportions': [0.65, 0.12, 0.18, 0.05], 'Ostium inter-vessel distance': 0.0, 'Ostium inter-vessel height': 0.0, 'Use linear through ostium wall': True, 'Vessel end length factor': 1.0, 'Vessel inner diameter': 2.0, 'Vessel wall thickness': 0.5, + 'Vessel wall relative thickness proportions': [0.65, 0.12, 0.18, 0.05], 'Vessel angle 1 degrees': 0.0, 'Vessel angle 1 spread degrees': 0.0, 'Vessel angle 2 degrees': 0.0, @@ -472,6 +478,19 @@ def updateSubScaffoldOptions(cls, options): ostiumOptions = options['Gastro-esophagal junction'] ostiumSettings = ostiumOptions.getScaffoldSettings() ostiumSettings['Ostium wall thickness'] = wallThickness + elementsCountThroughWall = options['Number of elements through wall'] + ostiumSettings['Number of elements through wall'] = elementsCountThroughWall + if elementsCountThroughWall == 1: + ostiumSettings['Ostium wall relative thickness proportions'] = [1.0] + ostiumSettings['Vessel wall relative thickness proportions'] = [1.0] + else: + mucosaRelThickness = options['Mucosa relative thickness'] + submucosaRelThickness = options['Submucosa relative thickness'] + circularRelThickness = options['Circular muscle layer relative thickness'] + longRelThickness = options['Longitudinal muscle layer relative thickness'] + relThicknesses = [mucosaRelThickness, submucosaRelThickness, circularRelThickness, longRelThickness] + ostiumSettings['Ostium wall relative thickness proportions'] = relThicknesses + ostiumSettings['Vessel wall relative thickness proportions'] = relThicknesses @classmethod def generateBaseMesh(cls, region, options): @@ -2425,24 +2444,23 @@ def generateBaseMesh(cls, region, options): # Annulus # Assemble endPoints for annulus - endPoints_x = [[None] * elementsCountAroundEso, [None] * elementsCountAroundEso] - endPoints_d1 = [[None] * elementsCountAroundEso, [None] * elementsCountAroundEso] - endPoints_d2 = [[None] * elementsCountAroundEso, [None] * elementsCountAroundEso] - endNode_Id = [[None] * elementsCountAroundEso, [None] * elementsCountAroundEso] - endDerivativesMap = [[None] * elementsCountAroundEso, [None] * elementsCountAroundEso] + endPoints_x = [[None] * elementsCountAroundEso for n3 in range(elementsCountThroughWall + 1)] + endPoints_d1 = [[None] * elementsCountAroundEso for n3 in range(elementsCountThroughWall + 1)] + endPoints_d2 = [[None] * elementsCountAroundEso for n3 in range(elementsCountThroughWall + 1)] + endNode_Id = [[None] * elementsCountAroundEso for n3 in range(elementsCountThroughWall + 1)] + endDerivativesMap = [[None] * elementsCountAroundEso for n3 in range(elementsCountThroughWall + 1)] endProportions = [] - thicknessIdx = [0, -1] for nAround in range(elementsCountAroundEso): - for n3 in range(len(thicknessIdx)): + for n3 in range(elementsCountThroughWall + 1): if nAround == 0: - idx = idxMat[nAround][thicknessIdx[n3]][0] + idx = idxMat[nAround][n3][0] elif nAround <= elementsAroundQuarterEso: - idx = idxMat[nAround][thicknessIdx[n3]][int((len(xOuter[nAround]) - 1) * 0.5)] + idx = idxMat[nAround][n3][int((len(xOuter[nAround]) - 1) * 0.5)] elif elementsAroundQuarterEso < nAround < elementsAroundHalfEso: - idx = idxMat[nAround + 1][thicknessIdx[n3]][int((len(xOuter[nAround + 1]) - 1) * 0.5)] + idx = idxMat[nAround + 1][n3][int((len(xOuter[nAround + 1]) - 1) * 0.5)] elif nAround == elementsAroundHalfEso: - idx = idxMat[nAround + 1][thicknessIdx[n3]][int(len(xOuter[nAround + 1]) * 0.5)] + idx = idxMat[nAround + 1][n3][int(len(xOuter[nAround + 1]) * 0.5)] elif nAround > elementsAroundHalfEso: idx = endNode_Id[n3][elementsAroundHalfEso - (nAround - elementsAroundHalfEso)] + 1 @@ -2451,23 +2469,24 @@ def generateBaseMesh(cls, region, options): endPoints_d2[n3][nAround] = d2List[idx - stomachStartNode] endNode_Id[n3][nAround] = idx - if n3 == len(thicknessIdx) - 1: # outer layer + if n3 == elementsCountThroughWall: # outer layer endPosition = trackSurfaceStomach.findNearestPosition(endPoints_x[n3][nAround]) endProportions.append(trackSurfaceStomach.getProportion(endPosition)) - for nAround in range(elementsCountAroundEso): - if nAround == 0: - endDerivativesMap[0][nAround] = endDerivativesMap[1][nAround] = ((0, -1, 0), (1, 0, 0), None) - elif nAround == elementsAroundQuarterEso: - endDerivativesMap[0][nAround] = endDerivativesMap[1][nAround] = ((0, 1, 0), (-1, 1, 0), None, (1, 0, 0)) - elif 0 < nAround < elementsAroundHalfEso: - endDerivativesMap[0][nAround] = endDerivativesMap[1][nAround] = ((0, 1, 0), (-1, 0, 0), None) - elif nAround == elementsAroundHalfEso: - endDerivativesMap[0][nAround] = endDerivativesMap[1][nAround] = ((1, 0, 0), (1, 1, 0), None, (0, -1, 0)) - elif nAround == int(elementsCountAroundEso * 0.75): - endDerivativesMap[0][nAround] = endDerivativesMap[1][nAround] = ((1, 0, 0), (1, 1, 0), None, (0, -1, 0)) - elif elementsAroundHalfEso < nAround < elementsCountAroundEso: - endDerivativesMap[0][nAround] = endDerivativesMap[1][nAround] = ((0, -1, 0), (1, 0, 0), None) + for n3 in range(elementsCountThroughWall + 1): + for nAround in range(elementsCountAroundEso): + if nAround == 0: + endDerivativesMap[n3][nAround] = ((0, -1, 0), (1, 0, 0), None) + elif nAround == elementsAroundQuarterEso: + endDerivativesMap[n3][nAround] = ((0, 1, 0), (-1, 1, 0), None, (1, 0, 0)) + elif 0 < nAround < elementsAroundHalfEso: + endDerivativesMap[n3][nAround] = ((0, 1, 0), (-1, 0, 0), None) + elif nAround == elementsAroundHalfEso: + endDerivativesMap[n3][nAround] = ((1, 0, 0), (1, 1, 0), None, (0, -1, 0)) + elif nAround == int(elementsCountAroundEso * 0.75): + endDerivativesMap[n3][nAround] = ((1, 0, 0), (1, 1, 0), None, (0, -1, 0)) + elif elementsAroundHalfEso < nAround < elementsCountAroundEso: + endDerivativesMap[n3][nAround] = ((0, -1, 0), (1, 0, 0), None) startProportions = [] for n in range(elementsCountAroundEso): From abf93e5c1d3e9e9de71c2f7236c6a1c99c323ab7 Mon Sep 17 00:00:00 2001 From: Mabelle Lin Date: Sat, 30 Oct 2021 22:20:49 +1300 Subject: [PATCH 10/22] Add annotation groups for annulus wall --- src/scaffoldmaker/annotation/stomach_terms.py | 7 ++- .../meshtypes/meshtype_3d_ostium1.py | 5 +- .../meshtypes/meshtype_3d_stomach1.py | 49 ++++++++++++++++--- src/scaffoldmaker/utils/annulusmesh.py | 12 ++++- 4 files changed, 63 insertions(+), 10 deletions(-) diff --git a/src/scaffoldmaker/annotation/stomach_terms.py b/src/scaffoldmaker/annotation/stomach_terms.py index 1b813a80..bd0905fe 100644 --- a/src/scaffoldmaker/annotation/stomach_terms.py +++ b/src/scaffoldmaker/annotation/stomach_terms.py @@ -12,9 +12,12 @@ ( "dorsal stomach", "None"), ( "duodenum", "UBERON:0002114", " FMA:7206", "ILX:0726125"), ( "duodenum on greater curvature", "None"), - ( "esophagus", "UBERON:0001043", "FMA: 7131", "ILX:0735017"), + ( "esophagus", "UBERON:0001043", "FMA:7131", "ILX:0735017"), + ( "esophagus mucosa", "UBERON:0002469", "FMA:62996", "ILX:0725079"), ( "esophagus on serosa split margin", "None"), ( "esophagogastric junction", "UBERON:0007650", "FMA: 9434", "ILX:0733910"), + ( "esophagus smooth muscle circular layer", "UBERON:0009960", "FMA:67605", "ILX:0735992"), + ( "esophagus smooth muscle longitudinal layer", "UBERON:0009961", "FMA:63573", "ILX:0727608"), ( "forestomach-glandular stomach junction", "UBERON:0012270", "ILX:0729974"), ( "forestomach-glandular stomach junction on inner wall", "None"), ( "forestomach-glandular stomach junction on outer wall", "None"), @@ -32,10 +35,12 @@ ( "pylorus on greater curvature", "None"), ( "pylorus on serosa split margin", "None"), ( "serosa of dorsal stomach", "None"), + ( "serosa of esophagus", "UBERON:0001975", "FMA:63057", "ILX:0725745"), ( "serosa of stomach", "UBERON:0001201", "FMA:14914", "ILX:0735818"), ( "serosa split margin", "None"), ( "serosa of ventral stomach", "None"), ( "stomach", "UBERON:0000945", "FMA:7148", "ILX:0736697"), + ( "submucosa of esophagus", "UBERON:0001972", "FMA:62997", "ILX:0728662"), ( "submucosa of stomach", "UBERON:0001200", "FMA:14908", "ILX:0732950"), ( "ventral stomach", "None"), ] diff --git a/src/scaffoldmaker/meshtypes/meshtype_3d_ostium1.py b/src/scaffoldmaker/meshtypes/meshtype_3d_ostium1.py index d606805f..a8c4424e 100644 --- a/src/scaffoldmaker/meshtypes/meshtype_3d_ostium1.py +++ b/src/scaffoldmaker/meshtypes/meshtype_3d_ostium1.py @@ -219,10 +219,11 @@ def getOstiumElementsCountsAroundVessels(elementsCountAroundOstium, elementsCoun def generateOstiumMesh(region, options, trackSurface, centrePosition, axis1, startNodeIdentifier = 1, startElementIdentifier = 1, - vesselMeshGroups = None, ostiumMeshGroups = None): + vesselMeshGroups = None, ostiumMeshGroups = None, wallAnnotationGroups = None): ''' :param vesselMeshGroups: List (over number of vessels) of list of mesh groups to add vessel elements to. :param ostiumMeshGroups: List of mesh groups to add only row of elements at ostium end to. + :param wallAnnotationGroups: list of annotation groups to add to wall elements. :return: nextNodeIdentifier, nextElementIdentifier, Ostium points tuple (ox[n3][n1][c], od1[n3][n1][c], od2[n3][n1][c], od3[n3][n1][c], oNodeId[n3][n1], oPositions). ''' @@ -819,7 +820,7 @@ def generateOstiumMesh(region, options, trackSurface, centrePosition, axis1, sta maxStartThickness = vesselWallThickness, maxEndThickness = vesselWallThickness, elementsCountRadial = elementsCountAlong, - meshGroups = rowMeshGroups) + meshGroups = rowMeshGroups, wallAnnotationGroups = wallAnnotationGroups) fm.endChange() return nodeIdentifier, elementIdentifier, (ox, od1, od2, od3, oNodeId, oPositions) diff --git a/src/scaffoldmaker/meshtypes/meshtype_3d_stomach1.py b/src/scaffoldmaker/meshtypes/meshtype_3d_stomach1.py index 8bf18100..53c58b37 100644 --- a/src/scaffoldmaker/meshtypes/meshtype_3d_stomach1.py +++ b/src/scaffoldmaker/meshtypes/meshtype_3d_stomach1.py @@ -757,10 +757,27 @@ def generateBaseMesh(cls, region, options): stomachMeshGroup = stomachGroup.getMeshGroup(mesh) allAnnotationGroups += [esophagusGroup, esophagogastricJunctionGroup] + ostiumWallAnnotationGroups = [] + if elementsCountThroughWall == 4: + esophagusMucosaGroup = AnnotationGroup(region, get_stomach_term("esophagus mucosa")) + esophagusSubmucosaGroup = AnnotationGroup(region, get_stomach_term("submucosa of esophagus")) + esophagusCircularGroup = AnnotationGroup(region, get_stomach_term("esophagus smooth muscle circular layer")) + esophagusLongitudinalGroup = AnnotationGroup(region, get_stomach_term("esophagus smooth muscle longitudinal layer")) + + ostiumWallAnnotationGroups = [[esophagusMucosaGroup, mucosaGroup], + [esophagusSubmucosaGroup, submucosaGroup], + [esophagusCircularGroup, circularMuscleGroup], + [esophagusLongitudinalGroup, longitudinalMuscleGroup]] + + allAnnotationGroups += [esophagusMucosaGroup, esophagusSubmucosaGroup, + esophagusCircularGroup, esophagusLongitudinalGroup] + nextNodeIdentifier, nextElementIdentifier, (o1_x, o1_d1, o1_d2, o1_d3, o1_NodeId, o1_Positions) = \ generateOstiumMesh(region, GEJSettings, trackSurfaceStomach, GEJPosition, axis1, - nodeIdentifier, elementIdentifier, vesselMeshGroups=[[stomachMeshGroup, esophagusMeshGroup]], - ostiumMeshGroups=[stomachMeshGroup, esophagogastricJunctionMeshGroup]) + nodeIdentifier, elementIdentifier, + vesselMeshGroups=[[stomachMeshGroup, esophagusMeshGroup]], + ostiumMeshGroups=[stomachMeshGroup, esophagogastricJunctionMeshGroup], + wallAnnotationGroups= ostiumWallAnnotationGroups) stomachStartNode = nextNodeIdentifier stomachStartElement = nextElementIdentifier @@ -2498,13 +2515,31 @@ def generateBaseMesh(cls, region, options): lastDuodenumElementIdentifier = elementIdentifier + stomachWallAnnotationGroups = [] + if elementsCountThroughWall == 4: + stomachWallAnnotationGroups = [[mucosaGroup], [submucosaGroup], [circularMuscleGroup], [longitudinalMuscleGroup]] + + # Remove mucosa layer from annulus + if elementsCountThroughWall == 4 and limitingRidge: + o1_x = o1_x[1:] + o1_d1 = o1_d1[1:] + o1_d2 = o1_d2[1:] + o1_NodeId = o1_NodeId[1:] + endPoints_x = endPoints_x[1:] + endPoints_d1 = endPoints_d1[1:] + endPoints_d2 = endPoints_d2[1:] + endNode_Id = endNode_Id[1:] + endDerivativesMap = endDerivativesMap[1:] + stomachWallAnnotationGroups = stomachWallAnnotationGroups[1:] + nextNodeIdentifier, nextElementIdentifier = createAnnulusMesh3d( nodes, mesh, nodeIdentifier, elementIdentifier, o1_x, o1_d1, o1_d2, None, o1_NodeId, None, endPoints_x, endPoints_d1, endPoints_d2, None, endNode_Id, endDerivativesMap, elementsCountRadial = elementsCountAcrossCardia, meshGroups= [stomachMeshGroup, cardiaMeshGroup], - tracksurface=trackSurfaceStomach, startProportions = startProportions, endProportions = endProportions, - rescaleStartDerivatives = True, rescaleEndDerivatives = True) + wallAnnotationGroups=stomachWallAnnotationGroups, tracksurface=trackSurfaceStomach, + startProportions=startProportions, endProportions=endProportions, + rescaleStartDerivatives=True, rescaleEndDerivatives=True) elementIdxThroughWall = [] n = lastDuodenumElementIdentifier - 1 @@ -2816,15 +2851,15 @@ def defineFaceAnnotations(cls, region, options, annotationGroups): get_stomach_term("serosa of dorsal stomach")) serosaVentralGroup = findOrCreateAnnotationGroupForTerm(annotationGroups, region, get_stomach_term("serosa of ventral stomach")) + esoSerosaGroup = findOrCreateAnnotationGroupForTerm(annotationGroups, region, + get_stomach_term("serosa of esophagus")) stomachGroup = getAnnotationGroupForTerm(annotationGroups, get_stomach_term("stomach")) bodyGroup = getAnnotationGroupForTerm(annotationGroups, get_stomach_term("body of stomach")) - esoGroup = getAnnotationGroupForTerm(annotationGroups, get_stomach_term("esophagus")) fundusGroup = getAnnotationGroupForTerm(annotationGroups, get_stomach_term("fundus of stomach")) antrumGroup = getAnnotationGroupForTerm(annotationGroups, get_stomach_term("pyloric antrum")) pylorusGroup = getAnnotationGroupForTerm(annotationGroups, get_stomach_term("pylorus")) esoGroup = getAnnotationGroupForTerm(annotationGroups, get_stomach_term("esophagus")) - esoGroup = getAnnotationGroupForTerm(annotationGroups, get_stomach_term("esophagus")) dorsalGroup = getAnnotationGroupForTerm(annotationGroups, get_stomach_term("dorsal stomach")) ventralGroup = getAnnotationGroupForTerm(annotationGroups, get_stomach_term("ventral stomach")) @@ -2870,6 +2905,8 @@ def defineFaceAnnotations(cls, region, options, annotationGroups): is_pylorusMargin = fm.createFieldAnd(is_marginOuter, is_pylorus) pylorusSplitMarginGroup.getMeshGroup(mesh1d).addElementsConditional(is_pylorusMargin) + is_esoSerosa = fm.createFieldAnd(is_exterior_face_outer, is_eso) + esoSerosaGroup.getMeshGroup(mesh2d).addElementsConditional(is_esoSerosa) def findClosestPositionAndDerivativeOnTrackSurface(x, nx, trackSurface, nxProportion1, elementsCountAlongTrackSurface): """ diff --git a/src/scaffoldmaker/utils/annulusmesh.py b/src/scaffoldmaker/utils/annulusmesh.py index d9d6ad96..4938a4d8 100644 --- a/src/scaffoldmaker/utils/annulusmesh.py +++ b/src/scaffoldmaker/utils/annulusmesh.py @@ -77,7 +77,8 @@ def createAnnulusMesh3d(nodes, mesh, nextNodeIdentifier, nextElementIdentifier, endPointsx, endPointsd1, endPointsd2, endPointsd3, endNodeId, endDerivativesMap, forceStartLinearXi3 = False, forceMidLinearXi3 = False, forceEndLinearXi3 = False, maxStartThickness = None, maxEndThickness = None, useCrossDerivatives = False, - elementsCountRadial = 1, meshGroups = None, tracksurface = None, startProportions = None, endProportions = None, + elementsCountRadial=1, meshGroups=None, wallAnnotationGroups=None, tracksurface=None, + startProportions=None, endProportions=None, rescaleStartDerivatives = False, rescaleEndDerivatives = False, sampleBlend = 0.0): """ Create an annulus mesh from a loop of start points/nodes with specified derivative mappings to @@ -110,6 +111,8 @@ def createAnnulusMesh3d(nodes, mesh, nextNodeIdentifier, nextElementIdentifier, :param meshGroups: Optional sequence of Zinc MeshGroup for adding all new elements to, or a sequence of length elementsCountRadial containing sequences of mesh groups to add rows of radial elements to from start to end. + :param wallAnnotationGroups: Annotation groups for adding all new elements to a sequence + of groups to add to elements through wall. :param tracksurface: Description for outer surface representation used for creating annulus mesh. Provides information for creating radial nodes on annulus that sit on tracksurface. Need startProportions and endProportions to work. @@ -165,6 +168,8 @@ def createAnnulusMesh3d(nodes, mesh, nextNodeIdentifier, nextElementIdentifier, rowMeshGroups = [ meshGroups ]*elementsCountRadial else: assert len(meshGroups) == elementsCountRadial, 'createAnnulusMesh3d: Length of meshGroups sequence does not equal elementsCountRadial' + if wallAnnotationGroups: + assert len(wallAnnotationGroups) == nodesCountWall - 1, 'createAnnulusMesh3d: Length of wallAnnotationGroups sequence does not equal elementsCountThroughWall' if tracksurface: assert startProportions and endProportions, 'createAnnulusMesh3d: Missing start and/or end proportions for use with tracksurface' assert len(startProportions) == nodesCountAround, 'createAnnulusMesh3d: Length of startProportions does not equal nodesCountAround' @@ -577,6 +582,11 @@ def createAnnulusMesh3d(nodes, mesh, nextNodeIdentifier, nextElementIdentifier, for meshGroup in rowMeshGroups[e2]: meshGroup.addElement(element) + if wallAnnotationGroups: + for annotationGroup in wallAnnotationGroups[e3]: + meshGroup = annotationGroup.getMeshGroup(mesh) + meshGroup.addElement(element) + fm.endChange() return nodeIdentifier, elementIdentifier From cc00f78eae9e2fb7b695a548877ccd21973bf0b8 Mon Sep 17 00:00:00 2001 From: Mabelle Lin Date: Mon, 1 Nov 2021 16:47:20 +1300 Subject: [PATCH 11/22] Modify code to correctly annotate limiting ridge --- .../meshtypes/meshtype_3d_stomach1.py | 115 +++++++++--------- 1 file changed, 59 insertions(+), 56 deletions(-) diff --git a/src/scaffoldmaker/meshtypes/meshtype_3d_stomach1.py b/src/scaffoldmaker/meshtypes/meshtype_3d_stomach1.py index 53c58b37..78248cfc 100644 --- a/src/scaffoldmaker/meshtypes/meshtype_3d_stomach1.py +++ b/src/scaffoldmaker/meshtypes/meshtype_3d_stomach1.py @@ -2795,67 +2795,23 @@ def defineFaceAnnotations(cls, region, options, annotationGroups): ''' limitingRidge = options['Limiting ridge'] + elementsCountThroughWall = options['Number of elements through wall'] - if limitingRidge: - fm = region.getFieldmodule() - fundusGroup = getAnnotationGroupForTerm(annotationGroups, get_stomach_term("fundus of stomach")) - bodyGroup = getAnnotationGroupForTerm(annotationGroups, get_stomach_term("body of stomach")) - cardiaGroup = getAnnotationGroupForTerm(annotationGroups, get_stomach_term("cardia of stomach")) - antrumGroup = getAnnotationGroupForTerm(annotationGroups, get_stomach_term("pyloric antrum")) - - limitingRidgeGroup = findOrCreateAnnotationGroupForTerm(annotationGroups, region, get_stomach_term("forestomach-glandular stomach junction")) - innerLimitingRidgeGroup = findOrCreateAnnotationGroupForTerm(annotationGroups, region, get_stomach_term("forestomach-glandular stomach junction on inner wall")) - outerLimitingRidgeGroup = findOrCreateAnnotationGroupForTerm(annotationGroups, region, get_stomach_term("forestomach-glandular stomach junction on outer wall")) - - mesh2d = fm.findMeshByDimension(2) - is_fundus = fundusGroup.getGroup() - is_body = bodyGroup.getGroup() - is_cardia = cardiaGroup.getGroup() - is_antrum = antrumGroup.getGroup() - is_limitingRidgeBody = fm.createFieldAnd(is_fundus, is_body) - is_limitingRidgeCardia = fm.createFieldAnd(is_body, is_cardia) - is_limitingRidgeAntrum = fm.createFieldAnd(is_antrum, is_cardia) - is_limitingRidgeBodyCardia = fm.createFieldOr(is_limitingRidgeBody, is_limitingRidgeCardia) - is_limitingRidgeAntrumCardia = fm.createFieldOr(is_limitingRidgeAntrum, is_limitingRidgeCardia) - is_limitingRidge = fm.createFieldOr(is_limitingRidgeBodyCardia, is_limitingRidgeAntrumCardia) - limitingRidgeGroup.getMeshGroup(mesh2d).addElementsConditional(is_limitingRidge) - - mesh1d = fm.findMeshByDimension(1) - is_exterior = fm.createFieldIsExterior() - is_exterior_face_outer = fm.createFieldAnd(is_exterior, fm.createFieldIsOnFace(Element.FACE_TYPE_XI3_1)) - is_exterior_face_inner = fm.createFieldAnd(is_exterior, fm.createFieldIsOnFace(Element.FACE_TYPE_XI3_0)) - - is_limitingRidgeInner = fm.createFieldAnd(is_limitingRidge, is_exterior_face_inner) - innerLimitingRidgeGroup.getMeshGroup(mesh1d).addElementsConditional(is_limitingRidgeInner) - - is_limitingRidgeOuter = fm.createFieldAnd(is_limitingRidge, is_exterior_face_outer) - outerLimitingRidgeGroup.getMeshGroup(mesh1d).addElementsConditional(is_limitingRidgeOuter) + serosaGroup = findOrCreateAnnotationGroupForTerm(annotationGroups, region, get_stomach_term("serosa of stomach")) + outerSplitMarginGroup = findOrCreateAnnotationGroupForTerm(annotationGroups, region, get_stomach_term("serosa split margin")) + esoSplitMarginGroup = findOrCreateAnnotationGroupForTerm(annotationGroups, region, get_stomach_term("esophagus on serosa split margin")) + fundusSplitMarginGroup = findOrCreateAnnotationGroupForTerm(annotationGroups, region, get_stomach_term("fundus on serosa split margin")) + bodySplitMarginGroup = findOrCreateAnnotationGroupForTerm(annotationGroups, region, get_stomach_term("body on serosa split margin")) + antrumSplitMarginGroup = findOrCreateAnnotationGroupForTerm(annotationGroups, region, get_stomach_term("antrum on serosa split margin")) + pylorusSplitMarginGroup = findOrCreateAnnotationGroupForTerm(annotationGroups, region, get_stomach_term("pylorus on serosa split margin")) - serosaGroup = findOrCreateAnnotationGroupForTerm(annotationGroups, region, - get_stomach_term("serosa of stomach")) - - outerSplitMarginGroup = findOrCreateAnnotationGroupForTerm(annotationGroups, region, - get_stomach_term("serosa split margin")) - esoSplitMarginGroup = findOrCreateAnnotationGroupForTerm(annotationGroups, region, - get_stomach_term("esophagus on serosa split margin")) - fundusSplitMarginGroup = findOrCreateAnnotationGroupForTerm(annotationGroups, region, - get_stomach_term("fundus on serosa split margin")) - bodySplitMarginGroup = findOrCreateAnnotationGroupForTerm(annotationGroups, region, - get_stomach_term("body on serosa split margin")) - antrumSplitMarginGroup = findOrCreateAnnotationGroupForTerm(annotationGroups, region, - get_stomach_term("antrum on serosa split margin")) - pylorusSplitMarginGroup = findOrCreateAnnotationGroupForTerm(annotationGroups, region, - get_stomach_term("pylorus on serosa split margin")) - - serosaDorsalGroup = findOrCreateAnnotationGroupForTerm(annotationGroups, region, - get_stomach_term("serosa of dorsal stomach")) - serosaVentralGroup = findOrCreateAnnotationGroupForTerm(annotationGroups, region, - get_stomach_term("serosa of ventral stomach")) - esoSerosaGroup = findOrCreateAnnotationGroupForTerm(annotationGroups, region, - get_stomach_term("serosa of esophagus")) + serosaDorsalGroup = findOrCreateAnnotationGroupForTerm(annotationGroups, region, get_stomach_term("serosa of dorsal stomach")) + serosaVentralGroup = findOrCreateAnnotationGroupForTerm(annotationGroups, region, get_stomach_term("serosa of ventral stomach")) + esoSerosaGroup = findOrCreateAnnotationGroupForTerm(annotationGroups, region, get_stomach_term("serosa of esophagus")) stomachGroup = getAnnotationGroupForTerm(annotationGroups, get_stomach_term("stomach")) bodyGroup = getAnnotationGroupForTerm(annotationGroups, get_stomach_term("body of stomach")) + cardiaGroup = getAnnotationGroupForTerm(annotationGroups, get_stomach_term("cardia of stomach")) fundusGroup = getAnnotationGroupForTerm(annotationGroups, get_stomach_term("fundus of stomach")) antrumGroup = getAnnotationGroupForTerm(annotationGroups, get_stomach_term("pyloric antrum")) pylorusGroup = getAnnotationGroupForTerm(annotationGroups, get_stomach_term("pylorus")) @@ -2908,6 +2864,53 @@ def defineFaceAnnotations(cls, region, options, annotationGroups): is_esoSerosa = fm.createFieldAnd(is_exterior_face_outer, is_eso) esoSerosaGroup.getMeshGroup(mesh2d).addElementsConditional(is_esoSerosa) + if limitingRidge: + fm = region.getFieldmodule() + limitingRidgeGroup = findOrCreateAnnotationGroupForTerm(annotationGroups, region, get_stomach_term("forestomach-glandular stomach junction")) + innerLimitingRidgeGroup = findOrCreateAnnotationGroupForTerm(annotationGroups, region, get_stomach_term("forestomach-glandular stomach junction on inner wall")) + outerLimitingRidgeGroup = findOrCreateAnnotationGroupForTerm(annotationGroups, region, get_stomach_term("forestomach-glandular stomach junction on outer wall")) + + mesh2d = fm.findMeshByDimension(2) + is_cardia = cardiaGroup.getGroup() + is_limitingRidgeBody = fm.createFieldAnd(is_fundus, is_body) + is_limitingRidgeCardia = fm.createFieldAnd(is_body, is_cardia) + is_limitingRidgeAntrum = fm.createFieldAnd(is_antrum, is_cardia) + is_limitingRidgeBodyCardia = fm.createFieldOr(is_limitingRidgeBody, is_limitingRidgeCardia) + is_limitingRidgeAntrumCardia = fm.createFieldOr(is_limitingRidgeAntrum, is_limitingRidgeCardia) + is_limitingRidge = fm.createFieldOr(is_limitingRidgeBodyCardia, is_limitingRidgeAntrumCardia) + + if elementsCountThroughWall == 4: + mucosaGroup = getAnnotationGroupForTerm(annotationGroups, get_stomach_term("mucosa of stomach")) + is_mucosa = mucosaGroup.getGroup() + is_bodyMucosa = fm.createFieldAnd(is_body, is_mucosa) + is_antrumMucosa = fm.createFieldAnd(is_antrum, is_mucosa) + is_bodyAntrumMucosa = fm.createFieldOr(is_bodyMucosa, is_antrumMucosa) + + is_xi1Interior = fm.createFieldAnd(fm.createFieldIsOnFace(Element.FACE_TYPE_XI1_0), fm.createFieldIsOnFace(Element.FACE_TYPE_XI1_1)) + is_xi1All = fm.createFieldOr(fm.createFieldIsOnFace(Element.FACE_TYPE_XI1_0), fm.createFieldIsOnFace(Element.FACE_TYPE_XI1_1)) + is_xi1BodyAntrumMucosaAll = fm.createFieldAnd(is_bodyAntrumMucosa, is_xi1All) + is_limitingRidgeAroundCardia = fm.createFieldAnd(is_xi1BodyAntrumMucosaAll, fm.createFieldNot(is_xi1Interior)) + + is_xi2Interior = fm.createFieldAnd(fm.createFieldIsOnFace(Element.FACE_TYPE_XI2_0), fm.createFieldIsOnFace(Element.FACE_TYPE_XI2_1)) + is_xi2ZeroBodyMucosa = fm.createFieldAnd(fm.createFieldIsOnFace(Element.FACE_TYPE_XI2_0), is_bodyMucosa) + is_limitingRidgeBodyBoundary = fm.createFieldAnd(is_xi2ZeroBodyMucosa, fm.createFieldNot(is_xi2Interior)) + + is_limitingRidgeMucosa = fm.createFieldOr(is_limitingRidgeAroundCardia, is_limitingRidgeBodyBoundary) + is_limitingRidge = fm.createFieldOr(is_limitingRidge, is_limitingRidgeMucosa) + + limitingRidgeGroup.getMeshGroup(mesh2d).addElementsConditional(is_limitingRidge) + + mesh1d = fm.findMeshByDimension(1) + is_xi3Interior = fm.createFieldAnd(fm.createFieldIsOnFace(Element.FACE_TYPE_XI3_0), fm.createFieldIsOnFace(Element.FACE_TYPE_XI3_1)) + is_xi3ZeroLimitingRidge = fm.createFieldAnd(is_limitingRidge, fm.createFieldIsOnFace(Element.FACE_TYPE_XI3_0)) + is_xi3OneLimitingRidge = fm.createFieldAnd(is_limitingRidge, fm.createFieldIsOnFace(Element.FACE_TYPE_XI3_1)) + + is_limitingRidgeInner = fm.createFieldAnd(is_xi3ZeroLimitingRidge, fm.createFieldNot(is_xi3Interior)) + innerLimitingRidgeGroup.getMeshGroup(mesh1d).addElementsConditional(is_limitingRidgeInner) + + is_limitingRidgeOuter = fm.createFieldAnd(is_xi3OneLimitingRidge, fm.createFieldNot(is_xi3Interior)) + outerLimitingRidgeGroup.getMeshGroup(mesh1d).addElementsConditional(is_limitingRidgeOuter) + def findClosestPositionAndDerivativeOnTrackSurface(x, nx, trackSurface, nxProportion1, elementsCountAlongTrackSurface): """ Find the closest position and derivative around the tracksurface of a point sitting near the fundus of stomach. From acbed49094a45314d666fcea9f3a6f78a6d38ca9 Mon Sep 17 00:00:00 2001 From: Mabelle Lin Date: Tue, 2 Nov 2021 11:16:48 +1300 Subject: [PATCH 12/22] Update thickness parameters for human and mouse --- .../meshtypes/meshtype_3d_stomach1.py | 28 ++++++++++++------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/src/scaffoldmaker/meshtypes/meshtype_3d_stomach1.py b/src/scaffoldmaker/meshtypes/meshtype_3d_stomach1.py index 78248cfc..4357b25d 100644 --- a/src/scaffoldmaker/meshtypes/meshtype_3d_stomach1.py +++ b/src/scaffoldmaker/meshtypes/meshtype_3d_stomach1.py @@ -229,14 +229,14 @@ class MeshType_3d_stomach1(Scaffold_base): 'Ostium diameter': 25.0, 'Ostium length': 15.0, 'Ostium wall thickness': 5.0, - 'Ostium wall relative thickness proportions': [0.65, 0.12, 0.18, 0.05], # update later with human data + 'Ostium wall relative thickness proportions': [0.55, 0.15, 0.25, 0.05], 'Ostium inter-vessel distance': 0.0, 'Ostium inter-vessel height': 0.0, 'Use linear through ostium wall': True, 'Vessel end length factor': 1.0, 'Vessel inner diameter': 5.0, 'Vessel wall thickness': 5.0, - 'Vessel wall relative thickness proportions': [0.65, 0.12, 0.18, 0.05], # update later with human data + 'Vessel wall relative thickness proportions': [0.55, 0.15, 0.25, 0.05], 'Vessel angle 1 degrees': 0.0, 'Vessel angle 1 spread degrees': 0.0, 'Vessel angle 2 degrees': 0.0, @@ -259,15 +259,15 @@ class MeshType_3d_stomach1(Scaffold_base): 'Outlet': False, 'Ostium diameter': 1.5, 'Ostium length': 1.5, - 'Ostium wall thickness': 0.25, - 'Ostium wall relative thickness proportions': [0.65, 0.12, 0.18, 0.05], # update later with mouse data + 'Ostium wall thickness': 0.45, + 'Ostium wall relative thickness proportions': [0.75, 0.05, 0.15, 0.05], 'Ostium inter-vessel distance': 0.0, 'Ostium inter-vessel height': 0.0, 'Use linear through ostium wall': True, 'Vessel end length factor': 1.0, 'Vessel inner diameter': 0.5, - 'Vessel wall thickness': 0.2, - 'Vessel wall relative thickness proportions': [0.65, 0.12, 0.18, 0.05], # update later with mouse data + 'Vessel wall thickness': 0.45, + 'Vessel wall relative thickness proportions': [0.75, 0.05, 0.15, 0.05], 'Vessel angle 1 degrees': 0.0, 'Vessel angle 1 spread degrees': 0.0, 'Vessel angle 2 degrees': 0.0, @@ -344,9 +344,9 @@ def getDefaultOptions(cls, parameterSetName='Default'): 'Number of elements across cardia': 1, 'Number of elements through wall': 4, 'Wall thickness': 5.0, - 'Mucosa relative thickness': 0.65, - 'Submucosa relative thickness': 0.12, - 'Circular muscle layer relative thickness': 0.18, + 'Mucosa relative thickness': 0.55, + 'Submucosa relative thickness': 0.15, + 'Circular muscle layer relative thickness': 0.25, 'Longitudinal muscle layer relative thickness': 0.05, 'Limiting ridge': False, 'Gastro-esophagal junction': copy.deepcopy(ostiumOption), @@ -361,7 +361,11 @@ def getDefaultOptions(cls, parameterSetName='Default'): options['Number of elements around esophagus'] = 12 options['Number of elements around duodenum'] = 14 options['Number of elements between cardia and duodenum'] = 2 - options['Wall thickness'] = 0.25 + options['Wall thickness'] = 0.45 + options['Mucosa relative thickness'] = 0.75 + options['Submucosa relative thickness'] = 0.05 + options['Circular muscle layer relative thickness'] = 0.15 + options['Longitudinal muscle layer relative thickness'] = 0.05 options['Gastro-esophagal junction position along factor'] = 0.53 options['Cardia derivative factor'] = 0.3 options['Limiting ridge'] = True @@ -370,6 +374,10 @@ def getDefaultOptions(cls, parameterSetName='Default'): options['Number of elements around duodenum'] = 14 options['Number of elements between cardia and duodenum'] = 2 options['Wall thickness'] = 0.5 + options['Mucosa relative thickness'] = 0.65 + options['Submucosa relative thickness'] = 0.12 + options['Circular muscle layer relative thickness'] = 0.18 + options['Longitudinal muscle layer relative thickness'] = 0.05 options['Gastro-esophagal junction position along factor'] = 0.55 options['Cardia derivative factor'] = 0.2 options['Limiting ridge'] = True From f841b7712f746755194e9b2a6a3037981aaaca29 Mon Sep 17 00:00:00 2001 From: Mabelle Lin Date: Tue, 2 Nov 2021 17:49:06 +1300 Subject: [PATCH 13/22] Update relative thicknesses for human colon with data from Simon Brookes --- .../meshtypes/meshtype_3d_colonsegment1.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/scaffoldmaker/meshtypes/meshtype_3d_colonsegment1.py b/src/scaffoldmaker/meshtypes/meshtype_3d_colonsegment1.py index de0252f5..5071e62e 100644 --- a/src/scaffoldmaker/meshtypes/meshtype_3d_colonsegment1.py +++ b/src/scaffoldmaker/meshtypes/meshtype_3d_colonsegment1.py @@ -70,12 +70,12 @@ def getDefaultOptions(parameterSetName='Default'): 'Start tenia coli width derivative': 0.0, 'End tenia coli width': 10.0, 'End tenia coli width derivative': 0.0, - 'Tenia coli thickness': 1.6, + 'Tenia coli thickness': 0.6, 'Wall thickness': 1.6, - 'Mucosa relative thickness': 0.3, - 'Submucosa relative thickness': 0.3, - 'Circular muscle layer relative thickness': 0.3, - 'Longitudinal muscle layer relative thickness': 0.1, + 'Mucosa relative thickness': 0.18, + 'Submucosa relative thickness': 0.25, + 'Circular muscle layer relative thickness': 0.52, + 'Longitudinal muscle layer relative thickness': 0.05, 'Use cross derivatives' : False, 'Use linear through wall' : True, 'Refine' : False, From ed2713d7498c4bb43ccca4e91942d59eb792aa6f Mon Sep 17 00:00:00 2001 From: Mabelle Lin Date: Fri, 5 Nov 2021 15:38:40 +1300 Subject: [PATCH 14/22] Add annotations needed for fitting --- src/scaffoldmaker/annotation/stomach_terms.py | 74 +++-- .../meshtypes/meshtype_3d_stomach1.py | 302 +++++++++++------- 2 files changed, 240 insertions(+), 136 deletions(-) diff --git a/src/scaffoldmaker/annotation/stomach_terms.py b/src/scaffoldmaker/annotation/stomach_terms.py index bd0905fe..b97ba8b8 100644 --- a/src/scaffoldmaker/annotation/stomach_terms.py +++ b/src/scaffoldmaker/annotation/stomach_terms.py @@ -4,45 +4,77 @@ # convention: preferred name, preferred id, followed by any other ids and alternative names stomach_terms = [ - ( "antrum on serosa split margin", "None"), ( "body of stomach", "UBERON:0001161", " FMA:14560", "ILX:0724929"), - ( "body on serosa split margin", "None"), + ( "body-antrum junction along the greater curvature on circular-longitudinal muscle interface", "None"), + ( "body-antrum junction along the greater curvature on luminal surface", "None"), + ( "body-antrum junction along the greater curvature on serosa", "None"), ( "cardia of stomach", "UBERON:0001162", " FMA:14561", "ILX:0729096"), ( "circular muscle layer of stomach", "ILX:0774731"), - ( "dorsal stomach", "None"), + ( "circular-longitudinal muscle interface of body of stomach along the gastric-omentum attachment", "None"), + ( "circular-longitudinal muscle interface of dorsal stomach", "None"), + ( "circular-longitudinal muscle interface of duodenum along the gastric-omentum attachment", "None"), + ( "circular-longitudinal muscle interface of esophagus along the cut margin", "None"), + ( "circular-longitudinal muscle interface of fundus of stomach along the gastric-omentum attachment", "None"), + ( "circular-longitudinal muscle interface of gastroduodenal junction", "None"), + ( "circular-longitudinal muscle interface of pyloric antrum along the gastric-omentum attachment", "None"), + ( "circular-longitudinal muscle interface of pyloric canal along the gastric-omentum attachment", "None"), + ( "circular-longitudinal muscle interface of stomach", "None"), + ( "circular-longitudinal muscle interface of ventral stomach", "None"), + ( "dorsal stomach", "ILX:0793086"), ( "duodenum", "UBERON:0002114", " FMA:7206", "ILX:0726125"), - ( "duodenum on greater curvature", "None"), + ( "esophagogastric junction", "UBERON:0007650", "FMA: 9434", "ILX:0733910"), + ( "esophagogastric junction along the greater curvature on circular-longitudinal muscle interface", "None"), + ( "esophagogastric junction along the greater curvature on luminal surface", "None"), + ( "esophagogastric junction along the greater curvature on serosa", "None"), + ( "esophagogastric junction along the lesser curvature on circular-longitudinal muscle interface", "None"), + ( "esophagogastric junction along the lesser curvature on luminal surface", "None"), + ( "esophagogastric junction along the lesser curvature on serosa", "None"), ( "esophagus", "UBERON:0001043", "FMA:7131", "ILX:0735017"), ( "esophagus mucosa", "UBERON:0002469", "FMA:62996", "ILX:0725079"), - ( "esophagus on serosa split margin", "None"), - ( "esophagogastric junction", "UBERON:0007650", "FMA: 9434", "ILX:0733910"), ( "esophagus smooth muscle circular layer", "UBERON:0009960", "FMA:67605", "ILX:0735992"), ( "esophagus smooth muscle longitudinal layer", "UBERON:0009961", "FMA:63573", "ILX:0727608"), ( "forestomach-glandular stomach junction", "UBERON:0012270", "ILX:0729974"), - ( "forestomach-glandular stomach junction on inner wall", "None"), - ( "forestomach-glandular stomach junction on outer wall", "None"), ( "fundus of stomach", "UBERON:0001160", " FMA:14559", "ILX:0724443"), - ( "fundus on serosa split margin", "None"), - ( "gastro-esophagal junction on lesser curvature", "None"), - #( "greater curvature of stomach", "UBERON:0001164", "FMA: 14574", "ILX:0724395"), - ( "junction between fundus and body on greater curvature", "None"), - # ( "lesser curvature of stomach", "UBERON:0001163", "FMA: 14572", "ILX:0733753"), - ( "limiting ridge on greater curvature", "None"), + ( "fundus-body junction along the greater curvature on circular-longitudinal muscle interface", "None"), + ( "fundus-body junction along the greater curvature on luminal surface", "None"), + ( "fundus-body junction along the greater curvature on serosa", "None"), + ( "gastroduodenal junction", "UBERON:0012650", "FMA:17046", "ILX:0725406"), + ( "gastroduodenal junction along the greater curvature on circular-longitudinal muscle interface", "None"), + ( "gastroduodenal junction along the greater curvature on luminal surface", "None"), + ( "gastroduodenal junction along the greater curvature on serosa", "None"), + ( "gastroduodenal junction along the lesser curvature on circular-longitudinal muscle interface", "None"), + ( "gastroduodenal junction along the lesser curvature on luminal surface", "None"), + ( "gastroduodenal junction along the lesser curvature on serosa", "None"), + ( "limiting ridge along the greater curvature on circular-longitudinal muscle interface", "None"), + ( "limiting ridge along the greater curvature on luminal surface", "None"), + ( "limiting ridge along the greater curvature on serosa", "None"), + ( "limiting ridge on circular-longitudinal muscle interface", "None"), + ( "limiting ridge on luminal surface", "None"), + ( "limiting ridge on serosa", "None"), ( "longitudinal muscle layer of stomach", "ILX:0772619"), + ( "luminal surface of body of stomach", "None"), + ( "luminal surface of cardia of stomach", "None"), + ( "luminal surface of duodenum", "None"), + ( "luminal surface of esophagus", "None"), + ( "luminal surface of fundus of stomach", "None"), + ( "luminal surface of pyloric antrum", "None"), + ( "luminal surface of pyloric canal", "None"), + ( "luminal surface of stomach", "None"), ( "mucosa of stomach", "UBERON:0001199", "FMA:14907", "ILX:0736669"), - ( "pylorus", "UBERON:0001166", " FMA:14581", "ILX:0734150"), ( "pyloric antrum", "UBERON:0001165", " FMA:14579", "ILX:0728672"), - ( "pylorus on greater curvature", "None"), - ( "pylorus on serosa split margin", "None"), - ( "serosa of dorsal stomach", "None"), + ( "pyloric canal", "UBERON:0008858", "FMA:14580", "ILX:0735898"), + ( "serosa of body of stomach", "ILX:0771402"), + ( "serosa of cardia of stomach", "ILX:0776646"), + ( "serosa of duodenum", "UBERON:0003336", "FMA:14948", "ILX:0732373"), ( "serosa of esophagus", "UBERON:0001975", "FMA:63057", "ILX:0725745"), + ( "serosa of fundus of stomach", "UBERON:0012503", "FMA:17073", "ILX:0726906"), + ( "serosa of pyloric antrum", "ILX:0777005"), + ( "serosa of pyloric canal", "ILX:0775898"), ( "serosa of stomach", "UBERON:0001201", "FMA:14914", "ILX:0735818"), - ( "serosa split margin", "None"), - ( "serosa of ventral stomach", "None"), ( "stomach", "UBERON:0000945", "FMA:7148", "ILX:0736697"), ( "submucosa of esophagus", "UBERON:0001972", "FMA:62997", "ILX:0728662"), ( "submucosa of stomach", "UBERON:0001200", "FMA:14908", "ILX:0732950"), - ( "ventral stomach", "None"), + ( "ventral stomach", "ILX:0793085"), ] def get_stomach_term(name : str): diff --git a/src/scaffoldmaker/meshtypes/meshtype_3d_stomach1.py b/src/scaffoldmaker/meshtypes/meshtype_3d_stomach1.py index 4357b25d..17fb2cc1 100644 --- a/src/scaffoldmaker/meshtypes/meshtype_3d_stomach1.py +++ b/src/scaffoldmaker/meshtypes/meshtype_3d_stomach1.py @@ -88,8 +88,8 @@ class MeshType_3d_stomach1(Scaffold_base): '_AnnotationGroup': True, 'dimension': 1, 'identifierRanges': '6', - 'name': get_stomach_term('pylorus')[0], - 'ontId': get_stomach_term('pylorus')[1] + 'name': get_stomach_term('pyloric canal')[0], + 'ontId': get_stomach_term('pyloric canal')[1] }, { '_AnnotationGroup': True, @@ -146,8 +146,8 @@ class MeshType_3d_stomach1(Scaffold_base): '_AnnotationGroup': True, 'dimension': 1, 'identifierRanges': '7', - 'name': get_stomach_term('pylorus')[0], - 'ontId': get_stomach_term('pylorus')[1] + 'name': get_stomach_term('pyloric canal')[0], + 'ontId': get_stomach_term('pyloric canal')[1] }, { '_AnnotationGroup': True, @@ -203,8 +203,8 @@ class MeshType_3d_stomach1(Scaffold_base): '_AnnotationGroup': True, 'dimension': 1, 'identifierRanges': '7', - 'name': get_stomach_term('pylorus')[0], - 'ontId': get_stomach_term('pylorus')[1] + 'name': get_stomach_term('pyloric canal')[0], + 'ontId': get_stomach_term('pyloric canal')[1] }, { '_AnnotationGroup': True, @@ -565,7 +565,7 @@ def generateBaseMesh(cls, region, options): # Extract length of each group along stomach from central path arcLengthOfGroupsAlong = [] - stomachTermsAlong = [None, 'fundus of stomach', 'body of stomach', 'pyloric antrum', 'pylorus', 'duodenum'] + stomachTermsAlong = [None, 'fundus of stomach', 'body of stomach', 'pyloric antrum', 'pyloric canal', 'duodenum'] for i in range(len(stomachTermsAlong)): tmpRegion = region.createRegion() centralPath.generate(tmpRegion) @@ -607,7 +607,7 @@ def generateBaseMesh(cls, region, options): fundusGroup = AnnotationGroup(region, get_stomach_term("fundus of stomach")) bodyGroup = AnnotationGroup(region, get_stomach_term("body of stomach")) antrumGroup = AnnotationGroup(region, get_stomach_term("pyloric antrum")) - pylorusGroup = AnnotationGroup(region, get_stomach_term("pylorus")) + pylorusGroup = AnnotationGroup(region, get_stomach_term("pyloric canal")) duodenumGroup = AnnotationGroup(region, get_stomach_term("duodenum")) annotationGroupAlong = [[stomachGroup, fundusGroup], @@ -2297,8 +2297,8 @@ def generateBaseMesh(cls, region, options): result2 = element.setNodesByIdentifier(eft1, nodeIdentifiers) if scaleFactors: result3 = element.setScaleFactors(eft1, scaleFactors) - if e1 == 0: - fundusBodyJunctionElementIdentifier = elementIdentifier + if e3 == 0 and e1 == 0: + fundusBodyJunctionInnerElementIdentifier = elementIdentifier elementIdxAround.append(elementIdentifier) elementIdentifier += 1 annotationGroups = annotationGroupsAlong[e2] + annotationGroupsThroughWall[e3] @@ -2565,53 +2565,55 @@ def generateBaseMesh(cls, region, options): mesh_destroy_elements_and_nodes_by_identifiers(mesh, fundusMucosaElementIdentifiers) # annotation fiducial points for embedding in whole body - GEJLCGroup = findOrCreateAnnotationGroupForTerm(allAnnotationGroups, region, get_stomach_term("gastro-esophagal junction on lesser curvature")) - GEJLCElement = mesh.findElementByIdentifier(stomachStartElement - elementsAroundHalfEso - 1) - GEJLCXi = [1.0, 1.0, 1.0] - cache.setMeshLocation(GEJLCElement, GEJLCXi) - markerPoint = markerPoints.createNode(nodeIdentifier, markerTemplateInternal) - nodeIdentifier += 1 - cache.setNode(markerPoint) - markerName.assignString(cache, GEJLCGroup.getName()) - markerLocation.assignMeshLocation(cache, GEJLCElement, GEJLCXi) - for group in [stomachGroup, GEJLCGroup]: - group.getNodesetGroup(nodes).addNode(markerPoint) - - fundusBodyJunctionGroup = findOrCreateAnnotationGroupForTerm(allAnnotationGroups, region, get_stomach_term("limiting ridge on greater curvature" if limitingRidge else "junction between fundus and body on greater curvature")) - fundusBodyJunctionElement = mesh.findElementByIdentifier(fundusBodyJunctionElementIdentifier) - fundusBodyJunctionXi = [0.0, 0.0 if limitingRidge else 1.0, 1.0] - cache.setMeshLocation(fundusBodyJunctionElement, fundusBodyJunctionXi) - markerPoint = markerPoints.createNode(nodeIdentifier, markerTemplateInternal) - nodeIdentifier += 1 - cache.setNode(markerPoint) - markerName.assignString(cache, fundusBodyJunctionGroup.getName()) - markerLocation.assignMeshLocation(cache, fundusBodyJunctionElement, fundusBodyJunctionXi) - for group in [stomachGroup, fundusBodyJunctionGroup]: - group.getNodesetGroup(nodes).addNode(markerPoint) - - pylorusGCGroup = findOrCreateAnnotationGroupForTerm(allAnnotationGroups, region, get_stomach_term("pylorus on greater curvature")) - pylorusGCElement = mesh.findElementByIdentifier(lastDuodenumElementIdentifier - (elementsCountAlongGroups[-1] + 1) * elementsCountAroundDuod) - pylorusGCXi = [0.0, 1.0, 1.0] - cache.setMeshLocation(pylorusGCElement, pylorusGCXi) - markerPoint = markerPoints.createNode(nodeIdentifier, markerTemplateInternal) - nodeIdentifier += 1 - cache.setNode(markerPoint) - markerName.assignString(cache, pylorusGCGroup.getName()) - markerLocation.assignMeshLocation(cache, pylorusGCElement, pylorusGCXi) - for group in [stomachGroup, pylorusGCGroup]: - group.getNodesetGroup(nodes).addNode(markerPoint) - - duodenumGCGroup = findOrCreateAnnotationGroupForTerm(allAnnotationGroups, region, get_stomach_term("duodenum on greater curvature")) - duodenumGCElement = mesh.findElementByIdentifier(lastDuodenumElementIdentifier - elementsCountAroundDuod) - duodenumGCXi = [0.0, 1.0, 1.0] - cache.setMeshLocation(duodenumGCElement, duodenumGCXi) - markerPoint = markerPoints.createNode(nodeIdentifier, markerTemplateInternal) - nodeIdentifier += 1 - cache.setNode(markerPoint) - markerName.assignString(cache, duodenumGCGroup.getName()) - markerLocation.assignMeshLocation(cache, duodenumGCElement, duodenumGCXi) - for group in [stomachGroup, duodenumGCGroup]: - group.getNodesetGroup(nodes).addNode(markerPoint) + markerNames = [["esophagogastric junction along the greater curvature on luminal surface", + "esophagogastric junction along the lesser curvature on luminal surface", + "gastroduodenal junction along the greater curvature on luminal surface", + "gastroduodenal junction along the lesser curvature on luminal surface", + "body-antrum junction along the greater curvature on luminal surface", + "limiting ridge along the greater curvature on luminal surface" if limitingRidge else + "fundus-body junction along the greater curvature on luminal surface"]] + if elementsCountThroughWall == 4: + markerNames.append(["esophagogastric junction along the greater curvature on circular-longitudinal muscle interface", + "esophagogastric junction along the lesser curvature on circular-longitudinal muscle interface", + "gastroduodenal junction along the greater curvature on circular-longitudinal muscle interface", + "gastroduodenal junction along the lesser curvature on circular-longitudinal muscle interface", + "body-antrum junction along the greater curvature on circular-longitudinal muscle interface", + "limiting ridge along the greater curvature on circular-longitudinal muscle interface" if limitingRidge else + "fundus-body junction along the greater curvature on circular-longitudinal muscle interface"]) + markerNames.append(["esophagogastric junction along the greater curvature on serosa", + "esophagogastric junction along the lesser curvature on serosa", + "gastroduodenal junction along the greater curvature on serosa", + "gastroduodenal junction along the lesser curvature on serosa", + "body-antrum junction along the greater curvature on serosa", + "limiting ridge along the greater curvature on serosa" if limitingRidge else + "fundus-body junction along the greater curvature on serosa"]) + + markerInnerElementIdentifiers = [stomachStartElement - elementsCountThroughWall * elementsCountAroundEso, + stomachStartElement - (elementsCountThroughWall - 1) * elementsCountAroundEso - elementsAroundHalfEso, + lastDuodenumElementIdentifier - elementsCountThroughWall * elementsCountAroundDuod * (elementsCountAlongGroups[-1] + 1), + lastDuodenumElementIdentifier - elementsCountThroughWall * elementsCountAroundDuod * (elementsCountAlongGroups[-1] + 1) + elementsAroundHalfDuod, + lastDuodenumElementIdentifier - elementsCountThroughWall * elementsCountAroundDuod * (sum(elementsCountAlongGroups[-3:]) + 1), + fundusBodyJunctionInnerElementIdentifier] + + elementsCountAroundLayer = [elementsCountAroundEso, elementsCountAroundEso, + elementsCountAroundDuod, elementsCountAroundDuod, + elementsCountAroundDuod, elementsCountAroundDuod] + + for n3 in range(len(markerNames)): + for n in range(len(markerNames[n3])): + markerGroup = findOrCreateAnnotationGroupForTerm(allAnnotationGroups, region, get_stomach_term(markerNames[n3][n])) + markerElementIdentifier = markerInnerElementIdentifiers[n] + (0 if n3 == 0 or elementsCountThroughWall == 1 else elementsCountAroundLayer[n] * (elementsCountThroughWall - 1)) + markerElement = mesh.findElementByIdentifier(markerElementIdentifier) + markerXi = [0.0, 1.0, 0.0 if n3 != len(markerNames) - 1 else 1.0] if n < len(markerNames[n3]) - 1 else \ + [0.0, 0.0 if limitingRidge else 1.0, 0.0 if n3 != len(markerNames) - 1 else 1.0] + cache.setMeshLocation(markerElement, markerXi) + markerPoint = markerPoints.createNode(nodeIdentifier, markerTemplateInternal) + nodeIdentifier += 1 + cache.setNode(markerPoint) + markerName.assignString(cache, markerGroup.getName()) + markerLocation.assignMeshLocation(cache, markerElement, markerXi) + for group in [stomachGroup, markerGroup]: + group.getNodesetGroup(nodes).addNode(markerPoint) # Create annotation groups for dorsal and ventral parts of the stomach dorsalGroup = AnnotationGroup(region, get_stomach_term("dorsal stomach")) @@ -2625,9 +2627,9 @@ def generateBaseMesh(cls, region, options): elementIdx = elementIdxMat[e2][e3][e1] element = mesh.findElementByIdentifier(elementIdx) if e1 < 0.5 * len(elementIdxMat[e2][e3]): - dorsalMeshGroup.addElement(element) - else: ventralMeshGroup.addElement(element) + else: + dorsalMeshGroup.addElement(element) allAnnotationGroups.append(dorsalGroup) allAnnotationGroups.append(ventralGroup) @@ -2805,81 +2807,146 @@ def defineFaceAnnotations(cls, region, options, annotationGroups): limitingRidge = options['Limiting ridge'] elementsCountThroughWall = options['Number of elements through wall'] - serosaGroup = findOrCreateAnnotationGroupForTerm(annotationGroups, region, get_stomach_term("serosa of stomach")) - outerSplitMarginGroup = findOrCreateAnnotationGroupForTerm(annotationGroups, region, get_stomach_term("serosa split margin")) - esoSplitMarginGroup = findOrCreateAnnotationGroupForTerm(annotationGroups, region, get_stomach_term("esophagus on serosa split margin")) - fundusSplitMarginGroup = findOrCreateAnnotationGroupForTerm(annotationGroups, region, get_stomach_term("fundus on serosa split margin")) - bodySplitMarginGroup = findOrCreateAnnotationGroupForTerm(annotationGroups, region, get_stomach_term("body on serosa split margin")) - antrumSplitMarginGroup = findOrCreateAnnotationGroupForTerm(annotationGroups, region, get_stomach_term("antrum on serosa split margin")) - pylorusSplitMarginGroup = findOrCreateAnnotationGroupForTerm(annotationGroups, region, get_stomach_term("pylorus on serosa split margin")) - - serosaDorsalGroup = findOrCreateAnnotationGroupForTerm(annotationGroups, region, get_stomach_term("serosa of dorsal stomach")) - serosaVentralGroup = findOrCreateAnnotationGroupForTerm(annotationGroups, region, get_stomach_term("serosa of ventral stomach")) - esoSerosaGroup = findOrCreateAnnotationGroupForTerm(annotationGroups, region, get_stomach_term("serosa of esophagus")) - stomachGroup = getAnnotationGroupForTerm(annotationGroups, get_stomach_term("stomach")) + dorsalStomachGroup = getAnnotationGroupForTerm(annotationGroups, get_stomach_term("dorsal stomach")) + ventralStomachGroup = getAnnotationGroupForTerm(annotationGroups, get_stomach_term("ventral stomach")) bodyGroup = getAnnotationGroupForTerm(annotationGroups, get_stomach_term("body of stomach")) cardiaGroup = getAnnotationGroupForTerm(annotationGroups, get_stomach_term("cardia of stomach")) + duodenumGroup = getAnnotationGroupForTerm(annotationGroups, get_stomach_term("duodenum")) fundusGroup = getAnnotationGroupForTerm(annotationGroups, get_stomach_term("fundus of stomach")) antrumGroup = getAnnotationGroupForTerm(annotationGroups, get_stomach_term("pyloric antrum")) - pylorusGroup = getAnnotationGroupForTerm(annotationGroups, get_stomach_term("pylorus")) + pylorusGroup = getAnnotationGroupForTerm(annotationGroups, get_stomach_term("pyloric canal")) esoGroup = getAnnotationGroupForTerm(annotationGroups, get_stomach_term("esophagus")) - dorsalGroup = getAnnotationGroupForTerm(annotationGroups, get_stomach_term("dorsal stomach")) - ventralGroup = getAnnotationGroupForTerm(annotationGroups, get_stomach_term("ventral stomach")) + + # Create new groups + stomachLuminalGroup = findOrCreateAnnotationGroupForTerm(annotationGroups, region, + get_stomach_term("luminal surface of stomach")) + stomachSerosaGroup = findOrCreateAnnotationGroupForTerm(annotationGroups, region, + get_stomach_term("serosa of stomach")) + bodyLuminalGroup = findOrCreateAnnotationGroupForTerm(annotationGroups, region, + get_stomach_term("luminal surface of body of stomach")) + bodySerosaGroup = findOrCreateAnnotationGroupForTerm(annotationGroups, region, + get_stomach_term("serosa of body of stomach")) + cardiaLuminalGroup = findOrCreateAnnotationGroupForTerm(annotationGroups, region, + get_stomach_term( + "luminal surface of cardia of stomach")) + cardiaSerosaGroup = findOrCreateAnnotationGroupForTerm(annotationGroups, region, + get_stomach_term("serosa of cardia of stomach")) + duodenumLuminalGroup = findOrCreateAnnotationGroupForTerm(annotationGroups, region, + get_stomach_term("luminal surface of duodenum")) + duodenumSerosaGroup = findOrCreateAnnotationGroupForTerm(annotationGroups, region, + get_stomach_term("serosa of duodenum")) + esoLuminalGroup = findOrCreateAnnotationGroupForTerm(annotationGroups, region, + get_stomach_term("luminal surface of esophagus")) + esoSerosaGroup = findOrCreateAnnotationGroupForTerm(annotationGroups, region, + get_stomach_term("serosa of esophagus")) + fundusLuminalGroup = findOrCreateAnnotationGroupForTerm(annotationGroups, region, + get_stomach_term( + "luminal surface of fundus of stomach")) + fundusSerosaGroup = findOrCreateAnnotationGroupForTerm(annotationGroups, region, + get_stomach_term("serosa of fundus of stomach")) + antrumLuminalGroup = findOrCreateAnnotationGroupForTerm(annotationGroups, region, + get_stomach_term("luminal surface of pyloric antrum")) + antrumSerosaGroup = findOrCreateAnnotationGroupForTerm(annotationGroups, region, + get_stomach_term("serosa of pyloric antrum")) + pylorusLuminalGroup = findOrCreateAnnotationGroupForTerm(annotationGroups, region, + get_stomach_term("luminal surface of pyloric canal")) + pylorusSerosaGroup = findOrCreateAnnotationGroupForTerm(annotationGroups, region, + get_stomach_term("serosa of pyloric canal")) + gastroduodenalJunctionGroup = findOrCreateAnnotationGroupForTerm(annotationGroups, region, + get_stomach_term("gastroduodenal junction")) fm = region.getFieldmodule() mesh2d = fm.findMeshByDimension(2) - is_stomach = stomachGroup.getGroup() - is_exterior = fm.createFieldIsExterior() - is_exterior_face_outer = fm.createFieldAnd(is_exterior, fm.createFieldIsOnFace(Element.FACE_TYPE_XI3_1)) - is_serosa = fm.createFieldAnd(is_stomach, is_exterior_face_outer) - serosaGroup.getMeshGroup(mesh2d).addElementsConditional(is_serosa) - - is_dorsal = dorsalGroup.getGroup() - is_ventral = ventralGroup.getGroup() - is_dorsalSerosa = fm.createFieldAnd(is_dorsal, is_serosa) - is_ventralSerosa = fm.createFieldAnd(is_ventral, is_serosa) - serosaDorsalGroup.getMeshGroup(mesh2d).addElementsConditional(is_dorsalSerosa) - serosaVentralGroup.getMeshGroup(mesh2d).addElementsConditional(is_ventralSerosa) - - is_margin = fm.createFieldAnd(is_dorsal, is_ventral) mesh1d = fm.findMeshByDimension(1) + is_exterior = fm.createFieldIsExterior() is_exterior_face_outer = fm.createFieldAnd(is_exterior, fm.createFieldIsOnFace(Element.FACE_TYPE_XI3_1)) - is_marginOuter = fm.createFieldAnd(is_margin, is_exterior_face_outer) - outerSplitMarginGroup.getMeshGroup(mesh1d).addElementsConditional(is_marginOuter) - - is_eso = esoGroup.getGroup() - is_esoMargin = fm.createFieldAnd(is_marginOuter, is_eso) - esoSplitMarginGroup.getMeshGroup(mesh1d).addElementsConditional(is_esoMargin) - - is_fundus = fundusGroup.getGroup() - is_fundusMargin = fm.createFieldAnd(is_marginOuter, is_fundus) - fundusSplitMarginGroup.getMeshGroup(mesh1d).addElementsConditional(is_fundusMargin) + is_exterior_face_inner = fm.createFieldAnd(is_exterior, fm.createFieldIsOnFace(Element.FACE_TYPE_XI3_0)) - is_body = bodyGroup.getGroup() - is_bodyMargin = fm.createFieldAnd(is_marginOuter, is_body) - bodySplitMarginGroup.getMeshGroup(mesh1d).addElementsConditional(is_bodyMargin) + is_gastroduod = fm.createFieldAnd(duodenumGroup.getGroup(), pylorusGroup.getGroup()) + gastroduodenalJunctionGroup.getMeshGroup(mesh2d).addElementsConditional(is_gastroduod) - is_antrum = antrumGroup.getGroup() - is_antrumMargin = fm.createFieldAnd(is_marginOuter, is_antrum) - antrumSplitMarginGroup.getMeshGroup(mesh1d).addElementsConditional(is_antrumMargin) - - is_pylorus = pylorusGroup.getGroup() - is_pylorusMargin = fm.createFieldAnd(is_marginOuter, is_pylorus) - pylorusSplitMarginGroup.getMeshGroup(mesh1d).addElementsConditional(is_pylorusMargin) + if elementsCountThroughWall == 4: + is_dorsal = dorsalStomachGroup.getGroup() + is_ventral = ventralStomachGroup.getGroup() + is_curvatures = fm.createFieldAnd(is_dorsal, is_ventral) + CMLMInterfaceGroup = findOrCreateAnnotationGroupForTerm(annotationGroups, region, get_stomach_term( + "circular-longitudinal muscle interface of stomach")) + circularMuscleGroup = getAnnotationGroupForTerm(annotationGroups,get_stomach_term("circular muscle layer of stomach")) + longitudinalMuscleGroup = getAnnotationGroupForTerm(annotationGroups, get_stomach_term("longitudinal muscle layer of stomach")) + is_CM = circularMuscleGroup.getGroup() + is_LM = longitudinalMuscleGroup.getGroup() + is_CMLMInterface = fm.createFieldAnd(is_CM, is_LM) + CMLMInterfaceGroup.getMeshGroup(mesh2d).addElementsConditional(is_CMLMInterface) + + dorsalStomach_CMLMGroup = findOrCreateAnnotationGroupForTerm(annotationGroups, region, get_stomach_term( + "circular-longitudinal muscle interface of dorsal stomach")) + ventralStomach_CMLMGroup = findOrCreateAnnotationGroupForTerm(annotationGroups, region, get_stomach_term( + "circular-longitudinal muscle interface of ventral stomach")) + is_dorsal_CMLM = fm.createFieldAnd(is_dorsal, is_CMLMInterface) + dorsalStomach_CMLMGroup.getMeshGroup(mesh2d).addElementsConditional(is_dorsal_CMLM) + is_ventral_CMLM = fm.createFieldAnd(is_ventral, is_CMLMInterface) + ventralStomach_CMLMGroup.getMeshGroup(mesh2d).addElementsConditional(is_ventral_CMLM) + + gastroduod_CMLMGroup = findOrCreateAnnotationGroupForTerm(annotationGroups, region, get_stomach_term("circular-longitudinal muscle interface of gastroduodenal junction")) + is_gastroduod_CMLM = fm.createFieldAnd(is_gastroduod, is_CMLMInterface) + gastroduod_CMLMGroup.getMeshGroup(mesh1d).addElementsConditional(is_gastroduod_CMLM) + + is_curvatures_CMLM = fm.createFieldAnd(is_curvatures, is_CMLMInterface) + bodyCurvaturesCMLMGroup = findOrCreateAnnotationGroupForTerm(annotationGroups, region, + get_stomach_term( + "circular-longitudinal muscle interface of body of stomach along the gastric-omentum attachment")) + duodenumCurvaturesCMLMGroup = findOrCreateAnnotationGroupForTerm(annotationGroups, region, + get_stomach_term( + "circular-longitudinal muscle interface of duodenum along the gastric-omentum attachment")) + esoCurvaturesCMLMGroup = findOrCreateAnnotationGroupForTerm(annotationGroups, region, + get_stomach_term( + "circular-longitudinal muscle interface of esophagus along the cut margin")) + fundusCurvaturesCMLMGroup = findOrCreateAnnotationGroupForTerm(annotationGroups, region, + get_stomach_term( + "circular-longitudinal muscle interface of fundus of stomach along the gastric-omentum attachment")) + antrumCurvaturesCMLMGroup = findOrCreateAnnotationGroupForTerm(annotationGroups, region, + get_stomach_term( + "circular-longitudinal muscle interface of pyloric antrum along the gastric-omentum attachment")) + pylorusCurvaturesCMLMGroup = findOrCreateAnnotationGroupForTerm(annotationGroups, region, + get_stomach_term( + "circular-longitudinal muscle interface of pyloric canal along the gastric-omentum attachment")) + + sectionCurvaturesCMLMGroups = [None, bodyCurvaturesCMLMGroup, None, duodenumCurvaturesCMLMGroup, + esoCurvaturesCMLMGroup, fundusCurvaturesCMLMGroup, antrumCurvaturesCMLMGroup, + pylorusCurvaturesCMLMGroup] + + sectionGroups = [stomachGroup, bodyGroup, cardiaGroup, duodenumGroup, esoGroup, fundusGroup, antrumGroup, + pylorusGroup] + sectionSerosaGroups = [stomachSerosaGroup, bodySerosaGroup, cardiaSerosaGroup, duodenumSerosaGroup, + esoSerosaGroup, fundusSerosaGroup, antrumSerosaGroup, + pylorusSerosaGroup] + sectionLuminalGroups = [stomachLuminalGroup, bodyLuminalGroup, cardiaLuminalGroup, duodenumLuminalGroup, + esoLuminalGroup, fundusLuminalGroup, antrumLuminalGroup, + pylorusLuminalGroup] + + for i in range(len(sectionGroups)): + is_section = sectionGroups[i].getGroup() + is_sectionSerosa = fm.createFieldAnd(is_section, is_exterior_face_outer) + sectionSerosaGroups[i].getMeshGroup(mesh2d).addElementsConditional(is_sectionSerosa) + is_sectionLuminal = fm.createFieldAnd(is_section, is_exterior_face_inner) + sectionLuminalGroups[i].getMeshGroup(mesh2d).addElementsConditional(is_sectionLuminal) - is_esoSerosa = fm.createFieldAnd(is_exterior_face_outer, is_eso) - esoSerosaGroup.getMeshGroup(mesh2d).addElementsConditional(is_esoSerosa) + if elementsCountThroughWall == 4: + is_sectionCurvaturesCMLM = fm.createFieldAnd(is_section, is_curvatures_CMLM) + if sectionCurvaturesCMLMGroups[i]: + sectionCurvaturesCMLMGroups[i].getMeshGroup(mesh1d).addElementsConditional(is_sectionCurvaturesCMLM) if limitingRidge: - fm = region.getFieldmodule() limitingRidgeGroup = findOrCreateAnnotationGroupForTerm(annotationGroups, region, get_stomach_term("forestomach-glandular stomach junction")) - innerLimitingRidgeGroup = findOrCreateAnnotationGroupForTerm(annotationGroups, region, get_stomach_term("forestomach-glandular stomach junction on inner wall")) - outerLimitingRidgeGroup = findOrCreateAnnotationGroupForTerm(annotationGroups, region, get_stomach_term("forestomach-glandular stomach junction on outer wall")) + innerLimitingRidgeGroup = findOrCreateAnnotationGroupForTerm(annotationGroups, region, get_stomach_term("limiting ridge on luminal surface")) + outerLimitingRidgeGroup = findOrCreateAnnotationGroupForTerm(annotationGroups, region, get_stomach_term("limiting ridge on serosa")) - mesh2d = fm.findMeshByDimension(2) + is_antrum = antrumGroup.getGroup() + is_body = bodyGroup.getGroup() is_cardia = cardiaGroup.getGroup() + is_fundus = fundusGroup.getGroup() is_limitingRidgeBody = fm.createFieldAnd(is_fundus, is_body) is_limitingRidgeCardia = fm.createFieldAnd(is_body, is_cardia) is_limitingRidgeAntrum = fm.createFieldAnd(is_antrum, is_cardia) @@ -2908,7 +2975,6 @@ def defineFaceAnnotations(cls, region, options, annotationGroups): limitingRidgeGroup.getMeshGroup(mesh2d).addElementsConditional(is_limitingRidge) - mesh1d = fm.findMeshByDimension(1) is_xi3Interior = fm.createFieldAnd(fm.createFieldIsOnFace(Element.FACE_TYPE_XI3_0), fm.createFieldIsOnFace(Element.FACE_TYPE_XI3_1)) is_xi3ZeroLimitingRidge = fm.createFieldAnd(is_limitingRidge, fm.createFieldIsOnFace(Element.FACE_TYPE_XI3_0)) is_xi3OneLimitingRidge = fm.createFieldAnd(is_limitingRidge, fm.createFieldIsOnFace(Element.FACE_TYPE_XI3_1)) @@ -2919,6 +2985,12 @@ def defineFaceAnnotations(cls, region, options, annotationGroups): is_limitingRidgeOuter = fm.createFieldAnd(is_xi3OneLimitingRidge, fm.createFieldNot(is_xi3Interior)) outerLimitingRidgeGroup.getMeshGroup(mesh1d).addElementsConditional(is_limitingRidgeOuter) + if elementsCountThroughWall == 4: + limitingRidge_CMLMGroup = findOrCreateAnnotationGroupForTerm(annotationGroups, region, get_stomach_term( + "limiting ridge on circular-longitudinal muscle interface")) + is_limitingRidgeCMLM = fm.createFieldAnd(is_CMLMInterface, is_limitingRidge) + limitingRidge_CMLMGroup.getMeshGroup(mesh1d).addElementsConditional(is_limitingRidgeCMLM) + def findClosestPositionAndDerivativeOnTrackSurface(x, nx, trackSurface, nxProportion1, elementsCountAlongTrackSurface): """ Find the closest position and derivative around the tracksurface of a point sitting near the fundus of stomach. From 474a97ae7415b2e3e4eb8e786ac1671e16232086 Mon Sep 17 00:00:00 2001 From: Mabelle Lin Date: Fri, 5 Nov 2021 17:27:58 +1300 Subject: [PATCH 15/22] Rename relative thickness proportions to relative thicknesses in ostium --- .../meshtypes/meshtype_3d_bladderurethra1.py | 24 +++++++++---------- .../meshtypes/meshtype_3d_ostium1.py | 16 ++++++------- .../meshtypes/meshtype_3d_stomach1.py | 20 ++++++++-------- 3 files changed, 30 insertions(+), 30 deletions(-) diff --git a/src/scaffoldmaker/meshtypes/meshtype_3d_bladderurethra1.py b/src/scaffoldmaker/meshtypes/meshtype_3d_bladderurethra1.py index 0107d833..5602d9a7 100644 --- a/src/scaffoldmaker/meshtypes/meshtype_3d_bladderurethra1.py +++ b/src/scaffoldmaker/meshtypes/meshtype_3d_bladderurethra1.py @@ -220,12 +220,12 @@ class MeshType_3d_bladderurethra1(Scaffold_base): 'Ostium diameter': 2.2, 'Ostium length': 0.5, 'Ostium wall thickness': 0.5, - 'Ostium wall relative thickness proportions': [1.0], + 'Ostium wall relative thicknesses': [1.0], 'Use linear through ostium wall': True, 'Vessel end length factor': 2.0, 'Vessel inner diameter': 0.8, 'Vessel wall thickness': 0.25, - 'Vessel wall relative thickness proportions': [1.0], + 'Vessel wall relative thicknesses': [1.0], 'Vessel angle 1 degrees': 0.0, 'Vessel angle 1 spread degrees': 0.0, 'Vessel angle 2 degrees': 0.0, @@ -248,12 +248,12 @@ class MeshType_3d_bladderurethra1(Scaffold_base): 'Ostium diameter': 3.0, 'Ostium length': 0.5, 'Ostium wall thickness': 0.5, - 'Ostium wall relative thickness proportions': [1.0], + 'Ostium wall relative thicknesses': [1.0], 'Use linear through ostium wall': True, 'Vessel end length factor': 2.0, 'Vessel inner diameter': 1.0, 'Vessel wall thickness': 0.1, - 'Vessel wall relative thickness proportions': [1.0], + 'Vessel wall relative thicknesses': [1.0], 'Vessel angle 1 degrees': 0.0, 'Vessel angle 1 spread degrees': 0.0, 'Vessel angle 2 degrees': 0.0, @@ -276,12 +276,12 @@ class MeshType_3d_bladderurethra1(Scaffold_base): 'Ostium diameter': 1.3, 'Ostium length': 0.25, 'Ostium wall thickness': 0.3, - 'Ostium wall relative thickness proportions': [1.0], + 'Ostium wall relative thicknesses': [1.0], 'Use linear through ostium wall': True, 'Vessel end length factor': 2.0, 'Vessel inner diameter': 0.45, 'Vessel wall thickness': 0.1, - 'Vessel wall relative thickness proportions': [1.0], + 'Vessel wall relative thicknesses': [1.0], 'Vessel angle 1 degrees': 0.0, 'Vessel angle 1 spread degrees': 0.0, 'Vessel angle 2 degrees': 0.0, @@ -304,12 +304,12 @@ class MeshType_3d_bladderurethra1(Scaffold_base): 'Ostium diameter': 2.0, 'Ostium length': 0.2, 'Ostium wall thickness': 0.4, - 'Ostium wall relative thickness proportions': [1.0], + 'Ostium wall relative thicknesses': [1.0], 'Use linear through ostium wall': True, 'Vessel end length factor': 2.0, 'Vessel inner diameter': 0.9, 'Vessel wall thickness': 0.2, - 'Ostium wall relative thickness proportions': [1.0], + 'Ostium wall relative thicknesses': [1.0], 'Vessel angle 1 degrees': 0.0, 'Vessel angle 1 spread degrees': 0.0, 'Vessel angle 2 degrees': 0.0, @@ -332,12 +332,12 @@ class MeshType_3d_bladderurethra1(Scaffold_base): 'Ostium diameter': 1.0, 'Ostium length': 0.25, 'Ostium wall thickness': 0.02, - 'Ostium wall relative thickness proportions': [1.0], + 'Ostium wall relative thicknesses': [1.0], 'Use linear through ostium wall': True, 'Vessel end length factor': 2.0, 'Vessel inner diameter': 0.3, 'Vessel wall thickness': 0.1, - 'Vessel wall relative thickness proportions': [1.0], + 'Vessel wall relative thicknesses': [1.0], 'Vessel angle 1 degrees': 0.0, 'Vessel angle 1 spread degrees': 0.0, 'Vessel angle 2 degrees': 0.0, @@ -1277,8 +1277,8 @@ def generateUreterInlets(region, nodes, mesh, ureterDefaultOptions,elementsCount # Update ostium and vessel wall relative thickness with elementsCountThroughWall # Set to uniform layer thicknesses now, can be changed to varying thickness later - ureterDefaultOptions['Ostium wall relative thickness proportions'] = [1.0 / elementsCountThroughWall] * elementsCountThroughWall - ureterDefaultOptions['Vessel wall relative thickness proportions'] = [1.0 / elementsCountThroughWall] * elementsCountThroughWall + ureterDefaultOptions['Ostium wall relative thicknesses'] = [1.0 / elementsCountThroughWall] * elementsCountThroughWall + ureterDefaultOptions['Vessel wall relative thicknesses'] = [1.0 / elementsCountThroughWall] * elementsCountThroughWall # Create ureters on the surface # Ureter 1 diff --git a/src/scaffoldmaker/meshtypes/meshtype_3d_ostium1.py b/src/scaffoldmaker/meshtypes/meshtype_3d_ostium1.py index a8c4424e..34ca3116 100644 --- a/src/scaffoldmaker/meshtypes/meshtype_3d_ostium1.py +++ b/src/scaffoldmaker/meshtypes/meshtype_3d_ostium1.py @@ -38,14 +38,14 @@ def getDefaultOptions(cls, parameterSetName='Default'): 'Ostium diameter' : 1.0, 'Ostium length' : 0.4, 'Ostium wall thickness' : 0.08, - 'Ostium wall relative thickness proportions': [1.0], + 'Ostium wall relative thicknesses': [1.0], 'Ostium inter-vessel distance' : 0.8, 'Ostium inter-vessel height' : 0.0, 'Use linear through ostium wall' : False, 'Vessel end length factor' : 1.0, 'Vessel inner diameter' : 0.6, 'Vessel wall thickness' : 0.04, - 'Vessel wall relative thickness proportions': [1.0], + 'Vessel wall relative thicknesses': [1.0], 'Vessel angle 1 degrees' : 0.0, 'Vessel angle 1 spread degrees' : 0.0, 'Vessel angle 2 degrees' : 0.0, @@ -70,14 +70,14 @@ def getOrderedOptionNames(): 'Ostium diameter', 'Ostium length', 'Ostium wall thickness', - 'Ostium wall relative thickness proportions', + 'Ostium wall relative thicknesses', 'Ostium inter-vessel distance', 'Ostium inter-vessel height', 'Use linear through ostium wall', 'Vessel end length factor', 'Vessel inner diameter', 'Vessel wall thickness', - 'Vessel wall relative thickness proportions', + 'Vessel wall relative thicknesses', 'Vessel angle 1 degrees', 'Vessel angle 1 spread degrees', 'Vessel angle 2 degrees', @@ -125,8 +125,8 @@ def checkOptions(cls, options): if options['Ostium diameter'] <= 0.0: options['Ostium diameter'] = 0.000001 # avoid division by zero elementsThroughWall = options['Number of elements through wall'] - ostiumThicknessProportionsCountKey = 'Ostium wall relative thickness proportions' - vesselThicknessProportionsCountKey = 'Vessel wall relative thickness proportions' + ostiumThicknessProportionsCountKey = 'Ostium wall relative thicknesses' + vesselThicknessProportionsCountKey = 'Vessel wall relative thicknesses' ostiumWallCount = len(options[ostiumThicknessProportionsCountKey]) vesselWallCount = len(options[vesselThicknessProportionsCountKey]) if elementsThroughWall == 1: @@ -242,7 +242,7 @@ def generateOstiumMesh(region, options, trackSurface, centrePosition, axis1, sta ostiumRadius = 0.5*unitScale*options['Ostium diameter'] ostiumLength = unitScale*options['Ostium length'] ostiumWallThickness = unitScale*options['Ostium wall thickness'] - ostiumWallThicknessProportions = copy.deepcopy(options['Ostium wall relative thickness proportions']) + ostiumWallThicknessProportions = copy.deepcopy(options['Ostium wall relative thicknesses']) interVesselHeight = unitScale*options['Ostium inter-vessel height'] interVesselDistance = unitScale*options['Ostium inter-vessel distance'] if (vesselsCount > 1) else 0.0 halfInterVesselDistance = 0.5*interVesselDistance @@ -250,7 +250,7 @@ def generateOstiumMesh(region, options, trackSurface, centrePosition, axis1, sta vesselEndDerivative = ostiumLength*options['Vessel end length factor']/elementsCountAlong vesselInnerRadius = 0.5*unitScale*options['Vessel inner diameter'] vesselWallThickness = unitScale*options['Vessel wall thickness'] - vesselWallThicknessProportions = copy.deepcopy(options['Vessel wall relative thickness proportions']) + vesselWallThicknessProportions = copy.deepcopy(options['Vessel wall relative thicknesses']) vesselOuterRadius = vesselInnerRadius + vesselWallThickness vesselAngle1Radians = math.radians(options['Vessel angle 1 degrees']) vesselAngle1SpreadRadians = math.radians(options['Vessel angle 1 spread degrees']) diff --git a/src/scaffoldmaker/meshtypes/meshtype_3d_stomach1.py b/src/scaffoldmaker/meshtypes/meshtype_3d_stomach1.py index 17fb2cc1..cc72a22f 100644 --- a/src/scaffoldmaker/meshtypes/meshtype_3d_stomach1.py +++ b/src/scaffoldmaker/meshtypes/meshtype_3d_stomach1.py @@ -229,14 +229,14 @@ class MeshType_3d_stomach1(Scaffold_base): 'Ostium diameter': 25.0, 'Ostium length': 15.0, 'Ostium wall thickness': 5.0, - 'Ostium wall relative thickness proportions': [0.55, 0.15, 0.25, 0.05], + 'Ostium wall relative thicknesses': [0.55, 0.15, 0.25, 0.05], 'Ostium inter-vessel distance': 0.0, 'Ostium inter-vessel height': 0.0, 'Use linear through ostium wall': True, 'Vessel end length factor': 1.0, 'Vessel inner diameter': 5.0, 'Vessel wall thickness': 5.0, - 'Vessel wall relative thickness proportions': [0.55, 0.15, 0.25, 0.05], + 'Vessel wall relative thicknesses': [0.55, 0.15, 0.25, 0.05], 'Vessel angle 1 degrees': 0.0, 'Vessel angle 1 spread degrees': 0.0, 'Vessel angle 2 degrees': 0.0, @@ -260,14 +260,14 @@ class MeshType_3d_stomach1(Scaffold_base): 'Ostium diameter': 1.5, 'Ostium length': 1.5, 'Ostium wall thickness': 0.45, - 'Ostium wall relative thickness proportions': [0.75, 0.05, 0.15, 0.05], + 'Ostium wall relative thicknesses': [0.75, 0.05, 0.15, 0.05], 'Ostium inter-vessel distance': 0.0, 'Ostium inter-vessel height': 0.0, 'Use linear through ostium wall': True, 'Vessel end length factor': 1.0, 'Vessel inner diameter': 0.5, 'Vessel wall thickness': 0.45, - 'Vessel wall relative thickness proportions': [0.75, 0.05, 0.15, 0.05], + 'Vessel wall relative thicknesses': [0.75, 0.05, 0.15, 0.05], 'Vessel angle 1 degrees': 0.0, 'Vessel angle 1 spread degrees': 0.0, 'Vessel angle 2 degrees': 0.0, @@ -291,14 +291,14 @@ class MeshType_3d_stomach1(Scaffold_base): 'Ostium diameter': 5.0, 'Ostium length': 5.0, 'Ostium wall thickness': 0.5, - 'Ostium wall relative thickness proportions': [0.65, 0.12, 0.18, 0.05], + 'Ostium wall relative thicknesses': [0.65, 0.12, 0.18, 0.05], 'Ostium inter-vessel distance': 0.0, 'Ostium inter-vessel height': 0.0, 'Use linear through ostium wall': True, 'Vessel end length factor': 1.0, 'Vessel inner diameter': 2.0, 'Vessel wall thickness': 0.5, - 'Vessel wall relative thickness proportions': [0.65, 0.12, 0.18, 0.05], + 'Vessel wall relative thicknesses': [0.65, 0.12, 0.18, 0.05], 'Vessel angle 1 degrees': 0.0, 'Vessel angle 1 spread degrees': 0.0, 'Vessel angle 2 degrees': 0.0, @@ -489,16 +489,16 @@ def updateSubScaffoldOptions(cls, options): elementsCountThroughWall = options['Number of elements through wall'] ostiumSettings['Number of elements through wall'] = elementsCountThroughWall if elementsCountThroughWall == 1: - ostiumSettings['Ostium wall relative thickness proportions'] = [1.0] - ostiumSettings['Vessel wall relative thickness proportions'] = [1.0] + ostiumSettings['Ostium wall relative thicknesses'] = [1.0] + ostiumSettings['Vessel wall relative thicknesses'] = [1.0] else: mucosaRelThickness = options['Mucosa relative thickness'] submucosaRelThickness = options['Submucosa relative thickness'] circularRelThickness = options['Circular muscle layer relative thickness'] longRelThickness = options['Longitudinal muscle layer relative thickness'] relThicknesses = [mucosaRelThickness, submucosaRelThickness, circularRelThickness, longRelThickness] - ostiumSettings['Ostium wall relative thickness proportions'] = relThicknesses - ostiumSettings['Vessel wall relative thickness proportions'] = relThicknesses + ostiumSettings['Ostium wall relative thicknesses'] = relThicknesses + ostiumSettings['Vessel wall relative thicknesses'] = relThicknesses @classmethod def generateBaseMesh(cls, region, options): From 8c713b2d39660201d19c1712a88507d63fae9da8 Mon Sep 17 00:00:00 2001 From: Mabelle Lin Date: Fri, 5 Nov 2021 17:54:02 +1300 Subject: [PATCH 16/22] Update unit tests --- tests/test_bladder.py | 8 ++--- tests/test_cecum.py | 4 +-- tests/test_colon.py | 12 +++---- tests/test_colonsegment.py | 6 ++-- tests/test_stomach.py | 68 ++++++++++++++++++++------------------ 5 files changed, 50 insertions(+), 48 deletions(-) diff --git a/tests/test_bladder.py b/tests/test_bladder.py index 6ccb628b..592799b6 100644 --- a/tests/test_bladder.py +++ b/tests/test_bladder.py @@ -21,7 +21,7 @@ def test_bladderurethra1(self): """ scaffold = MeshType_3d_bladderurethra1 parameterSetNames = MeshType_3d_bladderurethra1.getParameterSetNames() - self.assertEqual(parameterSetNames, ["Default", "Cat 1", "Rat 1", "Human 1"]) + self.assertEqual(parameterSetNames, ["Default", "Cat 1", "Human 1", "Mouse 1", "Pig 1", "Rat 1",]) options = scaffold.getDefaultOptions("Cat 1") self.assertEqual(26, len(options)) self.assertEqual(12, options.get("Number of elements along bladder")) @@ -64,7 +64,7 @@ def test_bladderurethra1(self): self.assertTrue(coordinates.isValid()) minimums, maximums = evaluateFieldNodesetRange(coordinates, nodes) assertAlmostEqualList(self, minimums, [-15.48570141314588, -12.992184072505665, -0.5], 1.0E-6) - assertAlmostEqualList(self, maximums, [15.485696373577879, 13.837600783918127, 127.68599990411343], 1.0E-6) + assertAlmostEqualList(self, maximums, [15.485696373577879, 13.837536258199144, 127.68631532717487], 1.0E-6) with ChangeManager(fieldmodule): one = fieldmodule.createFieldConstant(1.0) @@ -76,10 +76,10 @@ def test_bladderurethra1(self): fieldcache = fieldmodule.createFieldcache() result, surfaceArea = surfaceAreaField.evaluateReal(fieldcache, 1) self.assertEqual(result, RESULT_OK) - self.assertAlmostEqual(surfaceArea, 5335.535739164492, delta=1.0E-6) + self.assertAlmostEqual(surfaceArea, 5334.9516480055845, delta=1.0E-6) result, volume = volumeField.evaluateReal(fieldcache, 1) self.assertEqual(result, RESULT_OK) - self.assertAlmostEqual(volume, 2555.81694845211, delta=1.0E-6) + self.assertAlmostEqual(volume, 2555.560965374249, delta=1.0E-6) # check some annotationGroups: expectedSizes3d = { diff --git a/tests/test_cecum.py b/tests/test_cecum.py index 37a933bd..1c5f5199 100644 --- a/tests/test_cecum.py +++ b/tests/test_cecum.py @@ -79,10 +79,10 @@ def test_cecum1(self): fieldcache = fieldmodule.createFieldcache() result, surfaceArea = surfaceAreaField.evaluateReal(fieldcache, 1) self.assertEqual(result, RESULT_OK) - self.assertAlmostEqual(surfaceArea, 65959.34314511667, delta=1.0E-6) + self.assertAlmostEqual(surfaceArea, 65960.20655074248, delta=1.0E-6) result, volume = volumeField.evaluateReal(fieldcache, 1) self.assertEqual(result, RESULT_OK) - self.assertAlmostEqual(volume, 127894.8597203706, delta=1.0E-6) + self.assertAlmostEqual(volume, 127905.28250502056, delta=1.0E-6) if __name__ == "__main__": unittest.main() diff --git a/tests/test_colon.py b/tests/test_colon.py index d73a87a9..43f9a74f 100644 --- a/tests/test_colon.py +++ b/tests/test_colon.py @@ -69,7 +69,7 @@ def test_colon1(self): self.assertEqual(0.5, segmentSettings.get("Haustrum inner radius factor")) self.assertEqual(0.5, segmentSettings.get("Segment length end derivative factor")) self.assertEqual(3, segmentSettings.get("Number of tenia coli")) - self.assertEqual(1.6, segmentSettings.get("Tenia coli thickness")) + self.assertEqual(0.6, segmentSettings.get("Tenia coli thickness")) self.assertEqual(3, options.get("Number of segments")) self.assertEqual(0.0, options.get("Start phase")) self.assertEqual(25.0, options.get("Transverse length")) @@ -112,14 +112,14 @@ def test_colon1(self): coordinates = fieldmodule.findFieldByName("coordinates").castFiniteElement() self.assertTrue(coordinates.isValid()) minimums, maximums = evaluateFieldNodesetRange(coordinates, nodes) - assertAlmostEqualList(self, minimums, [ 108.02506479907721, -36.876103983560014, -25.89741158484918 ], 1.0E-6) - assertAlmostEqualList(self, maximums, [ 185.46457506076914, 48.1011574894518, 34.995316052158934 ], 1.0E-6) + assertAlmostEqualList(self, minimums, [ 108.02506479907721, -36.405037279268456, -25.89741158484918 ], 1.0E-6) + assertAlmostEqualList(self, maximums, [ 185.46457506076914, 48.1011574894518, 34.05259862880112 ], 1.0E-6) flatCoordinates = fieldmodule.findFieldByName("flat coordinates").castFiniteElement() self.assertTrue(flatCoordinates.isValid()) minimums, maximums = evaluateFieldNodesetRange(flatCoordinates, nodes) assertAlmostEqualList(self, minimums, [ 0.0, 0.0, 0.0 ], 1.0E-6) - assertAlmostEqualList(self, maximums, [ 186.72988844629867, 77.41781871321301, 3.2000000000000006 ], 1.0E-6) + assertAlmostEqualList(self, maximums, [ 186.72988844629867, 77.41781871321301, 2.2 ], 1.0E-6) colonCoordinates = fieldmodule.findFieldByName("colon coordinates").castFiniteElement() minimums, maximums = evaluateFieldNodesetRange(colonCoordinates, nodes) @@ -136,10 +136,10 @@ def test_colon1(self): fieldcache = fieldmodule.createFieldcache() result, surfaceArea = surfaceAreaField.evaluateReal(fieldcache, 1) self.assertEqual(result, RESULT_OK) - self.assertAlmostEqual(surfaceArea, 14612.416789097502, delta=1.0E-6) + self.assertAlmostEqual(surfaceArea, 14342.540002125375, delta=1.0E-6) result, volume = volumeField.evaluateReal(fieldcache, 1) self.assertEqual(result, RESULT_OK) - self.assertAlmostEqual(volume, 26825.42839677291, delta=1.0E-6) + self.assertAlmostEqual(volume, 25983.483155342656, delta=1.0E-6) def test_mousecolon1(self): """ diff --git a/tests/test_colonsegment.py b/tests/test_colonsegment.py index 2f010ba6..1fcf0e7e 100644 --- a/tests/test_colonsegment.py +++ b/tests/test_colonsegment.py @@ -63,7 +63,7 @@ def test_humancolonsegment1(self): self.assertTrue(flatCoordinates.isValid()) minimums, maximums = evaluateFieldNodesetRange(flatCoordinates, nodes) assertAlmostEqualList(self, minimums, [ 0.0, 0.0, 0.0 ], 1.0E-6) - assertAlmostEqualList(self, maximums, [397.2736607240895, 50.0, 3.2000000000000006], 1.0E-6) + assertAlmostEqualList(self, maximums, [397.2736607240895, 50.0, 2.2], 1.0E-6) with ChangeManager(fieldmodule): one = fieldmodule.createFieldConstant(1.0) @@ -75,10 +75,10 @@ def test_humancolonsegment1(self): fieldcache = fieldmodule.createFieldcache() result, surfaceArea = surfaceAreaField.evaluateReal(fieldcache, 1) self.assertEqual(result, RESULT_OK) - self.assertAlmostEqual(surfaceArea, 21129.564192298032, delta=1.0E-6) + self.assertAlmostEqual(surfaceArea, 21035.8818469729, delta=1.0E-6) result, volume = volumeField.evaluateReal(fieldcache, 1) self.assertEqual(result, RESULT_OK) - self.assertAlmostEqual(volume, 40783.41115796618, delta=1.0E-6) + self.assertAlmostEqual(volume, 39781.704358310606, delta=1.0E-6) def test_mousecolonsegment1(self): """ diff --git a/tests/test_stomach.py b/tests/test_stomach.py index a6d535fd..023b9adf 100644 --- a/tests/test_stomach.py +++ b/tests/test_stomach.py @@ -21,9 +21,9 @@ def test_stomach1(self): """ scaffold = MeshType_3d_stomach1 parameterSetNames = scaffold.getParameterSetNames() - self.assertEqual(parameterSetNames, [ "Default", "Human 1", "Rat 1" ]) + self.assertEqual(parameterSetNames, [ "Default", "Human 1", "Mouse 1", "Rat 1" ]) options = scaffold.getDefaultOptions("Rat 1") - self.assertEqual(15, len(options)) + self.assertEqual(19, len(options)) self.assertEqual(12, options.get("Number of elements around esophagus")) self.assertEqual(14, options.get("Number of elements around duodenum")) self.assertEqual(2, options.get("Number of elements between cardia and duodenum")) @@ -33,13 +33,15 @@ def test_stomach1(self): ostiumOptions = options['Gastro-esophagal junction'] ostiumSettings = ostiumOptions.getScaffoldSettings() self.assertEqual(1, ostiumSettings.get("Number of vessels")) - self.assertEqual(8, ostiumSettings.get("Number of elements around ostium")) - self.assertEqual(1, ostiumSettings.get("Number of elements through wall")) - self.assertEqual(4.0, ostiumSettings.get("Ostium diameter")) - self.assertEqual(3.5, ostiumSettings.get("Ostium length")) + self.assertEqual(12, ostiumSettings.get("Number of elements around ostium")) + self.assertEqual(4, ostiumSettings.get("Number of elements through wall")) + self.assertEqual(5.0, ostiumSettings.get("Ostium diameter")) + self.assertEqual(5.0, ostiumSettings.get("Ostium length")) self.assertEqual(0.5, ostiumSettings.get("Ostium wall thickness")) - self.assertEqual(1.25, ostiumSettings.get("Vessel inner diameter")) + self.assertEqual([0.65, 0.12, 0.18, 0.05], ostiumSettings.get("Ostium wall relative thicknesses")) + self.assertEqual(2.0, ostiumSettings.get("Vessel inner diameter")) self.assertEqual(0.5, ostiumSettings.get("Vessel wall thickness")) + self.assertEqual([0.65, 0.12, 0.18, 0.05], ostiumSettings.get("Vessel wall relative thicknesses")) self.assertEqual(0.0, ostiumSettings.get("Vessel angle 1 degrees")) self.assertEqual(0.55, options.get("Gastro-esophagal junction position along factor")) self.assertEqual(0.2, options.get("Cardia derivative factor")) @@ -48,26 +50,26 @@ def test_stomach1(self): region = context.getDefaultRegion() self.assertTrue(region.isValid()) annotationGroups = scaffold.generateBaseMesh(region, options) - self.assertEqual(13, len(annotationGroups)) + self.assertEqual(37, len(annotationGroups)) fieldmodule = region.getFieldmodule() self.assertEqual(RESULT_OK, fieldmodule.defineAllFaces()) mesh3d = fieldmodule.findMeshByDimension(3) - self.assertEqual(158, mesh3d.getSize()) + self.assertEqual(582, mesh3d.getSize()) mesh2d = fieldmodule.findMeshByDimension(2) - self.assertEqual(643, mesh2d.getSize()) + self.assertEqual(1965, mesh2d.getSize()) mesh1d = fieldmodule.findMeshByDimension(1) - self.assertEqual(823, mesh1d.getSize()) + self.assertEqual(2195, mesh1d.getSize()) nodes = fieldmodule.findNodesetByFieldDomainType(Field.DOMAIN_TYPE_NODES) - self.assertEqual(342, nodes.getSize()) + self.assertEqual(830, nodes.getSize()) datapoints = fieldmodule.findNodesetByFieldDomainType(Field.DOMAIN_TYPE_DATAPOINTS) self.assertEqual(0, datapoints.getSize()) coordinates = fieldmodule.findFieldByName("coordinates").castFiniteElement() self.assertTrue(coordinates.isValid()) minimums, maximums = evaluateFieldNodesetRange(coordinates, nodes) - assertAlmostEqualList(self, minimums, [-18.535034704449842, -14.989550140413265, -8.726949180639382], 1.0E-6) - assertAlmostEqualList(self, maximums, [18.39909561216101, 15.187335864125645, 8.727618869278126], 1.0E-6) + assertAlmostEqualList(self, minimums, [-18.238549598577396, -16.033751319943754, -8.905924748773598], 1.0E-6) + assertAlmostEqualList(self, maximums, [18.285156743233415, 15.214807824088728, 8.905433142848109], 1.0E-6) with ChangeManager(fieldmodule): one = fieldmodule.createFieldConstant(1.0) @@ -79,21 +81,21 @@ def test_stomach1(self): fieldcache = fieldmodule.createFieldcache() result, surfaceArea = surfaceAreaField.evaluateReal(fieldcache, 1) self.assertEqual(result, RESULT_OK) - self.assertAlmostEqual(surfaceArea, 2452.212186914634, delta=1.0E-6) + self.assertAlmostEqual(surfaceArea, 2557.832902256128, delta=1.0E-6) result, volume = volumeField.evaluateReal(fieldcache, 1) self.assertEqual(result, RESULT_OK) - self.assertAlmostEqual(volume, 1175.367658574881, delta=1.0E-6) + self.assertAlmostEqual(volume, 809.0349219398828, delta=1.0E-6) # check some annotationGroups: expectedSizes3d = { - "body of stomach" : 28, - "cardia of stomach" : 12, - "duodenum" : 14, - "esophagus" : 24, - "fundus of stomach" : 38, - "pyloric antrum" : 28, - "pylorus": 14, - "stomach": 158 + "body of stomach" : 112, + "cardia of stomach" : 36, + "duodenum" : 56, + "esophagus" : 96, + "fundus of stomach" : 114, + "pyloric antrum" : 112, + "pyloric canal": 56, + "stomach": 582 } for name in expectedSizes3d: group = getAnnotationGroupForTerm(annotationGroups, get_stomach_term(name)) @@ -109,7 +111,7 @@ def test_stomach1(self): for annotationGroup in removeAnnotationGroups: annotationGroups.remove(annotationGroup) - self.assertEqual(13, len(annotationGroups)) + self.assertEqual(37, len(annotationGroups)) refineRegion = region.createRegion() refineFieldmodule = refineRegion.getFieldmodule() @@ -127,16 +129,16 @@ def test_stomach1(self): for annotation in annotationGroups: if annotation not in oldAnnotationGroups: annotationGroup.addSubelements() - self.assertEqual(16, len(annotationGroups)) + self.assertEqual(68, len(annotationGroups)) # mesh3d = refineFieldmodule.findMeshByDimension(3) - self.assertEqual(10112, mesh3d.getSize()) + self.assertEqual(37248, mesh3d.getSize()) mesh2d = refineFieldmodule.findMeshByDimension(2) - self.assertEqual(33232, mesh2d.getSize()) + self.assertEqual(115248, mesh2d.getSize()) mesh1d = refineFieldmodule.findMeshByDimension(1) - self.assertEqual(36028, mesh1d.getSize()) + self.assertEqual(118796, mesh1d.getSize()) nodes = refineFieldmodule.findNodesetByFieldDomainType(Field.DOMAIN_TYPE_NODES) - self.assertEqual(12936, nodes.getSize()) + self.assertEqual(40814, nodes.getSize()) datapoints = refineFieldmodule.findNodesetByFieldDomainType(Field.DOMAIN_TYPE_DATAPOINTS) self.assertEqual(0, datapoints.getSize()) @@ -150,17 +152,17 @@ def test_stomach1(self): markerGroup = refineFieldmodule.findFieldByName("marker").castGroup() refinedNodes = refineFieldmodule.findNodesetByFieldDomainType(Field.DOMAIN_TYPE_NODES) markerNodes = markerGroup.getFieldNodeGroup(refinedNodes).getNodesetGroup() - self.assertEqual(4, markerNodes.getSize()) + self.assertEqual(18, markerNodes.getSize()) markerName = refineFieldmodule.findFieldByName("marker_name") self.assertTrue(markerName.isValid()) markerLocation = refineFieldmodule.findFieldByName("marker_location") self.assertTrue(markerLocation.isValid()) cache = refineFieldmodule.createFieldcache() - node = findNodeWithName(markerNodes, markerName, "gastro-esophagal junction on lesser curvature") + node = findNodeWithName(markerNodes, markerName, "esophagogastric junction along the lesser curvature on serosa") self.assertTrue(node.isValid()) cache.setNode(node) element, xi = markerLocation.evaluateMeshLocation(cache, 3) - self.assertEqual(1213, element.getIdentifier()) + self.assertEqual(5821, element.getIdentifier()) assertAlmostEqualList(self, xi, [ 0.0, 1.0, 1.0 ], 1.0E-10) if __name__ == "__main__": From 1875db3398d4411346c553bbb190625a5f37d212 Mon Sep 17 00:00:00 2001 From: Mabelle Lin Date: Fri, 5 Nov 2021 19:07:23 +1300 Subject: [PATCH 17/22] Correct typo in bladder mesh --- src/scaffoldmaker/meshtypes/meshtype_3d_bladderurethra1.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/scaffoldmaker/meshtypes/meshtype_3d_bladderurethra1.py b/src/scaffoldmaker/meshtypes/meshtype_3d_bladderurethra1.py index 9cdd9dae..a4c735a2 100644 --- a/src/scaffoldmaker/meshtypes/meshtype_3d_bladderurethra1.py +++ b/src/scaffoldmaker/meshtypes/meshtype_3d_bladderurethra1.py @@ -310,7 +310,7 @@ class MeshType_3d_bladderurethra1(Scaffold_base): 'Vessel end length factor': 2.0, 'Vessel inner diameter': 0.9, 'Vessel wall thickness': 0.2, - 'Ostium wall relative thicknesses': [1.0], + 'Vessel wall relative thicknesses': [1.0], 'Vessel angle 1 degrees': 0.0, 'Vessel angle 1 spread degrees': 0.0, 'Vessel angle 2 degrees': 0.0, From 97897a36e3c763d753402b5fdecc892a894e2bcf Mon Sep 17 00:00:00 2001 From: Mabelle Lin Date: Mon, 8 Nov 2021 14:14:04 +1300 Subject: [PATCH 18/22] Make code compliant with PEP8 --- src/scaffoldmaker/annotation/bladder_terms.py | 5 +- src/scaffoldmaker/annotation/colon_terms.py | 45 +- .../annotation/smallintestine_terms.py | 13 +- src/scaffoldmaker/annotation/stomach_terms.py | 147 +++--- .../meshtypes/meshtype_3d_bladderurethra1.py | 254 +++++---- .../meshtypes/meshtype_3d_colonsegment1.py | 199 +++---- .../meshtypes/meshtype_3d_ostium1.py | 491 ++++++++++-------- .../meshtypes/meshtype_3d_stomach1.py | 402 ++++++++------ src/scaffoldmaker/utils/annulusmesh.py | 356 ++++++++----- tests/test_bladder.py | 10 +- tests/test_cecum.py | 3 +- tests/test_colon.py | 20 +- tests/test_colonsegment.py | 9 +- tests/test_smallintestine.py | 13 +- tests/test_stomach.py | 23 +- 15 files changed, 1157 insertions(+), 833 deletions(-) diff --git a/src/scaffoldmaker/annotation/bladder_terms.py b/src/scaffoldmaker/annotation/bladder_terms.py index 7deccd52..a7c9febc 100644 --- a/src/scaffoldmaker/annotation/bladder_terms.py +++ b/src/scaffoldmaker/annotation/bladder_terms.py @@ -44,7 +44,8 @@ ("urethra junction of ventral bladder neck", "ILX:0738410") ] -def get_bladder_term(name : str): + +def get_bladder_term(name: str): """ Find term by matching name to any identifier held for a term. Raise exception if name not found. @@ -52,5 +53,5 @@ def get_bladder_term(name : str): """ for term in bladder_terms: if name in term: - return ( term[0], term[1] ) + return (term[0], term[1]) raise NameError("Bladder annotation term '" + name + "' not found.") diff --git a/src/scaffoldmaker/annotation/colon_terms.py b/src/scaffoldmaker/annotation/colon_terms.py index 6ac2951a..13ca29e9 100644 --- a/src/scaffoldmaker/annotation/colon_terms.py +++ b/src/scaffoldmaker/annotation/colon_terms.py @@ -4,29 +4,30 @@ # convention: preferred name, preferred id, followed by any other ids and alternative names colon_terms = [ - ( "ascending colon", "UBERON:0001156", "FMA:14545", "ILX:0734393"), - ( "descending colon", "UBERON:0001158", "FMA:14547", "ILX:0724444"), - ( "caecum", "UBERON:0001153", "FMA:14541", "ILX:0732270"), - ( "circular muscle layer of colon", "ILX:0772428"), - ( "colon", "UBERON:0001155", "FMA:14543", "ILX:0736005"), - ( "colonic mucosa", "UBERON:0000317", "FMA:14984", "ILX:0731046"), - ( "distal colon", "UBERON:0008971", "ILX:0727523"), - ( "longitudinal muscle layer of colon", "ILX:0775554"), - ( "luminal surface of the colonic mucosa", "ILX:0793083"), - ( "mesenteric zone", "None"), - ( "non-mesenteric zone", "None"), - ( "proximal colon", "UBERON:0008972", "ILX:0733240"), - ( "serosa of colon", "UBERON:0003335", "FMA:14990", "ILX:0736932"), - ( "spiral colon", "UBERON:0010239", "ILX:0735018"), - ( "submucosa of colon", "UBERON:0003331", "FMA:14985", "ILX:0728041"), - ( "taenia coli", "UBERON:0012419", "FMA:15041", "ILX:0731555"), - ( "taenia libera", "ILX:0739285"), - ( "taenia mesocolica", "ILX:0739284"), - ( "taenia omentalis", "ILX:0739286"), - ( "transverse colon", "UBERON:0001157", "FMA:14546", "ILX:0728767") + ("ascending colon", "UBERON:0001156", "FMA:14545", "ILX:0734393"), + ("descending colon", "UBERON:0001158", "FMA:14547", "ILX:0724444"), + ("caecum", "UBERON:0001153", "FMA:14541", "ILX:0732270"), + ("circular muscle layer of colon", "ILX:0772428"), + ("colon", "UBERON:0001155", "FMA:14543", "ILX:0736005"), + ("colonic mucosa", "UBERON:0000317", "FMA:14984", "ILX:0731046"), + ("distal colon", "UBERON:0008971", "ILX:0727523"), + ("longitudinal muscle layer of colon", "ILX:0775554"), + ("luminal surface of the colonic mucosa", "ILX:0793083"), + ("mesenteric zone", "None"), + ("non-mesenteric zone", "None"), + ("proximal colon", "UBERON:0008972", "ILX:0733240"), + ("serosa of colon", "UBERON:0003335", "FMA:14990", "ILX:0736932"), + ("spiral colon", "UBERON:0010239", "ILX:0735018"), + ("submucosa of colon", "UBERON:0003331", "FMA:14985", "ILX:0728041"), + ("taenia coli", "UBERON:0012419", "FMA:15041", "ILX:0731555"), + ("taenia libera", "ILX:0739285"), + ("taenia mesocolica", "ILX:0739284"), + ("taenia omentalis", "ILX:0739286"), + ("transverse colon", "UBERON:0001157", "FMA:14546", "ILX:0728767") ] -def get_colon_term(name : str): + +def get_colon_term(name: str): """ Find term by matching name to any identifier held for a term. Raise exception if name not found. @@ -34,5 +35,5 @@ def get_colon_term(name : str): """ for term in colon_terms: if name in term: - return ( term[0], term[1] ) + return (term[0], term[1]) raise NameError("Colon annotation term '" + name + "' not found.") diff --git a/src/scaffoldmaker/annotation/smallintestine_terms.py b/src/scaffoldmaker/annotation/smallintestine_terms.py index 63e1d00b..460de25c 100644 --- a/src/scaffoldmaker/annotation/smallintestine_terms.py +++ b/src/scaffoldmaker/annotation/smallintestine_terms.py @@ -4,13 +4,14 @@ # convention: preferred name, preferred id, followed by any other ids and alternative names smallintestine_terms = [ - ( "duodenum", "UBERON:0002114", "FMA:7206", "ILX:0726125"), - ( "ileum", "UBERON:0002116", "FMA:7208", "ILX:0728151"), - ( "jejunum", "UBERON:0002115", "FMA:7207", "ILX:0724224"), - ( "small intestine", "UBERON:0002108", "FMA:7200", "ILX:0726770") + ("duodenum", "UBERON:0002114", "FMA:7206", "ILX:0726125"), + ("ileum", "UBERON:0002116", "FMA:7208", "ILX:0728151"), + ("jejunum", "UBERON:0002115", "FMA:7207", "ILX:0724224"), + ("small intestine", "UBERON:0002108", "FMA:7200", "ILX:0726770") ] -def get_smallintestine_term(name : str): + +def get_smallintestine_term(name: str): """ Find term by matching name to any identifier held for a term. Raise exception if name not found. @@ -18,5 +19,5 @@ def get_smallintestine_term(name : str): """ for term in smallintestine_terms: if name in term: - return ( term[0], term[1] ) + return (term[0], term[1]) raise NameError("Small intestine annotation term '" + name + "' not found.") diff --git a/src/scaffoldmaker/annotation/stomach_terms.py b/src/scaffoldmaker/annotation/stomach_terms.py index b97ba8b8..a5f8577a 100644 --- a/src/scaffoldmaker/annotation/stomach_terms.py +++ b/src/scaffoldmaker/annotation/stomach_terms.py @@ -4,80 +4,81 @@ # convention: preferred name, preferred id, followed by any other ids and alternative names stomach_terms = [ - ( "body of stomach", "UBERON:0001161", " FMA:14560", "ILX:0724929"), - ( "body-antrum junction along the greater curvature on circular-longitudinal muscle interface", "None"), - ( "body-antrum junction along the greater curvature on luminal surface", "None"), - ( "body-antrum junction along the greater curvature on serosa", "None"), - ( "cardia of stomach", "UBERON:0001162", " FMA:14561", "ILX:0729096"), - ( "circular muscle layer of stomach", "ILX:0774731"), - ( "circular-longitudinal muscle interface of body of stomach along the gastric-omentum attachment", "None"), - ( "circular-longitudinal muscle interface of dorsal stomach", "None"), - ( "circular-longitudinal muscle interface of duodenum along the gastric-omentum attachment", "None"), - ( "circular-longitudinal muscle interface of esophagus along the cut margin", "None"), - ( "circular-longitudinal muscle interface of fundus of stomach along the gastric-omentum attachment", "None"), - ( "circular-longitudinal muscle interface of gastroduodenal junction", "None"), - ( "circular-longitudinal muscle interface of pyloric antrum along the gastric-omentum attachment", "None"), - ( "circular-longitudinal muscle interface of pyloric canal along the gastric-omentum attachment", "None"), - ( "circular-longitudinal muscle interface of stomach", "None"), - ( "circular-longitudinal muscle interface of ventral stomach", "None"), - ( "dorsal stomach", "ILX:0793086"), - ( "duodenum", "UBERON:0002114", " FMA:7206", "ILX:0726125"), - ( "esophagogastric junction", "UBERON:0007650", "FMA: 9434", "ILX:0733910"), - ( "esophagogastric junction along the greater curvature on circular-longitudinal muscle interface", "None"), - ( "esophagogastric junction along the greater curvature on luminal surface", "None"), - ( "esophagogastric junction along the greater curvature on serosa", "None"), - ( "esophagogastric junction along the lesser curvature on circular-longitudinal muscle interface", "None"), - ( "esophagogastric junction along the lesser curvature on luminal surface", "None"), - ( "esophagogastric junction along the lesser curvature on serosa", "None"), - ( "esophagus", "UBERON:0001043", "FMA:7131", "ILX:0735017"), - ( "esophagus mucosa", "UBERON:0002469", "FMA:62996", "ILX:0725079"), - ( "esophagus smooth muscle circular layer", "UBERON:0009960", "FMA:67605", "ILX:0735992"), - ( "esophagus smooth muscle longitudinal layer", "UBERON:0009961", "FMA:63573", "ILX:0727608"), - ( "forestomach-glandular stomach junction", "UBERON:0012270", "ILX:0729974"), - ( "fundus of stomach", "UBERON:0001160", " FMA:14559", "ILX:0724443"), - ( "fundus-body junction along the greater curvature on circular-longitudinal muscle interface", "None"), - ( "fundus-body junction along the greater curvature on luminal surface", "None"), - ( "fundus-body junction along the greater curvature on serosa", "None"), - ( "gastroduodenal junction", "UBERON:0012650", "FMA:17046", "ILX:0725406"), - ( "gastroduodenal junction along the greater curvature on circular-longitudinal muscle interface", "None"), - ( "gastroduodenal junction along the greater curvature on luminal surface", "None"), - ( "gastroduodenal junction along the greater curvature on serosa", "None"), - ( "gastroduodenal junction along the lesser curvature on circular-longitudinal muscle interface", "None"), - ( "gastroduodenal junction along the lesser curvature on luminal surface", "None"), - ( "gastroduodenal junction along the lesser curvature on serosa", "None"), - ( "limiting ridge along the greater curvature on circular-longitudinal muscle interface", "None"), - ( "limiting ridge along the greater curvature on luminal surface", "None"), - ( "limiting ridge along the greater curvature on serosa", "None"), - ( "limiting ridge on circular-longitudinal muscle interface", "None"), - ( "limiting ridge on luminal surface", "None"), - ( "limiting ridge on serosa", "None"), - ( "longitudinal muscle layer of stomach", "ILX:0772619"), - ( "luminal surface of body of stomach", "None"), - ( "luminal surface of cardia of stomach", "None"), - ( "luminal surface of duodenum", "None"), - ( "luminal surface of esophagus", "None"), - ( "luminal surface of fundus of stomach", "None"), - ( "luminal surface of pyloric antrum", "None"), - ( "luminal surface of pyloric canal", "None"), - ( "luminal surface of stomach", "None"), - ( "mucosa of stomach", "UBERON:0001199", "FMA:14907", "ILX:0736669"), - ( "pyloric antrum", "UBERON:0001165", " FMA:14579", "ILX:0728672"), - ( "pyloric canal", "UBERON:0008858", "FMA:14580", "ILX:0735898"), - ( "serosa of body of stomach", "ILX:0771402"), - ( "serosa of cardia of stomach", "ILX:0776646"), - ( "serosa of duodenum", "UBERON:0003336", "FMA:14948", "ILX:0732373"), - ( "serosa of esophagus", "UBERON:0001975", "FMA:63057", "ILX:0725745"), - ( "serosa of fundus of stomach", "UBERON:0012503", "FMA:17073", "ILX:0726906"), - ( "serosa of pyloric antrum", "ILX:0777005"), - ( "serosa of pyloric canal", "ILX:0775898"), - ( "serosa of stomach", "UBERON:0001201", "FMA:14914", "ILX:0735818"), - ( "stomach", "UBERON:0000945", "FMA:7148", "ILX:0736697"), - ( "submucosa of esophagus", "UBERON:0001972", "FMA:62997", "ILX:0728662"), - ( "submucosa of stomach", "UBERON:0001200", "FMA:14908", "ILX:0732950"), - ( "ventral stomach", "ILX:0793085"), + ("body of stomach", "UBERON:0001161", " FMA:14560", "ILX:0724929"), + ("body-antrum junction along the greater curvature on circular-longitudinal muscle interface", "None"), + ("body-antrum junction along the greater curvature on luminal surface", "None"), + ("body-antrum junction along the greater curvature on serosa", "None"), + ("cardia of stomach", "UBERON:0001162", " FMA:14561", "ILX:0729096"), + ("circular muscle layer of stomach", "ILX:0774731"), + ("circular-longitudinal muscle interface of body of stomach along the gastric-omentum attachment", "None"), + ("circular-longitudinal muscle interface of dorsal stomach", "None"), + ("circular-longitudinal muscle interface of duodenum along the gastric-omentum attachment", "None"), + ("circular-longitudinal muscle interface of esophagus along the cut margin", "None"), + ("circular-longitudinal muscle interface of fundus of stomach along the gastric-omentum attachment", "None"), + ("circular-longitudinal muscle interface of gastroduodenal junction", "None"), + ("circular-longitudinal muscle interface of pyloric antrum along the gastric-omentum attachment", "None"), + ("circular-longitudinal muscle interface of pyloric canal along the gastric-omentum attachment", "None"), + ("circular-longitudinal muscle interface of stomach", "None"), + ("circular-longitudinal muscle interface of ventral stomach", "None"), + ("dorsal stomach", "ILX:0793086"), + ("duodenum", "UBERON:0002114", " FMA:7206", "ILX:0726125"), + ("esophagogastric junction", "UBERON:0007650", "FMA: 9434", "ILX:0733910"), + ("esophagogastric junction along the greater curvature on circular-longitudinal muscle interface", "None"), + ("esophagogastric junction along the greater curvature on luminal surface", "None"), + ("esophagogastric junction along the greater curvature on serosa", "None"), + ("esophagogastric junction along the lesser curvature on circular-longitudinal muscle interface", "None"), + ("esophagogastric junction along the lesser curvature on luminal surface", "None"), + ("esophagogastric junction along the lesser curvature on serosa", "None"), + ("esophagus", "UBERON:0001043", "FMA:7131", "ILX:0735017"), + ("esophagus mucosa", "UBERON:0002469", "FMA:62996", "ILX:0725079"), + ("esophagus smooth muscle circular layer", "UBERON:0009960", "FMA:67605", "ILX:0735992"), + ("esophagus smooth muscle longitudinal layer", "UBERON:0009961", "FMA:63573", "ILX:0727608"), + ("forestomach-glandular stomach junction", "UBERON:0012270", "ILX:0729974"), + ("fundus of stomach", "UBERON:0001160", " FMA:14559", "ILX:0724443"), + ("fundus-body junction along the greater curvature on circular-longitudinal muscle interface", "None"), + ("fundus-body junction along the greater curvature on luminal surface", "None"), + ("fundus-body junction along the greater curvature on serosa", "None"), + ("gastroduodenal junction", "UBERON:0012650", "FMA:17046", "ILX:0725406"), + ("gastroduodenal junction along the greater curvature on circular-longitudinal muscle interface", "None"), + ("gastroduodenal junction along the greater curvature on luminal surface", "None"), + ("gastroduodenal junction along the greater curvature on serosa", "None"), + ("gastroduodenal junction along the lesser curvature on circular-longitudinal muscle interface", "None"), + ("gastroduodenal junction along the lesser curvature on luminal surface", "None"), + ("gastroduodenal junction along the lesser curvature on serosa", "None"), + ("limiting ridge along the greater curvature on circular-longitudinal muscle interface", "None"), + ("limiting ridge along the greater curvature on luminal surface", "None"), + ("limiting ridge along the greater curvature on serosa", "None"), + ("limiting ridge on circular-longitudinal muscle interface", "None"), + ("limiting ridge on luminal surface", "None"), + ("limiting ridge on serosa", "None"), + ("longitudinal muscle layer of stomach", "ILX:0772619"), + ("luminal surface of body of stomach", "None"), + ("luminal surface of cardia of stomach", "None"), + ("luminal surface of duodenum", "None"), + ("luminal surface of esophagus", "None"), + ("luminal surface of fundus of stomach", "None"), + ("luminal surface of pyloric antrum", "None"), + ("luminal surface of pyloric canal", "None"), + ("luminal surface of stomach", "None"), + ("mucosa of stomach", "UBERON:0001199", "FMA:14907", "ILX:0736669"), + ("pyloric antrum", "UBERON:0001165", " FMA:14579", "ILX:0728672"), + ("pyloric canal", "UBERON:0008858", "FMA:14580", "ILX:0735898"), + ("serosa of body of stomach", "ILX:0771402"), + ("serosa of cardia of stomach", "ILX:0776646"), + ("serosa of duodenum", "UBERON:0003336", "FMA:14948", "ILX:0732373"), + ("serosa of esophagus", "UBERON:0001975", "FMA:63057", "ILX:0725745"), + ("serosa of fundus of stomach", "UBERON:0012503", "FMA:17073", "ILX:0726906"), + ("serosa of pyloric antrum", "ILX:0777005"), + ("serosa of pyloric canal", "ILX:0775898"), + ("serosa of stomach", "UBERON:0001201", "FMA:14914", "ILX:0735818"), + ("stomach", "UBERON:0000945", "FMA:7148", "ILX:0736697"), + ("submucosa of esophagus", "UBERON:0001972", "FMA:62997", "ILX:0728662"), + ("submucosa of stomach", "UBERON:0001200", "FMA:14908", "ILX:0732950"), + ("ventral stomach", "ILX:0793085"), ] -def get_stomach_term(name : str): + +def get_stomach_term(name: str): """ Find term by matching name to any identifier held for a term. Raise exception if name not found. @@ -85,5 +86,5 @@ def get_stomach_term(name : str): """ for term in stomach_terms: if name in term: - return ( term[0], term[1] ) + return (term[0], term[1]) raise NameError("Stomach annotation term '" + name + "' not found.") diff --git a/src/scaffoldmaker/meshtypes/meshtype_3d_bladderurethra1.py b/src/scaffoldmaker/meshtypes/meshtype_3d_bladderurethra1.py index a4c735a2..f1a3faaf 100644 --- a/src/scaffoldmaker/meshtypes/meshtype_3d_bladderurethra1.py +++ b/src/scaffoldmaker/meshtypes/meshtype_3d_bladderurethra1.py @@ -1,17 +1,19 @@ -''' +""" Generates 3D bladder and urethra meshes along the central path, with variable numbers of elements around, along and through wall. -''' +""" import copy import math -from opencmiss.utils.zinc.field import findOrCreateFieldGroup, findOrCreateFieldNodeGroup, findOrCreateFieldStoredMeshLocation, findOrCreateFieldStoredString +from opencmiss.utils.zinc.field import findOrCreateFieldGroup, findOrCreateFieldNodeGroup, \ + findOrCreateFieldStoredMeshLocation, findOrCreateFieldStoredString from opencmiss.zinc.element import Element from opencmiss.zinc.field import Field from opencmiss.zinc.node import Node -from scaffoldmaker.annotation.annotationgroup import AnnotationGroup, findOrCreateAnnotationGroupForTerm, getAnnotationGroupForTerm +from scaffoldmaker.annotation.annotationgroup import AnnotationGroup, findOrCreateAnnotationGroupForTerm, \ + getAnnotationGroupForTerm from scaffoldmaker.annotation.bladder_terms import get_bladder_term from scaffoldmaker.meshtypes.meshtype_1d_path1 import MeshType_1d_path1, extractPathParametersFromRegion from scaffoldmaker.meshtypes.meshtype_3d_ostium1 import MeshType_3d_ostium1, generateOstiumMesh @@ -29,10 +31,10 @@ class MeshType_3d_bladderurethra1(Scaffold_base): - ''' + """ Generates 3D bladder and urethra meshes with variable numbers of elements around, along the central path, and through the wall. - ''' + """ centralPathDefaultScaffoldPackages_LUT = { 'Cat 1': ScaffoldPackage(MeshType_1d_path1, { 'scaffoldSettings': { @@ -517,19 +519,21 @@ def getOptionScaffoldTypeParameterSetNames(cls, optionName, scaffoldType): return list(cls.centralPathDefaultScaffoldPackages_LUT.keys()) if optionName == 'Ureter': return list(cls.ostiumDefaultScaffoldPackages.keys()) - assert scaffoldType in cls.getOptionValidScaffoldTypes(optionName), cls.__name__ + '.getOptionScaffoldTypeParameterSetNames. ' + \ - 'Invalid option \'' + optionName + '\' scaffold type ' + scaffoldType.getName() + assert scaffoldType in cls.getOptionValidScaffoldTypes(optionName), \ + cls.__name__ + '.getOptionScaffoldTypeParameterSetNames. ' + 'Invalid option \'' + \ + optionName + '\' scaffold type ' + scaffoldType.getName() return scaffoldType.getParameterSetNames() @classmethod def getOptionScaffoldPackage(cls, optionName, scaffoldType, parameterSetName=None): - ''' + """ :param parameterSetName: Name of valid parameter set for option Scaffold, or None for default. :return: ScaffoldPackage. - ''' + """ if parameterSetName: assert parameterSetName in cls.getOptionScaffoldTypeParameterSetNames(optionName, scaffoldType), \ - 'Invalid parameter set ' + str(parameterSetName) + ' for scaffold ' + str(scaffoldType.getName()) + ' in option ' + str(optionName) + ' of scaffold ' + cls.getName() + 'Invalid parameter set ' + str(parameterSetName) + ' for scaffold ' + str(scaffoldType.getName()) + \ + ' in option ' + str(optionName) + ' of scaffold ' + cls.getName() if optionName == 'Central path LUT': if not parameterSetName: parameterSetName = list(cls.centralPathDefaultScaffoldPackages_LUT.keys())[0] @@ -568,9 +572,9 @@ def checkOptions(cls, options): @classmethod def updateSubScaffoldOptions(cls, options): - ''' + """ Update ostium sub-scaffold options which depend on parent options. - ''' + """ bladderWallThickness = options['Wall thickness'] elementsCountThroughWall = options['Number of elements through wall'] ureterOptions = options['Ureter'] @@ -580,12 +584,12 @@ def updateSubScaffoldOptions(cls, options): @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 - ''' + """ cls.updateSubScaffoldOptions(options) centralPath = options['Central path LUT'] elementsCountAlongBladder = options['Number of elements along bladder'] @@ -619,7 +623,6 @@ def generateBaseMesh(cls, region, options): fm = region.getFieldmodule() fm.beginChange() - nodes = fm.findNodesetByFieldDomainType(Field.DOMAIN_TYPE_NODES) mesh = fm.findMeshByDimension(3) # Annotation fiducial point @@ -639,16 +642,20 @@ def generateBaseMesh(cls, region, options): # Bladder part tmpRegion = region.createRegion() centralPath.generate(tmpRegion) - cx_bladder, cd1_bladder, cd2_bladder, cd12_bladder = extractPathParametersFromRegion(tmpRegion, - [Node.VALUE_LABEL_VALUE, Node.VALUE_LABEL_D_DS1, Node.VALUE_LABEL_D_DS2, Node.VALUE_LABEL_D2_DS1DS2], groupName='urinary bladder') + cx_bladder, cd1_bladder, cd2_bladder, cd12_bladder = \ + extractPathParametersFromRegion(tmpRegion, [Node.VALUE_LABEL_VALUE, Node.VALUE_LABEL_D_DS1, + Node.VALUE_LABEL_D_DS2, Node.VALUE_LABEL_D2_DS1DS2], + groupName='urinary bladder') # for i in range(len(cx_bladder)): # print(i, '[', cx_bladder[i], ',', cd1_bladder[i], ',', cd2_bladder[i], ',', cd12_bladder[i], '],') del tmpRegion # Urethra part tmpRegion = region.createRegion() centralPath.generate(tmpRegion) - cx_urethra, cd1_urethra, cd2_urethra, cd12_urethra = extractPathParametersFromRegion(tmpRegion, - [Node.VALUE_LABEL_VALUE, Node.VALUE_LABEL_D_DS1, Node.VALUE_LABEL_D_DS2, Node.VALUE_LABEL_D2_DS1DS2], groupName='urethra') + cx_urethra, cd1_urethra, cd2_urethra, cd12_urethra = \ + extractPathParametersFromRegion(tmpRegion, [Node.VALUE_LABEL_VALUE, Node.VALUE_LABEL_D_DS1, + Node.VALUE_LABEL_D_DS2, Node.VALUE_LABEL_D2_DS1DS2], + groupName='urethra') # for i in range(len(cx_urethra)): # print(i, '[', cx_urethra[i], ',', cd1_urethra[i], ',', cd2_urethra[i], ',', cd12_urethra[i], '],') del tmpRegion @@ -658,14 +665,16 @@ def generateBaseMesh(cls, region, options): bladderLength = 0.0 elementsCountInBladder = len(cx_bladder) - 1 for e in range(elementsCountInBladder): - arcLength = interp.getCubicHermiteArcLength(cx_bladder[e], cd1_bladder[e], cx_bladder[e + 1], cd1_bladder[e + 1]) + arcLength = interp.getCubicHermiteArcLength(cx_bladder[e], cd1_bladder[e], + cx_bladder[e + 1], cd1_bladder[e + 1]) bladderLength += arcLength bladderSegmentLength = bladderLength / elementsCountAlongBladder # Urethra part urethraLength = 0.0 elementsCountInUrethra = len(cx_urethra) - 1 for e in range(elementsCountInUrethra): - arcLength = interp.getCubicHermiteArcLength(cx_urethra[e], cd1_urethra[e], cx_urethra[e + 1], cd1_urethra[e + 1]) + arcLength = interp.getCubicHermiteArcLength(cx_urethra[e], cd1_urethra[e], + cx_urethra[e + 1], cd1_urethra[e + 1]) urethraLength += arcLength urethraSegmentLength = urethraLength / elementsCountAlongUrethra @@ -758,7 +767,8 @@ def generateBaseMesh(cls, region, options): innerNodes_d1 += [nd1] * elementsCountAround for n1 in range(0, len(nx_max)): xAround, d1Around = createEllipsePoints([0.0, 0.0, nx_max[n1][2]], 2 * math.pi, [nx_max[n1][1], 0.0, 0.0], - [0.0, nx_min[n1][1], 0.0], elementsCountAround, startRadians=-math.pi/2) + [0.0, nx_min[n1][1], 0.0], elementsCountAround, + startRadians=-math.pi/2) innerNodes_x += xAround if n1 >= 1: innerNodes_d1 += d1Around @@ -903,7 +913,8 @@ def generateBaseMesh(cls, region, options): d2Final = [] d3Final = [] for n3 in range(elementsCountThroughWall + 1): - xApex = [xApexInner[c] + d3ApexUnit[c] * bladderWallThickness / elementsCountThroughWall * n3 for c in range(3)] + xApex = [xApexInner[c] + + d3ApexUnit[c] * bladderWallThickness / elementsCountThroughWall * n3 for c in range(3)] xFinal.append(xApex) d1Final.append(d1ApexInner) d2Final.append(d2ApexInner) @@ -950,8 +961,9 @@ def generateBaseMesh(cls, region, options): ureter1Position = trackSurfaceUreter1.createPositionProportion(ureterPositionAround, ureterPositionDown) ureterElementPositionAround = ureter1Position.e1 ureterElementPositionDown = ureter1Position.e2 - elementToDeleteStartIdx1 = elementsCountThroughWall * elementsCountAround * (ureterElementPositionDown - (0 if ureter1Position.xi2 > 0.5 else 1)) \ - + ureterElementPositionAround + (1 if ureter1Position.xi1 > 0.5 else 0) + elementToDeleteStartIdx1 = elementsCountThroughWall * elementsCountAround * \ + (ureterElementPositionDown - (0 if ureter1Position.xi2 > 0.5 else 1)) + \ + ureterElementPositionAround + (1 if ureter1Position.xi1 > 0.5 else 0) elementIndex = elementToDeleteStartIdx1 - elementsCountAround * ureterElementPositionDown @@ -970,9 +982,11 @@ def generateBaseMesh(cls, region, options): trackSurfaceUreter2 = TrackSurface(elementsCount1, elementsCount2, nodesOnTrackSurface2_x, nodesOnTrackSurface2_d1, nodesOnTrackSurface2_d2) - ureter2Position = TrackSurfacePosition(elementsCountAround // 2 - ureterElementPositionAround + (-1 if ureter1Position.xi1 > 0 else 0), + ureter2Position = TrackSurfacePosition(elementsCountAround // 2 - + ureterElementPositionAround + (-1 if ureter1Position.xi1 > 0 else 0), ureterElementPositionDown, - (1 - ureter1Position.xi1) if ureter1Position.xi1 > 0 else ureter1Position.xi1, + (1 - ureter1Position.xi1) if ureter1Position.xi1 > 0 + else ureter1Position.xi1, ureter1Position.xi2) if includeUreter: @@ -1011,9 +1025,7 @@ def generateBaseMesh(cls, region, options): annotationGroupsThroughWall.append([]) neckMeshGroup = neckGroup. getMeshGroup(mesh) - bodyMeshGroup = bodyGroup.getMeshGroup(mesh) urinaryBladderMeshGroup = bladderGroup.getMeshGroup(mesh) - urethraMeshGroup = urethraGroup. getMeshGroup(mesh) ureterMeshGroup = ureterGroup. getMeshGroup(mesh) # Create nodes and elements @@ -1074,11 +1086,20 @@ def generateBaseMesh(cls, region, options): ureterMeshGroup, bladderMeshGroup) # Define markers for apex, ureter and urethra junctions with bladder - apexGroup = findOrCreateAnnotationGroupForTerm(annotationGroups, region, get_bladder_term("apex of urinary bladder")) - leftUreterGroup = findOrCreateAnnotationGroupForTerm(annotationGroups, region, get_bladder_term("left ureter junction with bladder")) - rightUreterGroup = findOrCreateAnnotationGroupForTerm(annotationGroups, region, get_bladder_term("right ureter junction with bladder")) - dorsalUrethraGroup = findOrCreateAnnotationGroupForTerm(annotationGroups, region, get_bladder_term("urethra junction of dorsal bladder neck")) - ventralUrethraGroup = findOrCreateAnnotationGroupForTerm(annotationGroups, region, get_bladder_term("urethra junction of ventral bladder neck")) + apexGroup = \ + findOrCreateAnnotationGroupForTerm(annotationGroups, region, get_bladder_term("apex of urinary bladder")) + leftUreterGroup = \ + findOrCreateAnnotationGroupForTerm(annotationGroups, region, + get_bladder_term("left ureter junction with bladder")) + rightUreterGroup = \ + findOrCreateAnnotationGroupForTerm(annotationGroups, region, + get_bladder_term("right ureter junction with bladder")) + dorsalUrethraGroup = \ + findOrCreateAnnotationGroupForTerm(annotationGroups, region, + get_bladder_term("urethra junction of dorsal bladder neck")) + ventralUrethraGroup = \ + findOrCreateAnnotationGroupForTerm(annotationGroups, region, + get_bladder_term("urethra junction of ventral bladder neck")) idx1 = 1 xi1 = [0.0, 0.0, 0.0] @@ -1093,13 +1114,16 @@ def generateBaseMesh(cls, region, options): xi3 = [0.0, 1.0, 0.0] markerList.append({"group": rightUreterGroup, "elementId": idx3, "xi": xi3}) else: - idx2 = ureterElementPositionDown * elementsCountAround * elementsCountThroughWall + ureterElementPositionAround + 1 + idx2 = ureterElementPositionDown * elementsCountAround * elementsCountThroughWall + \ + ureterElementPositionAround + 1 xi2 = [ureter1Position.xi1, ureter1Position.xi2, 0.0] markerList.append({"group": leftUreterGroup, "elementId": idx2, "xi": xi2}) - idx3 = ureterElementPositionDown * elementsCountAround * elementsCountThroughWall + elementsCountAround - ureterElementPositionAround + idx3 = ureterElementPositionDown * elementsCountAround * elementsCountThroughWall + \ + elementsCountAround - ureterElementPositionAround xi3 = [1 - ureter1Position.xi1, ureter1Position.xi2, 0.0] markerList.append({"group": rightUreterGroup, "elementId": idx3, "xi": xi3}) - idx4 = (elementsCountAlongBladder - 1) * elementsCountAround * elementsCountThroughWall + elementsCountAround // 2 + idx4 = (elementsCountAlongBladder - 1) * elementsCountAround * elementsCountThroughWall + \ + elementsCountAround // 2 xi4 = [1.0, 1.0, 0.0] markerList.append({"group": dorsalUrethraGroup, "elementId": idx4, "xi": xi4}) idx5 = (elementsCountAlongBladder - 1) * elementsCountAround * elementsCountThroughWall + 1 @@ -1122,11 +1146,11 @@ def generateBaseMesh(cls, region, options): @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(). - ''' + """ refineElementsCountAlong = options['Refine number of elements along'] refineElementsCountAround = options['Refine number of elements around'] refineElementsCountThroughWall = options['Refine number of elements through wall'] @@ -1137,14 +1161,14 @@ def refineMesh(cls, meshrefinement, options): @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. - ''' + """ # Create 2d surface mesh groups fm = region.getFieldmodule() bodyGroup = getAnnotationGroupForTerm(annotationGroups, get_bladder_term("dome of the bladder")) @@ -1174,72 +1198,105 @@ def defineFaceAnnotations(cls, region, options, annotationGroups): is_dorsal_bladder = bladderDorsalGroup.getFieldElementGroup(mesh2d) is_ventral_bladder = bladderVentralGroup.getFieldElementGroup(mesh2d) - serosaOfUrinaryBladder = findOrCreateAnnotationGroupForTerm(annotationGroups, region, get_bladder_term("serosa of urinary bladder")) + serosaOfUrinaryBladder = \ + findOrCreateAnnotationGroupForTerm(annotationGroups, region, get_bladder_term("serosa of urinary bladder")) serosaOfUrinaryBladder.getMeshGroup(mesh2d).addElementsConditional(is_urinaryBladder_serosa) - lumenOfUrinaryBladder = findOrCreateAnnotationGroupForTerm(annotationGroups, region, get_bladder_term("bladder lumen")) + lumenOfUrinaryBladder = \ + findOrCreateAnnotationGroupForTerm(annotationGroups, region, get_bladder_term("bladder lumen")) lumenOfUrinaryBladder.getMeshGroup(mesh2d).addElementsConditional(is_urinaryBladder_lumen) - serosaOfBody = findOrCreateAnnotationGroupForTerm(annotationGroups, region, get_bladder_term("serosa of body of urinary bladder")) + serosaOfBody = \ + findOrCreateAnnotationGroupForTerm(annotationGroups, region, + get_bladder_term("serosa of body of urinary bladder")) serosaOfBody.getMeshGroup(mesh2d).addElementsConditional(is_body_serosa) - lumenOfBody = findOrCreateAnnotationGroupForTerm(annotationGroups, region, get_bladder_term("lumen of body of urinary bladder")) + lumenOfBody = \ + findOrCreateAnnotationGroupForTerm(annotationGroups, region, + get_bladder_term("lumen of body of urinary bladder")) lumenOfBody.getMeshGroup(mesh2d).addElementsConditional(is_body_lumen) - serosaOfNeck = findOrCreateAnnotationGroupForTerm(annotationGroups, region, get_bladder_term("serosa of neck of urinary bladder")) + serosaOfNeck = findOrCreateAnnotationGroupForTerm(annotationGroups, region, + get_bladder_term("serosa of neck of urinary bladder")) serosaOfNeck.getMeshGroup(mesh2d).addElementsConditional(is_neck_serosa) - lumenOfNeck = findOrCreateAnnotationGroupForTerm(annotationGroups, region, get_bladder_term("lumen of neck of urinary bladder")) + lumenOfNeck = findOrCreateAnnotationGroupForTerm(annotationGroups, region, + get_bladder_term("lumen of neck of urinary bladder")) lumenOfNeck.getMeshGroup(mesh2d).addElementsConditional(is_neck_lumen) is_bladder_serosa_dorsal = fm.createFieldAnd(is_urinaryBladder_serosa, is_dorsal_bladder) - serosaOfBladder_dorsal = findOrCreateAnnotationGroupForTerm(annotationGroups, region, get_bladder_term("dorsal part of serosa of urinary bladder")) + serosaOfBladder_dorsal = \ + findOrCreateAnnotationGroupForTerm(annotationGroups, region, + get_bladder_term("dorsal part of serosa of urinary bladder")) serosaOfBladder_dorsal.getMeshGroup(mesh2d).addElementsConditional(is_bladder_serosa_dorsal) is_bladder_serosa_ventral = fm.createFieldAnd(is_urinaryBladder_serosa, is_ventral_bladder) - serosaOfBladder_ventral = findOrCreateAnnotationGroupForTerm(annotationGroups, region, get_bladder_term("ventral part of serosa of urinary bladder")) + serosaOfBladder_ventral = \ + findOrCreateAnnotationGroupForTerm(annotationGroups, region, + get_bladder_term("ventral part of serosa of urinary bladder")) serosaOfBladder_ventral.getMeshGroup(mesh2d).addElementsConditional(is_bladder_serosa_ventral) is_bladder_lumen_dorsal = fm.createFieldAnd(is_urinaryBladder_lumen, is_dorsal_bladder) - lumenOfBladder_dorsal = findOrCreateAnnotationGroupForTerm(annotationGroups, region, get_bladder_term("dorsal part of urinary bladder lumen")) + lumenOfBladder_dorsal = \ + findOrCreateAnnotationGroupForTerm(annotationGroups, region, + get_bladder_term("dorsal part of urinary bladder lumen")) lumenOfBladder_dorsal.getMeshGroup(mesh2d).addElementsConditional(is_bladder_lumen_dorsal) is_bladder_lumen_ventral = fm.createFieldAnd(is_urinaryBladder_lumen, is_ventral_bladder) - lumenOfBladder_ventral = findOrCreateAnnotationGroupForTerm(annotationGroups, region, get_bladder_term("ventral part of urinary bladder lumen")) + lumenOfBladder_ventral = \ + findOrCreateAnnotationGroupForTerm(annotationGroups, region, + get_bladder_term("ventral part of urinary bladder lumen")) lumenOfBladder_ventral.getMeshGroup(mesh2d).addElementsConditional(is_bladder_lumen_ventral) is_body_serosa_dorsal = fm.createFieldAnd(is_body_serosa, is_dorsal_bladder) - serosaOfBody_dorsal = findOrCreateAnnotationGroupForTerm(annotationGroups, region, get_bladder_term("dorsal part of serosa of body of urinary bladder")) + serosaOfBody_dorsal = \ + findOrCreateAnnotationGroupForTerm(annotationGroups, region, + get_bladder_term("dorsal part of serosa of body of urinary bladder")) serosaOfBody_dorsal.getMeshGroup(mesh2d).addElementsConditional(is_body_serosa_dorsal) is_body_serosa_ventral = fm.createFieldAnd(is_body_serosa, is_ventral_bladder) - serosaOfBody_ventral = findOrCreateAnnotationGroupForTerm(annotationGroups, region, get_bladder_term("ventral part of serosa of body of urinary bladder")) + serosaOfBody_ventral = \ + findOrCreateAnnotationGroupForTerm(annotationGroups, region, + get_bladder_term("ventral part of serosa of body of urinary bladder")) serosaOfBody_ventral.getMeshGroup(mesh2d).addElementsConditional(is_body_serosa_ventral) is_body_lumen_dorsal = fm.createFieldAnd(is_body_lumen, is_dorsal_bladder) - lumenOfBody_dorsal = findOrCreateAnnotationGroupForTerm(annotationGroups, region, get_bladder_term("dorsal part of lumen of body of urinary bladder")) + lumenOfBody_dorsal = \ + findOrCreateAnnotationGroupForTerm(annotationGroups, region, + get_bladder_term("dorsal part of lumen of body of urinary bladder")) lumenOfBody_dorsal.getMeshGroup(mesh2d).addElementsConditional(is_body_lumen_dorsal) is_body_lumen_ventral = fm.createFieldAnd(is_body_lumen, is_ventral_bladder) - lumenOfBody_ventral = findOrCreateAnnotationGroupForTerm(annotationGroups, region, get_bladder_term("ventral part of lumen of body of urinary bladder")) + lumenOfBody_ventral = \ + findOrCreateAnnotationGroupForTerm(annotationGroups, region, + get_bladder_term("ventral part of lumen of body of urinary bladder")) lumenOfBody_ventral.getMeshGroup(mesh2d).addElementsConditional(is_body_lumen_ventral) is_neck_serosa_dorsal = fm.createFieldAnd(is_neck_serosa, is_dorsal_bladder) - serosaOfNeck_dorsal = findOrCreateAnnotationGroupForTerm(annotationGroups, region, get_bladder_term("dorsal part of serosa of neck of urinary bladder")) + serosaOfNeck_dorsal = \ + findOrCreateAnnotationGroupForTerm(annotationGroups, region, + get_bladder_term("dorsal part of serosa of neck of urinary bladder")) serosaOfNeck_dorsal.getMeshGroup(mesh2d).addElementsConditional(is_neck_serosa_dorsal) is_neck_serosa_ventral = fm.createFieldAnd(is_neck_serosa, is_ventral_bladder) - serosaOfNeck_ventral = findOrCreateAnnotationGroupForTerm(annotationGroups, region, get_bladder_term("ventral part of serosa of neck of urinary bladder")) + serosaOfNeck_ventral = \ + findOrCreateAnnotationGroupForTerm(annotationGroups, region, + get_bladder_term("ventral part of serosa of neck of urinary bladder")) serosaOfNeck_ventral.getMeshGroup(mesh2d).addElementsConditional(is_neck_serosa_ventral) is_neck_lumen_dorsal = fm.createFieldAnd(is_neck_lumen, is_dorsal_bladder) - lumenOfNeck_dorsal = findOrCreateAnnotationGroupForTerm(annotationGroups, region, get_bladder_term("dorsal part of lumen of neck of urinary bladder")) + lumenOfNeck_dorsal = \ + findOrCreateAnnotationGroupForTerm(annotationGroups, region, + get_bladder_term("dorsal part of lumen of neck of urinary bladder")) lumenOfNeck_dorsal.getMeshGroup(mesh2d).addElementsConditional(is_neck_lumen_dorsal) is_neck_lumen_ventral = fm.createFieldAnd(is_neck_lumen, is_ventral_bladder) - lumenOfNeck_ventral = findOrCreateAnnotationGroupForTerm(annotationGroups, region, get_bladder_term("ventral part of lumen of neck of urinary bladder")) + lumenOfNeck_ventral = \ + findOrCreateAnnotationGroupForTerm(annotationGroups, region, + get_bladder_term("ventral part of lumen of neck of urinary bladder")) lumenOfNeck_ventral.getMeshGroup(mesh2d).addElementsConditional(is_neck_lumen_ventral) if options['Include urethra'] == True: urethraGroup = getAnnotationGroupForTerm(annotationGroups, get_bladder_term("urethra")) - urethraVentralGroup = getAnnotationGroupForTerm(annotationGroups, get_bladder_term("ventral part of urethra")) + urethraVentralGroup = getAnnotationGroupForTerm(annotationGroups, + get_bladder_term("ventral part of urethra")) urethraDorsalGroup = getAnnotationGroupForTerm(annotationGroups, get_bladder_term("dorsal part of urethra")) is_urethra = urethraGroup.getFieldElementGroup(mesh2d) @@ -1249,46 +1306,60 @@ def defineFaceAnnotations(cls, region, options, annotationGroups): is_dorsal_urethra = urethraDorsalGroup.getFieldElementGroup(mesh2d) is_ventral_urethra = urethraVentralGroup.getFieldElementGroup(mesh2d) - serosaOfUrethra = findOrCreateAnnotationGroupForTerm(annotationGroups, region, get_bladder_term("serosa of urethra")) + serosaOfUrethra = findOrCreateAnnotationGroupForTerm(annotationGroups, region, + get_bladder_term("serosa of urethra")) serosaOfUrethra.getMeshGroup(mesh2d).addElementsConditional(is_urethra_serosa) - lumenOfUrethra = findOrCreateAnnotationGroupForTerm(annotationGroups, region, get_bladder_term("lumen of urethra")) + lumenOfUrethra = findOrCreateAnnotationGroupForTerm(annotationGroups, region, + get_bladder_term("lumen of urethra")) lumenOfUrethra.getMeshGroup(mesh2d).addElementsConditional(is_urethra_lumen) is_urethra_serosa_dorsal = fm.createFieldAnd(is_urethra_serosa, is_dorsal_urethra) - serosaOfUrethra_dorsal = findOrCreateAnnotationGroupForTerm(annotationGroups, region, get_bladder_term("dorsal part of serosa of urethra")) + serosaOfUrethra_dorsal = \ + findOrCreateAnnotationGroupForTerm(annotationGroups, region, + get_bladder_term("dorsal part of serosa of urethra")) serosaOfUrethra_dorsal.getMeshGroup(mesh2d).addElementsConditional(is_urethra_serosa_dorsal) is_urethra_serosa_ventral = fm.createFieldAnd(is_urethra_serosa, is_ventral_urethra) - serosaOfUrethra_ventral = findOrCreateAnnotationGroupForTerm(annotationGroups, region, get_bladder_term("ventral part of serosa of urethra")) + serosaOfUrethra_ventral = \ + findOrCreateAnnotationGroupForTerm(annotationGroups, region, + get_bladder_term("ventral part of serosa of urethra")) serosaOfUrethra_ventral.getMeshGroup(mesh2d).addElementsConditional(is_urethra_serosa_ventral) is_urethra_lumen_dorsal = fm.createFieldAnd(is_urethra_lumen, is_dorsal_urethra) - lumenOfUrethra_dorsal = findOrCreateAnnotationGroupForTerm(annotationGroups, region, get_bladder_term("dorsal part of lumen of urethra")) + lumenOfUrethra_dorsal = \ + findOrCreateAnnotationGroupForTerm(annotationGroups, region, + get_bladder_term("dorsal part of lumen of urethra")) lumenOfUrethra_dorsal.getMeshGroup(mesh2d).addElementsConditional(is_urethra_lumen_dorsal) is_urethra_lumen_ventral = fm.createFieldAnd(is_urethra_lumen, is_ventral_urethra) - lumenOfUrethra_ventral = findOrCreateAnnotationGroupForTerm(annotationGroups, region, get_bladder_term("ventral part of lumen of urethra")) + lumenOfUrethra_ventral = \ + findOrCreateAnnotationGroupForTerm(annotationGroups, region, + get_bladder_term("ventral part of lumen of urethra")) lumenOfUrethra_ventral.getMeshGroup(mesh2d).addElementsConditional(is_urethra_lumen_ventral) -def generateUreterInlets(region, nodes, mesh, ureterDefaultOptions,elementsCountAround, elementsCountThroughWall, - elementsCountAroundUreter, trackSurfaceUreter1, ureter1Position, trackSurfaceUreter2, - ureter2Position, ureterElementPositionDown, ureterElementPositionAround, - xBladder, d1Bladder, d2Bladder, nextNodeIdentifier, nextElementIdentifier, - elementsCountUreterRadial, ureterMeshGroup, bladderMeshGroup): + +def generateUreterInlets(region, nodes, mesh, ureterDefaultOptions, elementsCountAround, elementsCountThroughWall, + elementsCountAroundUreter, trackSurfaceUreter1, ureter1Position, trackSurfaceUreter2, + ureter2Position, ureterElementPositionDown, ureterElementPositionAround, + xBladder, d1Bladder, d2Bladder, nextNodeIdentifier, nextElementIdentifier, + elementsCountUreterRadial, ureterMeshGroup, bladderMeshGroup): # Update ostium and vessel wall relative thickness with elementsCountThroughWall # Set to uniform layer thicknesses now, can be changed to varying thickness later - ureterDefaultOptions['Ostium wall relative thicknesses'] = [1.0 / elementsCountThroughWall] * elementsCountThroughWall - ureterDefaultOptions['Vessel wall relative thicknesses'] = [1.0 / elementsCountThroughWall] * elementsCountThroughWall + ureterDefaultOptions['Ostium wall relative thicknesses'] = [1.0 / elementsCountThroughWall] * \ + elementsCountThroughWall + ureterDefaultOptions['Vessel wall relative thicknesses'] = [1.0 / elementsCountThroughWall] * \ + elementsCountThroughWall # Create ureters on the surface # Ureter 1 - centerUreter1_x, centerUreter1_d1, centerUreter1_d2 = trackSurfaceUreter1.evaluateCoordinates(ureter1Position, - derivatives=True) + centerUreter1_x, centerUreter1_d1, centerUreter1_d2 = \ + trackSurfaceUreter1.evaluateCoordinates(ureter1Position, derivatives=True) td1, td2, td3 = calculate_surface_axes(centerUreter1_d1, centerUreter1_d2, [1.0, 0.0, 0.0]) - endPointStartId1 = elementsCountThroughWall + 1 \ - + (elementsCountThroughWall + 1) * elementsCountAround * (ureterElementPositionDown - (1 if ureter1Position.xi2 > 0.5 else 2)) \ - + ureterElementPositionAround + (1 if ureter1Position.xi1 > 0.5 else 0) + endPointStartId1 = \ + elementsCountThroughWall + 1 + (elementsCountThroughWall + 1) * elementsCountAround * \ + (ureterElementPositionDown - (1 if ureter1Position.xi2 > 0.5 else 2)) + \ + ureterElementPositionAround + (1 if ureter1Position.xi1 > 0.5 else 0) ureter1StartCornerx = xBladder[endPointStartId1 - 1] v1 = [(ureter1StartCornerx[c] - centerUreter1_x[c]) for c in range(3)] ureter1Direction = vector.crossproduct3(td3, v1) @@ -1301,10 +1372,10 @@ def generateUreterInlets(region, nodes, mesh, ureterDefaultOptions,elementsCount centerUreter2_x, centerUreter2_d1, centerUreter2_d2 = trackSurfaceUreter2.evaluateCoordinates(ureter2Position, derivatives=True) ad1, ad2, ad3 = calculate_surface_axes(centerUreter2_d1, centerUreter2_d2, [1.0, 0.0, 0.0]) - endPointStartId2 = elementsCountThroughWall + 1 \ - + (elementsCountThroughWall + 1) * elementsCountAround * \ - (ureterElementPositionDown - (1 if ureter1Position.xi2 > 0.5 else 2)) \ - + elementsCountAround - ureterElementPositionAround + (-1 if ureter1Position.xi1 > 0.5 else 0) + endPointStartId2 = elementsCountThroughWall + 1 + \ + (elementsCountThroughWall + 1) * elementsCountAround * \ + (ureterElementPositionDown - (1 if ureter1Position.xi2 > 0.5 else 2)) + \ + elementsCountAround - ureterElementPositionAround + (-1 if ureter1Position.xi1 > 0.5 else 0) ureter2StartCornerx = xBladder[endPointStartId2 - 1] v2 = [(ureter2StartCornerx[c] - centerUreter2_x[c]) for c in range(3)] ureter2Direction = vector.crossproduct3(ad3, v2) @@ -1396,9 +1467,9 @@ def generateUreterInlets(region, nodes, mesh, ureterDefaultOptions,elementsCount firstIdxAround2 = elementsCountAround//2 - ureterElementPositionAround - (2 if ureter1Position.xi1 > 0.5 else 1) for n in range(3): - endProportions1.append([(firstIdxAround1)/elementsAroundTrackSurface1, + endProportions1.append([firstIdxAround1/elementsAroundTrackSurface1, (firstIdxAlong + n)/elementsAlongTrackSurface1]) - endProportions2.append([(firstIdxAround2) / elementsAroundTrackSurface2, + endProportions2.append([firstIdxAround2 / elementsAroundTrackSurface2, (firstIdxAlong + n) / elementsAlongTrackSurface2]) for n in range(2): endProportions1.append([(firstIdxAround1 + n + 1) / elementsAroundTrackSurface1, @@ -1410,7 +1481,7 @@ def generateUreterInlets(region, nodes, mesh, ureterDefaultOptions,elementsCount (firstIdxAlong - n + 1) / elementsAlongTrackSurface1]) endProportions2.append([(firstIdxAround2 + 2) / elementsAroundTrackSurface2, (firstIdxAlong - n + 1) / elementsAlongTrackSurface2]) - endProportions1.append([(firstIdxAround1 + 1 )/ elementsAroundTrackSurface1, + endProportions1.append([(firstIdxAround1 + 1) / elementsAroundTrackSurface1, firstIdxAlong / elementsAlongTrackSurface1]) endProportions2.append([(firstIdxAround2 + 1) / elementsAroundTrackSurface2, firstIdxAlong / elementsAlongTrackSurface2]) @@ -1433,10 +1504,13 @@ def generateUreterInlets(region, nodes, mesh, ureterDefaultOptions,elementsCount # Delete elements under annulus mesh element_identifiers = [] - elementToDeleteStartIdx1 = elementsCountThroughWall * elementsCountAround * (ureterElementPositionDown - (0 if ureter1Position.xi2 > 0.5 else 1)) \ - + ureterElementPositionAround + (1 if ureter1Position.xi1 > 0.5 else 0) - elementToDeleteStartIdx2 = elementsCountThroughWall * elementsCountAround * (ureterElementPositionDown - (0 if ureter1Position.xi2 > 0.5 else 1)) \ - + elementsCountAround - ureterElementPositionAround + (-1 if ureter1Position.xi1 > 0.5 else 0) + elementToDeleteStartIdx1 = elementsCountThroughWall * elementsCountAround * \ + (ureterElementPositionDown - (0 if ureter1Position.xi2 > 0.5 else 1)) + \ + ureterElementPositionAround + (1 if ureter1Position.xi1 > 0.5 else 0) + elementToDeleteStartIdx2 = elementsCountThroughWall * elementsCountAround * \ + (ureterElementPositionDown - (0 if ureter1Position.xi2 > 0.5 else 1)) + \ + elementsCountAround - ureterElementPositionAround + \ + (-1 if ureter1Position.xi1 > 0.5 else 0) elementToDeleteStartIdxList = [elementToDeleteStartIdx1, elementToDeleteStartIdx2] for i in range(2): elementToDeleteStart = elementToDeleteStartIdxList[i] diff --git a/src/scaffoldmaker/meshtypes/meshtype_3d_colonsegment1.py b/src/scaffoldmaker/meshtypes/meshtype_3d_colonsegment1.py index f101ed37..4ae1810c 100644 --- a/src/scaffoldmaker/meshtypes/meshtype_3d_colonsegment1.py +++ b/src/scaffoldmaker/meshtypes/meshtype_3d_colonsegment1.py @@ -12,8 +12,8 @@ from opencmiss.zinc.element import Element from opencmiss.zinc.field import Field from opencmiss.zinc.node import Node -from scaffoldmaker.annotation.annotationgroup import AnnotationGroup, mergeAnnotationGroups, findOrCreateAnnotationGroupForTerm, findAnnotationGroupByName, \ - getAnnotationGroupForTerm +from scaffoldmaker.annotation.annotationgroup import AnnotationGroup, mergeAnnotationGroups, \ + findOrCreateAnnotationGroupForTerm, findAnnotationGroupByName, getAnnotationGroupForTerm from scaffoldmaker.annotation.colon_terms import get_colon_term from scaffoldmaker.meshtypes.scaffold_base import Scaffold_base from scaffoldmaker.utils import interpolation as interp @@ -26,7 +26,7 @@ class MeshType_3d_colonsegment1(Scaffold_base): - ''' + """ Generates a single 3-D colon segment mesh with variable numbers of tenia coli, elements around, along the central line, and through wall. The cross-section profile of the colon @@ -37,7 +37,7 @@ class MeshType_3d_colonsegment1(Scaffold_base): Human (Default): Three tenia coli, triangular profile with rounded corners at the inter-haustral septa, and a clover profile in the intra-haustral region. - ''' + """ @staticmethod def getName(): @@ -344,10 +344,10 @@ def generateBaseMesh(cls, region, options): [circularMuscleGroup], [longitudinalMuscleGroup]] # Create coordinates and derivatives - xList, d1List, d2List, d3List, curvatureList = tubemesh.getCoordinatesFromInner(xWarpedList, d1WarpedList, - d2WarpedList, d3WarpedUnitList, contractedWallThicknessList, relativeThicknessList, - elementsCountAround, elementsCountAlongSegment, elementsCountThroughWall, - transitElementList) + xList, d1List, d2List, d3List, curvatureList = \ + tubemesh.getCoordinatesFromInner(xWarpedList, d1WarpedList, d2WarpedList, d3WarpedUnitList, + contractedWallThicknessList, relativeThicknessList, elementsCountAround, + elementsCountAlongSegment, elementsCountThroughWall, transitElementList) xColonSegment = d1ColonSegment = d2ColonSegment = [] @@ -406,14 +406,14 @@ def refineMesh(cls, meshrefinement, options): @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. - ''' + """ # Create 2d surface mesh groups fm = region.getFieldmodule() mesh2d = fm.findMeshByDimension(2) @@ -427,7 +427,8 @@ def defineFaceAnnotations(cls, region, options, annotationGroups): serosa = findOrCreateAnnotationGroupForTerm(annotationGroups, region, get_colon_term("serosa of colon")) serosa.getMeshGroup(mesh2d).addElementsConditional(is_serosa) is_mucosaInnerSurface = fm.createFieldAnd(is_colon, is_exterior_face_xi3_0) - mucosaInnerSurface = findOrCreateAnnotationGroupForTerm(annotationGroups, region, get_colon_term("luminal surface of the colonic mucosa")) + mucosaInnerSurface = findOrCreateAnnotationGroupForTerm(annotationGroups, region, + get_colon_term("luminal surface of the colonic mucosa")) mucosaInnerSurface.getMeshGroup(mesh2d).addElementsConditional(is_mucosaInnerSurface) @@ -477,8 +478,9 @@ def getColonSegmentTubeMeshInnerPoints(self, nSegment): xInner, d1Inner, d2Inner, transitElementList, xiSegment, relaxedLengthSegment, contractedWallThicknessSegment, \ segmentAxis, annotationGroupsAround = \ getColonSegmentInnerPoints(self._region, - self._elementsCountAroundTC, self._elementsCountAroundHaustrum, self._elementsCountAlongSegment, - self._tcCount, self._segmentLengthEndDerivativeFactor, self._segmentLengthMidDerivativeFactor, + self._elementsCountAroundTC, self._elementsCountAroundHaustrum, + self._elementsCountAlongSegment, self._tcCount, + self._segmentLengthEndDerivativeFactor, self._segmentLengthMidDerivativeFactor, self._segmentLength, self._wallThickness, self._cornerInnerRadiusFactor, haustrumInnerRadiusFactorSegmentList, radiusSegmentList, dRadiusSegmentList, tcWidthSegmentList, @@ -497,8 +499,7 @@ def getColonSegmentTubeMeshInnerPoints(self, nSegment): contractedWallThickness = contractedWallThicknessSegment[startIdx:self._elementsCountAlongSegment + 1] self._contractedWallThicknessList += contractedWallThickness - return xInner, d1Inner, d2Inner, transitElementList, segmentAxis, \ - annotationGroupsAround + return xInner, d1Inner, d2Inner, transitElementList, segmentAxis, annotationGroupsAround def getTubeTCWidthList(self): return self._tubeTCWidthList @@ -510,13 +511,11 @@ def getContractedWallThicknessList(self): return self._contractedWallThicknessList -def getColonSegmentInnerPoints(region, elementsCountAroundTC, - elementsCountAroundHaustrum, elementsCountAlongSegment, - tcCount, segmentLengthEndDerivativeFactor, - segmentLengthMidDerivativeFactor, segmentLength, wallThickness, - cornerInnerRadiusFactor, haustrumInnerRadiusFactorSegmentList, - radiusSegmentList, dRadiusSegmentList, tcWidthSegmentList, - startPhase): +def getColonSegmentInnerPoints(region, elementsCountAroundTC, elementsCountAroundHaustrum, elementsCountAlongSegment, + tcCount, segmentLengthEndDerivativeFactor, segmentLengthMidDerivativeFactor, + segmentLength, wallThickness, cornerInnerRadiusFactor, + haustrumInnerRadiusFactorSegmentList, radiusSegmentList, dRadiusSegmentList, + tcWidthSegmentList, startPhase): """ Generates a 3-D colon segment mesh with variable numbers of tenia coli, numbers of elements around, along the central path, and through wall. @@ -572,9 +571,6 @@ def getColonSegmentInnerPoints(region, elementsCountAroundTC, transitElementList = transitElementListHaustrum * tcCount # create nodes - x = [0.0, 0.0, 0.0] - dx_ds1 = [0.0, 0.0, 0.0] - dx_ds2 = [0.0, 0.0, 0.0] sampleElementOut = 20 segmentAxis = [0.0, 0.0, 1.0] @@ -598,8 +594,9 @@ def getColonSegmentInnerPoints(region, elementsCountAroundTC, radius = radiusSegmentList[n2] tcWidth = tcWidthSegmentList[n2] - xHalfSet, d1HalfSet = createHalfSetInterHaustralSegment(elementsCountAroundTC, - elementsCountAroundHaustrum, tcCount, tcWidth, radius, cornerInnerRadiusFactor, sampleElementOut) + xHalfSet, d1HalfSet = \ + createHalfSetInterHaustralSegment(elementsCountAroundTC, elementsCountAroundHaustrum, tcCount, tcWidth, + radius, cornerInnerRadiusFactor, sampleElementOut) for i in range(len(xHalfSet)): d2HalfSet.append([0.0, 0.0, 0.0]) @@ -780,7 +777,8 @@ def getColonSegmentInnerPoints(region, elementsCountAroundTC, dx_ds1InnerAroundList = [] if startPhase == 0.0 and n2 == 0: d1Corrected = d1Phase0FirstFace - elif startPhase == 0.0 and elementsCountAlongSegment % 2 == 0 and n2 == int(elementsCountAlongSegment * 0.5): + elif startPhase == 0.0 and elementsCountAlongSegment % 2 == 0 and \ + n2 == int(elementsCountAlongSegment * 0.5): dx_ds1InnerAroundList = dx_ds1InnerAroundList + d1Phase0MidFace elif startPhase == 0.0 and n2 > elementsCountAlongSegment - 1: d1Corrected = d1Phase0LastFace @@ -789,7 +787,8 @@ def getColonSegmentInnerPoints(region, elementsCountAroundTC, dx_ds1InnerAroundList = dx_ds1InnerAroundList + d1180FirstFace elif startPhase == 180.0 and n2 > elementsCountAlongSegment - 1: dx_ds1InnerAroundList = dx_ds1InnerAroundList + d1180LastFace - elif startPhase == 180.0 and elementsCountAlongSegment % 2 == 0 and n2 == int(elementsCountAlongSegment * 0.5): + elif startPhase == 180.0 and elementsCountAlongSegment % 2 == 0 and \ + n2 == int(elementsCountAlongSegment * 0.5): d1Corrected = d1180MidFace else: @@ -824,9 +823,10 @@ def getColonSegmentInnerPoints(region, elementsCountAroundTC, tcCount) # Calculate xiList for relaxed state - xHalfSetRelaxed, d1HalfSetRelaxed = createHalfSetIntraHaustralSegment(elementsCountAroundTC, - elementsCountAroundHaustrum, tcCount, tcWidthSegmentList[n2], radiusSegmentList[n2], - cornerInnerRadiusFactor, sampleElementOut, haustrumInnerRadiusFactor) + xHalfSetRelaxed, d1HalfSetRelaxed = \ + createHalfSetIntraHaustralSegment(elementsCountAroundTC, elementsCountAroundHaustrum, tcCount, + tcWidthSegmentList[n2], radiusSegmentList[n2], + cornerInnerRadiusFactor, sampleElementOut, haustrumInnerRadiusFactor) xRelaxed, d1Relaxed, _ = getFullProfileFromHalfHaustrum(xHalfSetRelaxed, d1HalfSetRelaxed, d2Around, tcCount) xiFace, relaxedLengthAroundFace = getXiListFromOuterLengthProfile(xRelaxed, d1Relaxed, segmentAxis, @@ -923,7 +923,8 @@ def createHalfSetInterHaustralSegment(elementsCountAroundTC, elementsCountAround # Sample haustrum into equally sized elements xHaustrum, d1Haustrum, _, _ = sampleHaustrum(xLoop, d1Loop, xTC[-1], d1TC[-1], - arcLength / numberOfHalves, arcDistanceTCEdge, elementsCountAroundHaustrum) + arcLength / numberOfHalves, arcDistanceTCEdge, + elementsCountAroundHaustrum) xHalfSetInterHaustra = xHalfSetInterHaustra + xTC + xHaustrum[1:] d1HalfSetInterHaustra = d1HalfSetInterHaustra + d1TC + d1Haustrum[1:] @@ -932,7 +933,8 @@ def createHalfSetInterHaustralSegment(elementsCountAroundTC, elementsCountAround def createHalfSetIntraHaustralSegment(elementsCountAroundTC, elementsCountAroundHaustrum, - tcCount, tcWidth, radius, cornerInnerRadiusFactor, sampleElementOut, haustrumInnerRadiusFactor): + tcCount, tcWidth, radius, cornerInnerRadiusFactor, sampleElementOut, + haustrumInnerRadiusFactor): """ Find locations and derivative of nodes in half of an intra-haustral segment. Bow-tie profile for segment with two tenia coli and @@ -1295,8 +1297,9 @@ def getXiListFromOuterLengthProfile(xInner, d1Inner, segmentAxis, arcLengthList = [] for n1 in range(len(xOuter)): - arcLengthPerElement = interp.getCubicHermiteArcLength(xOuter[n1], d1Outer[n1], - xOuter[(n1 + 1) % len(xOuter)], d1Outer[(n1 + 1) % len(xOuter)]) + arcLengthPerElement = \ + interp.getCubicHermiteArcLength(xOuter[n1], d1Outer[n1], + xOuter[(n1 + 1) % len(xOuter)], d1Outer[(n1 + 1) % len(xOuter)]) arcLengthList.append(arcLengthPerElement) # Total arcLength @@ -1361,8 +1364,9 @@ def getTeniaColi(region, xList, d1List, d2List, d3List, curvatureList, d3TCRaw = [] tcWidth = tubeTCWidthList[n2] for N in range(tcCount): - idxTCMid = elementsCountThroughWall + 1 + (n2 - 1) * elementsCountAround * (elementsCountThroughWall + 1) \ - + elementsCountAround * elementsCountThroughWall + \ + idxTCMid = elementsCountThroughWall + 1 + (n2 - 1) * \ + elementsCountAround * (elementsCountThroughWall + 1) + \ + elementsCountAround * elementsCountThroughWall + \ N * (elementsCountAroundTC + elementsCountAroundHaustrum) if closedProximalEnd \ else n2 * elementsCountAround * (elementsCountThroughWall + 1) + \ elementsCountAround * elementsCountThroughWall + \ @@ -1371,7 +1375,8 @@ def getTeniaColi(region, xList, d1List, d2List, d3List, curvatureList, xMid = [xList[idxTCMid][i] + unitNorm[i] * tcThickness for i in range(3)] d1Mid = d1List[idxTCMid] TCStartIdx = idxTCMid - int(elementsCountAroundTC * 0.5) if N > 0 else \ - idxTCMid + tcCount * (elementsCountAroundTC + elementsCountAroundHaustrum) - int(elementsCountAroundTC * 0.5) + idxTCMid + tcCount * (elementsCountAroundTC + elementsCountAroundHaustrum) - \ + int(elementsCountAroundTC * 0.5) TCEndIdx = idxTCMid + int(elementsCountAroundTC * 0.5) if closedProximalEnd: tcWidth = vector.magnitude([xList[TCStartIdx][c] - xList[TCEndIdx][c] for c in range(3)]) @@ -1560,7 +1565,7 @@ def createFlatCoordinatesTeniaColi(xiList, relaxedLengthList, nx = [v1, v2, v3] nd1 = [d1, d2, d3] sx, sd1, _, _, _ = interp.sampleCubicHermiteCurves(nx, nd1, elementsCountAroundTC) - if N > 0 and N < tcCount: + if 0 < N < tcCount: w = sx[1:-1] dw = sd1[1:-1] if elementsCountAroundTC > 2 else nd1[1:-1] elif N == 0: @@ -1591,8 +1596,10 @@ def createFlatCoordinatesTeniaColi(xiList, relaxedLengthList, d2FlatListTC = d2FlatListTC + d2FlatListTC[-((elementsCountAroundTC - 1) * tcCount + 1):] xFlat, d1Flat, d2Flat, _ = combineTeniaColiWithColon(xFlatColon, d1FlatColon, d2FlatColon, [], - xFlatListTC, d1FlatListTC, d2FlatListTC, [], (elementsCountAroundTC - 1) * tcCount + 1, - elementsCountAround + 1, elementsCountAlong, elementsCountThroughWall, closedProximalEnd) + xFlatListTC, d1FlatListTC, d2FlatListTC, [], + (elementsCountAroundTC - 1) * tcCount + 1, + elementsCountAround + 1, elementsCountAlong, + elementsCountThroughWall, closedProximalEnd) return xFlat, d1Flat, d2Flat @@ -1638,7 +1645,6 @@ def createColonCoordinatesTeniaColi(xiList, relativeThicknessList, lengthToDiame elementsCountAround * elementsCountThroughWall xTCRaw = [] d1TCRaw = [] - d2TCRaw = [] for N in range(tcCount): TCMidIdx = faceStartIdx + N * (elementsCountAroundTC + elementsCountAroundHaustrum) TCStartIdx = TCMidIdx - int(elementsCountAroundTC * 0.5) if N > 0 else \ @@ -1685,7 +1691,7 @@ def createNodesAndElementsTeniaColi(region, Create nodes and elements for the coordinates and flat coordinates fields. Note that flat coordinates not implemented for closedProximalEnd yet. :param x, d1, d2, d3: coordinates and derivatives of coordinates field. - :param xFlat, d1Flat, d2Flat, d3Flat: coordinates and derivatives of + :param xFlat, d1Flat, d2Flat: coordinates and derivatives of flat coordinates field. :param xOrgan, d1Organ, d2Organ, d3Organ, organCoordinateFieldName: coordinates, derivatives and name of organ coordinates field. @@ -1741,7 +1747,7 @@ def createNodesAndElementsTeniaColi(region, elementtemplate = mesh.createElementtemplate() elementtemplate.setElementShapeType(Element.SHAPE_TYPE_CUBE) - result = elementtemplate.defineField(coordinates, -1, eft) + elementtemplate.defineField(coordinates, -1, eft) # Tenia coli edge elements elementtemplate1 = mesh.createElementtemplate() @@ -1864,9 +1870,12 @@ def createNodesAndElementsTeniaColi(region, flatCoordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D2_DS1DS2, 1, zero) if n1 == 0: # print('NodeIdentifier', nodeIdentifier, 'version 2', xFlatList[i+elementsCountAround]) - flatCoordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_VALUE, 2, xFlat[i + elementsCountAround]) - flatCoordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS1, 2, d1Flat[i + elementsCountAround]) - flatCoordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS2, 2, d2Flat[i + elementsCountAround]) + flatCoordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_VALUE, 2, + xFlat[i + elementsCountAround]) + flatCoordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS1, 2, + d1Flat[i + elementsCountAround]) + flatCoordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS2, 2, + d2Flat[i + elementsCountAround]) if useCrossDerivatives: flatCoordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D2_DS1DS2, 2, zero) nodeIdentifier = nodeIdentifier + 1 @@ -1883,9 +1892,12 @@ def createNodesAndElementsTeniaColi(region, if useCrossDerivatives: flatCoordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D2_DS1DS2, 1, zero) if nTC == 0: - flatCoordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_VALUE, 2, xFlat[j + (elementsCountAroundTC - 1) * tcCount]) - flatCoordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS1, 2, d1Flat[j + (elementsCountAroundTC - 1) * tcCount]) - flatCoordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS2, 2, d2Flat[j + (elementsCountAroundTC - 1) * tcCount]) + flatCoordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_VALUE, 2, + xFlat[j + (elementsCountAroundTC - 1) * tcCount]) + flatCoordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS1, 2, + d1Flat[j + (elementsCountAroundTC - 1) * tcCount]) + flatCoordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS2, 2, + d2Flat[j + (elementsCountAroundTC - 1) * tcCount]) if useCrossDerivatives: flatCoordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D2_DS1DS2, 2, zero) nodeIdentifier = nodeIdentifier + 1 @@ -1950,7 +1962,7 @@ def createNodesAndElementsTeniaColi(region, math.sin(radiansAround), math.cos(radiansAround), radiansPerElementAround, math.sin(radiansAroundNext), math.cos(radiansAroundNext), radiansPerElementAround ] - result = element.setScaleFactors(eft3, scalefactors) + element.setScaleFactors(eft3, scalefactors) elementIdentifier = elementIdentifier + 1 annotationGroups = annotationGroupsAround[e1] + annotationGroupsAlong[0] + \ annotationGroupsThroughWall[e3] @@ -1986,7 +1998,8 @@ def createNodesAndElementsTeniaColi(region, elementtemplate6.defineField(coordinates, -1, eft6) nodeIdentifiers = [bni21, bni22, bni23, bni31, bni31 + 1] - element = mesh.createElement(elementIdentifier, elementtemplate4 if tetrahedralElement else elementtemplate6) + element = \ + mesh.createElement(elementIdentifier, elementtemplate4 if tetrahedralElement else elementtemplate6) element.setNodesByIdentifier(eft4 if tetrahedralElement else eft6, nodeIdentifiers) element.setScaleFactors(eft4 if tetrahedralElement else eft6, scalefactors) elementIdentifier = elementIdentifier + 1 @@ -2013,10 +2026,11 @@ def createNodesAndElementsTeniaColi(region, bni21 = elementsCountThroughWall + 1 bni22 = bni21 + elementsCountAround * elementsCountThroughWall + int(elementsCountAroundTC * 0.5) + \ - elementsCountAroundHaustrum + N * (elementsCountAroundTC + elementsCountAroundHaustrum) + eTC + 1 + elementsCountAroundHaustrum + N * (elementsCountAroundTC + elementsCountAroundHaustrum) + \ + eTC + 1 bni23 = bni22 + 1 - bni31 = bni21 + elementsCountAround * (elementsCountThroughWall + 1) + int(elementsCountAroundTC * 0.5) + \ - N * (elementsCountAroundTC - 1) + eTC + 1 + bni31 = bni21 + elementsCountAround * (elementsCountThroughWall + 1) + \ + int(elementsCountAroundTC * 0.5) + N * (elementsCountAroundTC - 1) + eTC + 1 if eTC == 0: eft5 = eftfactory.createEftTetrahedronXi1Zero(va * 10000, vb * 10000) @@ -2026,7 +2040,7 @@ def createNodesAndElementsTeniaColi(region, element.setNodesByIdentifier(eft5, nodeIdentifiers) element.setScaleFactors(eft5, scalefactors) - elif eTC > 0 and eTC < elementsCountAroundTC - 1: + elif 0 < eTC < elementsCountAroundTC - 1: eft6 = eftfactory.createEftPyramidBottomSimple(va * 10000, vb * 10000) elementtemplate6.defineField(coordinates, -1, eft6) nodeIdentifiers = [bni21, bni22, bni23, bni31 - 1, bni31] @@ -2043,9 +2057,10 @@ def createNodesAndElementsTeniaColi(region, element.setScaleFactors(eft4, scalefactors) elementIdentifier = elementIdentifier + 1 - annotationGroups = annotationGroupsAround[elementsCountAround + int( - elementsCountAroundTC * 0.5) + N * elementsCountAroundTC + eTC] + annotationGroupsAlong[0] + \ - ([longitudinalMuscleGroup] if longitudinalMuscle else []) + annotationGroups = \ + annotationGroupsAround[elementsCountAround + int(elementsCountAroundTC * 0.5) + + N * elementsCountAroundTC + eTC] + annotationGroupsAlong[0] + \ + ([longitudinalMuscleGroup] if longitudinalMuscle else []) if annotationGroups: allAnnotationGroups = mergeAnnotationGroups(allAnnotationGroups, annotationGroups) for annotationGroup in annotationGroups: @@ -2088,7 +2103,8 @@ def createNodesAndElementsTeniaColi(region, elementtemplate6.defineField(coordinates, -1, eft6) nodeIdentifiers = [bni21, bni22, bni23, bni31, bni32] - element = mesh.createElement(elementIdentifier, elementtemplate5 if tetrahedralElement else elementtemplate6) + element = \ + mesh.createElement(elementIdentifier, elementtemplate5 if tetrahedralElement else elementtemplate6) element.setNodesByIdentifier(eft5 if tetrahedralElement else eft6, nodeIdentifiers) element.setScaleFactors(eft5 if tetrahedralElement else eft6, scalefactors) elementIdentifier = elementIdentifier + 1 @@ -2144,10 +2160,10 @@ def createNodesAndElementsTeniaColi(region, # Add elements for tenia coli for eTC in range(int(elementsCountAroundTC * 0.5)): if closedProximalEnd: - bni21 = elementsCountThroughWall + 1 + (e2 - 1) * now + elementsCountThroughWall * elementsCountAround + \ - eTC + 1 + tcOffset1 - bni22 = elementsCountThroughWall + 1 + (e2 - 1) * now + elementsCountThroughWall * elementsCountAround + \ - eTC + 2 + tcOffset1 + bni21 = elementsCountThroughWall + 1 + (e2 - 1) * now + \ + elementsCountThroughWall * elementsCountAround + eTC + 1 + tcOffset1 + bni22 = elementsCountThroughWall + 1 + (e2 - 1) * now + \ + elementsCountThroughWall * elementsCountAround + eTC + 2 + tcOffset1 bni31 = elementsCountThroughWall + 1 + e2 * now + eTC + 1 + tcOffset1 bni32 = elementsCountThroughWall + 1 + e2 * now + eTC + 2 + tcOffset1 else: @@ -2162,14 +2178,20 @@ def createNodesAndElementsTeniaColi(region, else: nodeIdentifiers = [bni21, bni22, bni21 + now + tcOffset, bni22 + now + tcOffset, bni31, bni31 + now + tcOffset] - element = mesh.createElement(elementIdentifier, elementtemplate if eTC < int(elementsCountAroundTC * 0.5) - 1 else elementtemplate1) + element = \ + mesh.createElement(elementIdentifier, + elementtemplate if eTC < int(elementsCountAroundTC * 0.5) - 1 else elementtemplate1) element.setNodesByIdentifier(eft if eTC < int(elementsCountAroundTC * 0.5) - 1 else eft1, nodeIdentifiers) if xFlat: - element.merge(flatElementtemplate1 if eTC < int(elementsCountAroundTC * 0.5) - 1 else flatElementtemplate3) - element.setNodesByIdentifier(eftFlat3 if eTC < int(elementsCountAroundTC * 0.5) - 1 else eftFlat5, nodeIdentifiers) + element.merge(flatElementtemplate1 if eTC < int(elementsCountAroundTC * 0.5) - 1 else + flatElementtemplate3) + element.setNodesByIdentifier(eftFlat3 if eTC < int(elementsCountAroundTC * 0.5) - 1 else + eftFlat5, nodeIdentifiers) if xOrgan: - element.merge(organElementtemplate if eTC < int(elementsCountAroundTC * 0.5) - 1 else organElementtemplate1) - element.setNodesByIdentifier(eftOrgan if eTC < int(elementsCountAroundTC * 0.5) - 1 else eftOrgan1, nodeIdentifiers) + element.merge(organElementtemplate if eTC < int(elementsCountAroundTC * 0.5) - 1 else + organElementtemplate1) + element.setNodesByIdentifier(eftOrgan if eTC < int(elementsCountAroundTC * 0.5) - 1 else + eftOrgan1, nodeIdentifiers) elementIdentifier = elementIdentifier + 1 annotationGroups = annotationGroupsAround[elementsCountAround + eTC] + annotationGroupsAlong[e2] + \ ([longitudinalMuscleGroup] if longitudinalMuscle else []) @@ -2187,8 +2209,8 @@ def createNodesAndElementsTeniaColi(region, + eTC + 1 + tcOffset1 + int(elementsCountAroundTC * 0.5) + \ (N + 1) * elementsCountAroundHaustrum + N * elementsCountAroundTC bni22 = elementsCountThroughWall + 1 + ( - e2 - 1) * now + elementsCountThroughWall * elementsCountAround \ - + eTC + 2 + tcOffset1 + int(elementsCountAroundTC * 0.5) + \ + e2 - 1) * now + elementsCountThroughWall * elementsCountAround + \ + eTC + 2 + tcOffset1 + int(elementsCountAroundTC * 0.5) + \ (N + 1) * elementsCountAroundHaustrum + N * elementsCountAroundTC bni31 = elementsCountThroughWall + 1 + e2 * now + eTC + 1 + tcOffset1 + \ int(elementsCountAroundTC * 0.5) - 1 + N * (elementsCountAroundTC - 1) @@ -2199,8 +2221,8 @@ def createNodesAndElementsTeniaColi(region, int(elementsCountAroundTC * 0.5) + ( N + 1) * elementsCountAroundHaustrum + N * elementsCountAroundTC bni22 = e2 * now + elementsCountThroughWall * elementsCountAround + eTC + 2 + tcOffset1 + \ - int(elementsCountAroundTC * 0.5) + ( - N + 1) * elementsCountAroundHaustrum + N * elementsCountAroundTC + int(elementsCountAroundTC * 0.5) + (N + 1) * elementsCountAroundHaustrum + \ + N * elementsCountAroundTC bni31 = (e2 + 1) * now + eTC + 1 + tcOffset1 + int(elementsCountAroundTC * 0.5) - 1 + \ N * (elementsCountAroundTC - 1) bni32 = (e2 + 1) * now + eTC + 2 + tcOffset1 + int(elementsCountAroundTC * 0.5) - 1 + \ @@ -2216,7 +2238,7 @@ def createNodesAndElementsTeniaColi(region, if xOrgan: element.merge(organElementtemplate2) element.setNodesByIdentifier(eftOrgan2, nodeIdentifiers) - elif eTC > 0 and eTC < elementsCountAroundTC - 1: + elif 0 < eTC < elementsCountAroundTC - 1: nodeIdentifiers = [bni21, bni22, bni21 + now + tcOffset, bni22 + now + tcOffset, bni31, bni32, bni31 + now + tcOffset, bni32 + now + tcOffset] element = mesh.createElement(elementIdentifier, elementtemplate) @@ -2239,9 +2261,10 @@ def createNodesAndElementsTeniaColi(region, element.merge(organElementtemplate1) element.setNodesByIdentifier(eftOrgan1, nodeIdentifiers) elementIdentifier = elementIdentifier + 1 - annotationGroups = annotationGroupsAround[elementsCountAround + int( - elementsCountAroundTC * 0.5) + N * elementsCountAroundTC + eTC] + annotationGroupsAlong[e2] + \ - ([longitudinalMuscleGroup] if longitudinalMuscle else []) + annotationGroups = \ + annotationGroupsAround[elementsCountAround + int(elementsCountAroundTC * 0.5) + + N * elementsCountAroundTC + eTC] + annotationGroupsAlong[e2] + \ + ([longitudinalMuscleGroup] if longitudinalMuscle else []) if annotationGroups: allAnnotationGroups = mergeAnnotationGroups(allAnnotationGroups, annotationGroups) for annotationGroup in annotationGroups: @@ -2250,21 +2273,23 @@ def createNodesAndElementsTeniaColi(region, for eTC in range(int(elementsCountAroundTC * 0.5)): if closedProximalEnd: - bni21 = elementsCountThroughWall + 1 + (e2 - 1) * now + elementsCountThroughWall * elementsCountAround + \ - eTC + 1 + tcOffset1 + int(elementsCountAroundTC * 0.5) + tcCount * elementsCountAroundHaustrum + \ + bni21 = elementsCountThroughWall + 1 + (e2 - 1) * now + \ + elementsCountThroughWall * elementsCountAround + eTC + 1 + tcOffset1 + \ + int(elementsCountAroundTC * 0.5) + tcCount * elementsCountAroundHaustrum + \ (tcCount - 1) * elementsCountAroundTC - bni22 = elementsCountThroughWall + 1 + (e2 - 1) * now + elementsCountThroughWall * elementsCountAround + \ - 1 + tcOffset1 if eTC == int(elementsCountAroundTC * 0.5 - 1) else bni21 + 1 + bni22 = elementsCountThroughWall + 1 + (e2 - 1) * now + \ + elementsCountThroughWall * elementsCountAround + 1 + \ + tcOffset1 if eTC == int(elementsCountAroundTC * 0.5 - 1) else bni21 + 1 bni31 = elementsCountThroughWall + 1 + e2 * now + eTC + 1 + tcOffset1 + \ int(elementsCountAroundTC * 0.5) - 1 + (tcCount - 1) * (elementsCountAroundTC - 1) bni32 = elementsCountThroughWall + 1 + e2 * now + 1 + tcOffset1 if eTC == int( elementsCountAroundTC * 0.5 - 1) \ else bni31 + 1 else: - bni21 = e2 * now + (elementsCountThroughWall) * elementsCountAround + eTC + 1 + tcOffset1 + \ + bni21 = e2 * now + elementsCountThroughWall * elementsCountAround + eTC + 1 + tcOffset1 + \ int(elementsCountAroundTC * 0.5) + tcCount * elementsCountAroundHaustrum + \ (tcCount - 1) * elementsCountAroundTC - bni22 = e2 * now + (elementsCountThroughWall) * elementsCountAround + 1 + tcOffset1 if eTC == int( + bni22 = e2 * now + elementsCountThroughWall * elementsCountAround + 1 + tcOffset1 if eTC == int( elementsCountAroundTC * 0.5 - 1) else bni21 + 1 bni31 = (e2 + 1) * now + eTC + 1 + tcOffset1 + int(elementsCountAroundTC * 0.5) - 1 + \ (tcCount - 1) * (elementsCountAroundTC - 1) @@ -2289,9 +2314,9 @@ def createNodesAndElementsTeniaColi(region, element.merge(organElementtemplate if eTC > 0 else organElementtemplate2) element.setNodesByIdentifier(eftOrgan if eTC > 0 else eftOrgan2, nodeIdentifiers) elementIdentifier = elementIdentifier + 1 - annotationGroups = annotationGroupsAround[ - elementsCountAround + int(elementsCountAroundTC * (tcCount - 0.5)) + eTC] + \ - annotationGroupsAlong[e2] + ([longitudinalMuscleGroup] if longitudinalMuscle else []) + annotationGroups = \ + annotationGroupsAround[elementsCountAround + int(elementsCountAroundTC * (tcCount - 0.5)) + eTC] + \ + annotationGroupsAlong[e2] + ([longitudinalMuscleGroup] if longitudinalMuscle else []) if annotationGroups: allAnnotationGroups = mergeAnnotationGroups(allAnnotationGroups, annotationGroups) for annotationGroup in annotationGroups: diff --git a/src/scaffoldmaker/meshtypes/meshtype_3d_ostium1.py b/src/scaffoldmaker/meshtypes/meshtype_3d_ostium1.py index 10676bf0..a217703b 100644 --- a/src/scaffoldmaker/meshtypes/meshtype_3d_ostium1.py +++ b/src/scaffoldmaker/meshtypes/meshtype_3d_ostium1.py @@ -21,9 +21,9 @@ class MeshType_3d_ostium1(Scaffold_base): - ''' + """ Generates a 3-D single or double/common ostium inlet or outlet. - ''' + """ @staticmethod def getName(): return '3D Ostium 1' @@ -31,33 +31,33 @@ def getName(): @classmethod def getDefaultOptions(cls, parameterSetName='Default'): return { - 'Number of vessels' : 2, - 'Number of elements across common' : 2, - 'Number of elements around ostium' : 10, - 'Number of elements along' : 1, - 'Number of elements through wall' : 1, - 'Unit scale' : 1.0, - 'Outlet' : False, - 'Ostium diameter' : 1.0, - 'Ostium length' : 0.4, - 'Ostium wall thickness' : 0.08, + 'Number of vessels': 2, + 'Number of elements across common': 2, + 'Number of elements around ostium': 10, + 'Number of elements along': 1, + 'Number of elements through wall': 1, + 'Unit scale': 1.0, + 'Outlet': False, + 'Ostium diameter': 1.0, + 'Ostium length': 0.4, + 'Ostium wall thickness': 0.08, 'Ostium wall relative thicknesses': [1.0], - 'Ostium inter-vessel distance' : 0.8, - 'Ostium inter-vessel height' : 0.0, - 'Use linear through ostium wall' : False, - 'Vessel end length factor' : 1.0, - 'Vessel inner diameter' : 0.6, - 'Vessel wall thickness' : 0.04, + 'Ostium inter-vessel distance': 0.8, + 'Ostium inter-vessel height': 0.0, + 'Use linear through ostium wall': False, + 'Vessel end length factor': 1.0, + 'Vessel inner diameter': 0.6, + 'Vessel wall thickness': 0.04, 'Vessel wall relative thicknesses': [1.0], - 'Vessel angle 1 degrees' : 0.0, - 'Vessel angle 1 spread degrees' : 0.0, - 'Vessel angle 2 degrees' : 0.0, - 'Use linear through vessel wall' : True, - #'Use cross derivatives' : False, - 'Refine' : False, - 'Refine number of elements around' : 4, - 'Refine number of elements along' : 4, - 'Refine number of elements through wall' : 1 + 'Vessel angle 1 degrees': 0.0, + 'Vessel angle 1 spread degrees': 0.0, + 'Vessel angle 2 degrees': 0.0, + 'Use linear through vessel wall': True, + # 'Use cross derivatives': False, + 'Refine': False, + 'Refine number of elements around': 4, + 'Refine number of elements along': 4, + 'Refine number of elements through wall': 1 } @staticmethod @@ -85,7 +85,7 @@ def getOrderedOptionNames(): 'Vessel angle 1 spread degrees', 'Vessel angle 2 degrees', 'Use linear through vessel wall', - #'Use cross derivatives', # not implemented + # 'Use cross derivatives', # not implemented 'Refine', 'Refine number of elements around', 'Refine number of elements along', @@ -95,7 +95,7 @@ def getOrderedOptionNames(): @classmethod def checkOptions(cls, options): dependentChanges = False - vesselsCount = options['Number of vessels'] + vesselsCount = options['Number of vessels'] if vesselsCount < 1: vesselsCount = options['Number of vessels'] = 1 elif vesselsCount > 3: @@ -109,8 +109,8 @@ def checkOptions(cls, options): 'Refine number of elements through wall']: if options[key] < 1: options[key] = 1 - if (options['Number of elements around ostium'] < 2*vesselsCount) : - options['Number of elements around ostium'] = 2*vesselsCount + if options['Number of elements around ostium'] < 2 * vesselsCount: + options['Number of elements around ostium'] = 2 * vesselsCount dependentChanges = True # because can happen by changing number of vessels # currently must have even number around ostium if multiple vessels: if (vesselsCount > 1) and (options['Number of elements around ostium'] % 2): @@ -123,7 +123,7 @@ def checkOptions(cls, options): 'Ostium inter-vessel distance', 'Vessel inner diameter', 'Vessel wall thickness']: - if options[key] < 0.0: + if options[key] < 0.0: options[key] = 0.0 if options['Ostium diameter'] <= 0.0: options['Ostium diameter'] = 0.000001 # avoid division by zero @@ -135,16 +135,20 @@ def checkOptions(cls, options): if elementsThroughWall == 1: options[ostiumThicknessProportionsCountKey] = options[vesselThicknessProportionsCountKey] = [1.0] if ostiumWallCount < elementsThroughWall: - options[ostiumThicknessProportionsCountKey] += [options[ostiumThicknessProportionsCountKey][-1] for i in range(elementsThroughWall - ostiumWallCount)] + options[ostiumThicknessProportionsCountKey] += \ + [options[ostiumThicknessProportionsCountKey][-1] for i in range(elementsThroughWall - ostiumWallCount)] dependentChanges = True elif ostiumWallCount > elementsThroughWall: - options[ostiumThicknessProportionsCountKey] = options[ostiumThicknessProportionsCountKey][:elementsThroughWall] + options[ostiumThicknessProportionsCountKey] = \ + options[ostiumThicknessProportionsCountKey][:elementsThroughWall] dependentChanges = True if vesselWallCount < elementsThroughWall: - options[vesselThicknessProportionsCountKey] += [options[vesselThicknessProportionsCountKey][-1] for i in range(elementsThroughWall - vesselWallCount)] + options[vesselThicknessProportionsCountKey] += \ + [options[vesselThicknessProportionsCountKey][-1] for i in range(elementsThroughWall - vesselWallCount)] dependentChanges = True elif vesselWallCount > elementsThroughWall: - options[vesselThicknessProportionsCountKey] = options[vesselThicknessProportionsCountKey][:elementsThroughWall] + options[vesselThicknessProportionsCountKey] = \ + options[vesselThicknessProportionsCountKey][:elementsThroughWall] dependentChanges = True return dependentChanges @@ -157,20 +161,22 @@ def generateBaseMesh(cls, region, options): :return: [] empty list of AnnotationGroup """ unitScale = options['Unit scale'] - ostiumRadius = 0.5*unitScale*options['Ostium diameter'] - interVesselDistance = unitScale*options['Ostium inter-vessel distance'] - - scale = 1.1*(ostiumRadius*2.0 + interVesselDistance) - nx = [ [ -scale, -scale, 0.0 ], [ scale, -scale, 0.0 ], [ -scale, scale, 0.0 ], [ scale, scale, 0.0 ] ] - nd1 = [ [ 2.0*scale, 0.0, 0.0 ] ]*4 - nd2 = [ [ 0.0, 2.0*scale, 0.0 ] ]*4 - # curve track surface: - #zf = 2.0 - #nd1 = [ [ 2.0*scale, 0.0, zf*scale ], [ 2.0*scale, 0.0, -zf*scale ], [ 2.0*scale, 0.0, zf*scale ], [ 2.0*scale, 0.0, -zf*scale ] ] - #nd2 = [ [ 0.0, 2.0*scale, zf*scale ], [ 0.0, 2.0*scale, zf*scale ], [ 0.0, 2.0*scale, -zf*scale ], [ 0.0, 2.0*scale, -zf*scale ] ] + ostiumRadius = 0.5 * unitScale * options['Ostium diameter'] + interVesselDistance = unitScale * options['Ostium inter-vessel distance'] + + scale = 1.1 * (ostiumRadius * 2.0 + interVesselDistance) + nx = [[-scale, -scale, 0.0], [scale, -scale, 0.0], [-scale, scale, 0.0], [scale, scale, 0.0]] + nd1 = [[2.0 * scale, 0.0, 0.0]] * 4 + nd2 = [[0.0, 2.0 * scale, 0.0]] * 4 + # # curve track surface: + # zf = 2.0 + # nd1 = [ [ 2.0 * scale, 0.0, zf * scale ], [ 2.0 * scale, 0.0, -zf * scale ], + # [ 2.0 * scale, 0.0, zf * scale ], [ 2.0 * scale, 0.0, -zf * scale ] ] + # nd2 = [ [ 0.0, 2.0 * scale, zf * scale ], [ 0.0, 2.0 * scale, zf * scale ], + # [ 0.0, 2.0 * scale, -zf * scale ], [ 0.0, 2.0 * scale, -zf * scale ] ] trackSurface = TrackSurface(1, 1, nx, nd1, nd2) centrePosition = TrackSurfacePosition(0, 0, 0.5, 0.5) - axis1 = [ 1.0, 0.0, 0.0 ] + axis1 = [1.0, 0.0, 0.0] generateOstiumMesh(region, options, trackSurface, centrePosition, axis1) return [] # no annotation groups @@ -184,29 +190,30 @@ def refineMesh(cls, meshrefinement, options): refineElementsCountAround = options['Refine number of elements around'] refineElementsCountAlong = options['Refine number of elements along'] refineElementsCountThroughWall = options['Refine number of elements through wall'] - meshrefinement.refineAllElementsCubeStandard3d(refineElementsCountAround, refineElementsCountAlong, refineElementsCountThroughWall) + meshrefinement.refineAllElementsCubeStandard3d(refineElementsCountAround, refineElementsCountAlong, + refineElementsCountThroughWall) def getOstiumElementsCountsAroundVessels(elementsCountAroundOstium, elementsCountAcross, vesselsCount): - ''' + """ Determine numbers of elements around each vessel to fit numbers of elements around ostium. :param elementsCountAroundOstium: Number of elements around outside of ostium. - Minimum of 2 + 2*vesselsCount. Must be even if more than 1 vessels. + Minimum of 2 + 2 * vesselsCount. Must be even if more than 1 vessels. :param elementsCountAcross: Number of elements across ostium between multiple vessels. Unused if vesselsCount is 1. :param vesselsCount: Number of vessels from 1 to 3. :return: List of numbers of elements around each vessel to fit numbers around ostium, number of elements mid side (non-zero only for 3+ vessels). - ''' + """ assert 1 <= vesselsCount <= 3 - assert elementsCountAroundOstium >= 2*vesselsCount - assert (1 == vesselsCount) or (elementsCountAroundOstium%2 == 0) + assert elementsCountAroundOstium >= 2 * vesselsCount + assert (1 == vesselsCount) or (elementsCountAroundOstium % 2 == 0) assert elementsCountAcross > 0 if vesselsCount == 1: - return [ elementsCountAroundOstium ], 0 + return [elementsCountAroundOstium], 0 if vesselsCount == 2: - count = elementsCountAroundOstium//2 + elementsCountAcross - return [ count, count ], 0 + count = elementsCountAroundOstium // 2 + elementsCountAcross + return [count, count], 0 # vesselsCount == 3: # Around oinc Total:Vessels 1 Total:Vessels 2 # 6 1 8: 3-4-3 10: 4-6-4 @@ -215,51 +222,55 @@ def getOstiumElementsCountsAroundVessels(elementsCountAroundOstium, elementsCoun # 12 2 14: 5-6-5 16: 6-8-6 # 14 2 16: 6-6-6 18: 7-8-7 # 16 2 18: 7-6-7 20: 8-8-8 - elementsCountAroundMid = ((elementsCountAroundOstium + 1)//6) - countInner = 2*(elementsCountAroundMid + elementsCountAcross) - countOuter = (elementsCountAroundOstium - 2*elementsCountAroundMid)//2 + elementsCountAcross - return [ countOuter, countInner, countOuter ], elementsCountAroundMid + elementsCountAroundMid = ((elementsCountAroundOstium + 1) // 6) + countInner = 2 * (elementsCountAroundMid + elementsCountAcross) + countOuter = (elementsCountAroundOstium - 2 * elementsCountAroundMid) // 2 + elementsCountAcross + return [countOuter, countInner, countOuter], elementsCountAroundMid -def generateOstiumMesh(region, options, trackSurface, centrePosition, axis1, startNodeIdentifier = 1, startElementIdentifier = 1, - vesselMeshGroups = None, ostiumMeshGroups = None, wallAnnotationGroups = None): - ''' +def generateOstiumMesh(region, options, trackSurface, centrePosition, axis1, startNodeIdentifier=1, + startElementIdentifier=1, vesselMeshGroups=None, ostiumMeshGroups=None, + wallAnnotationGroups=None): + """ :param vesselMeshGroups: List (over number of vessels) of list of mesh groups to add vessel elements to. :param ostiumMeshGroups: List of mesh groups to add only row of elements at ostium end to. :param wallAnnotationGroups: list of annotation groups to add to wall elements. :return: nextNodeIdentifier, nextElementIdentifier, Ostium points tuple (ox[n3][n1][c], od1[n3][n1][c], od2[n3][n1][c], od3[n3][n1][c], oNodeId[n3][n1], oPositions). - ''' + """ vesselsCount = options['Number of vessels'] elementsCountAroundOstium = options['Number of elements around ostium'] elementsCountAcross = options['Number of elements across common'] - elementsCountsAroundVessels, elementsCountAroundMid = getOstiumElementsCountsAroundVessels(elementsCountAroundOstium, elementsCountAcross, vesselsCount) - elementsCountAroundEnd = (elementsCountAroundOstium - 2*elementsCountAroundMid)//2 - #print('\nvesselsCount', vesselsCount, 'elementsCountsAroundOstium', elementsCountAroundOstium, 'elementsCountAcross', elementsCountAcross) - #print('--> elementsCountsAroundVessels', elementsCountsAroundVessels, 'elementsCountAroundMid', elementsCountAroundMid) + elementsCountsAroundVessels, elementsCountAroundMid = \ + getOstiumElementsCountsAroundVessels(elementsCountAroundOstium, elementsCountAcross, vesselsCount) + elementsCountAroundEnd = (elementsCountAroundOstium - 2 * elementsCountAroundMid) // 2 + # print('\nvesselsCount', vesselsCount, 'elementsCountsAroundOstium', elementsCountAroundOstium, + # 'elementsCountAcross', elementsCountAcross) + # print('--> elementsCountsAroundVessels', elementsCountsAroundVessels, + # 'elementsCountAroundMid', elementsCountAroundMid) elementsCountAlong = options['Number of elements along'] elementsCountThroughWall = options['Number of elements through wall'] unitScale = options['Unit scale'] isOutlet = options['Outlet'] - ostiumRadius = 0.5*unitScale*options['Ostium diameter'] - ostiumLength = unitScale*options['Ostium length'] - ostiumWallThickness = unitScale*options['Ostium wall thickness'] + ostiumRadius = 0.5 * unitScale * options['Ostium diameter'] + ostiumLength = unitScale * options['Ostium length'] + ostiumWallThickness = unitScale * options['Ostium wall thickness'] ostiumWallThicknessProportions = copy.deepcopy(options['Ostium wall relative thicknesses']) - interVesselHeight = unitScale*options['Ostium inter-vessel height'] - interVesselDistance = unitScale*options['Ostium inter-vessel distance'] if (vesselsCount > 1) else 0.0 - halfInterVesselDistance = 0.5*interVesselDistance + interVesselHeight = unitScale * options['Ostium inter-vessel height'] + interVesselDistance = unitScale * options['Ostium inter-vessel distance'] if (vesselsCount > 1) else 0.0 + halfInterVesselDistance = 0.5 * interVesselDistance useCubicHermiteThroughOstiumWall = not(options['Use linear through ostium wall']) - vesselEndDerivative = ostiumLength*options['Vessel end length factor']/elementsCountAlong - vesselInnerRadius = 0.5*unitScale*options['Vessel inner diameter'] - vesselWallThickness = unitScale*options['Vessel wall thickness'] + vesselEndDerivative = ostiumLength * options['Vessel end length factor'] / elementsCountAlong + vesselInnerRadius = 0.5 * unitScale * options['Vessel inner diameter'] + vesselWallThickness = unitScale * options['Vessel wall thickness'] vesselWallThicknessProportions = copy.deepcopy(options['Vessel wall relative thicknesses']) vesselOuterRadius = vesselInnerRadius + vesselWallThickness vesselAngle1Radians = math.radians(options['Vessel angle 1 degrees']) vesselAngle1SpreadRadians = math.radians(options['Vessel angle 1 spread degrees']) vesselAngle2Radians = math.radians(options['Vessel angle 2 degrees']) useCubicHermiteThroughVesselWall = not(options['Use linear through vessel wall']) - useCrossDerivatives = False # options['Use cross derivatives'] # not implemented + # useCrossDerivatives = False # options['Use cross derivatives'] # not implemented fm = region.getFieldmodule() fm.beginChange() @@ -289,32 +300,34 @@ def generateOstiumMesh(region, options, trackSurface, centrePosition, axis1, sta # get directions in plane of surface at centre: cx, cd1, cd2 = trackSurface.evaluateCoordinates(centrePosition, True) trackDirection1, trackDirection2, centreNormal = calculate_surface_axes(cd1, cd2, axis1) - trackDirection2reverse = [ -d for d in trackDirection2 ] + trackDirection2reverse = [-d for d in trackDirection2] - halfCircumference = math.pi*ostiumRadius - circumference = 2.0*halfCircumference + halfCircumference = math.pi * ostiumRadius + circumference = 2.0 * halfCircumference distance = 0.0 elementLengthAroundOstiumMid = 0.0 - vesselsSpanAll = interVesselDistance*(vesselsCount - 1) - vesselsSpanMid = interVesselDistance*(vesselsCount - 2) + vesselsSpanAll = interVesselDistance * (vesselsCount - 1) + vesselsSpanMid = interVesselDistance * (vesselsCount - 2) if vesselsCount == 1: - elementLengthAroundOstiumEnd = circumference/elementsCountAroundOstium - vesselOstiumPositions = [ centrePosition ] - ocx = [ cx ] - ocd1 = [ trackDirection1 ] - ocd2 = [ trackDirection2 ] - ocd3 = [ centreNormal ] + elementLengthAroundOstiumEnd = circumference / elementsCountAroundOstium + vesselOstiumPositions = [centrePosition] + ocx = [cx] + ocd1 = [trackDirection1] + ocd2 = [trackDirection2] + ocd3 = [centreNormal] else: - elementLengthAroundOstiumEnd = (circumference + 2.0*interVesselDistance)/(elementsCountAroundOstium - 2*elementsCountAroundMid) + elementLengthAroundOstiumEnd = (circumference + 2.0 * interVesselDistance) / \ + (elementsCountAroundOstium - 2 * elementsCountAroundMid) if elementsCountAroundMid > 0: - elementLengthAroundOstiumMid = interVesselDistance*(vesselsCount - 2)/elementsCountAroundMid + elementLengthAroundOstiumMid = interVesselDistance * (vesselsCount - 2) / elementsCountAroundMid vesselOstiumPositions = [] - ocx = [] + ocx = [] ocd1 = [] ocd2 = [] ocd3 = [] for v in range(vesselsCount): - vesselOstiumPositions.append(trackSurface.trackVector(centrePosition, trackDirection1, (v/(vesselsCount - 1) - 0.5)*vesselsSpanAll)) + vesselOstiumPositions.append(trackSurface.trackVector(centrePosition, trackDirection1, + (v / (vesselsCount - 1) - 0.5) * vesselsSpanAll)) x, d1, d2 = trackSurface.evaluateCoordinates(vesselOstiumPositions[-1], -1) d1, d2, d3 = calculate_surface_axes(d1, d2, trackDirection1) ocx .append(x) @@ -334,29 +347,34 @@ def generateOstiumMesh(region, options, trackSurface, centrePosition, axis1, sta for n1 in range(elementsCountAroundOstium): elementLength = elementLengthAroundOstiumEnd if distance <= (vesselsSpanMid + halfInterVesselDistance): - position = trackSurface.trackVector(centrePosition, trackDirection1, 0.5*vesselsSpanMid - distance) + position = trackSurface.trackVector(centrePosition, trackDirection1, 0.5 * vesselsSpanMid - distance) sideDirection = trackDirection2reverse if n1 < elementsCountAroundMid: elementLength = elementLengthAroundOstiumMid elif distance < (vesselsSpanMid + halfInterVesselDistance + halfCircumference): position = vesselOstiumPositions[0] - angleRadians = (distance - (vesselsSpanMid + halfInterVesselDistance))/ostiumRadius + angleRadians = (distance - (vesselsSpanMid + halfInterVesselDistance)) / ostiumRadius w1 = -math.sin(angleRadians) w2 = -math.cos(angleRadians) - sideDirection = [ (w1*trackDirection1[c] + w2*trackDirection2[c]) for c in range(3) ] - elif distance < (2.0*vesselsSpanMid + halfInterVesselDistance + halfCircumference + interVesselDistance): - position = trackSurface.trackVector(centrePosition, trackDirection1, distance - (1.5*vesselsSpanMid + interVesselDistance + halfCircumference)) + sideDirection = [(w1 * trackDirection1[c] + w2 * trackDirection2[c]) for c in range(3)] + elif distance < (2.0 * vesselsSpanMid + halfInterVesselDistance + halfCircumference + interVesselDistance): + position = trackSurface.trackVector(centrePosition, trackDirection1, distance - + (1.5 * vesselsSpanMid + interVesselDistance + halfCircumference)) sideDirection = trackDirection2 if 0 <= (n1 - elementsCountAroundEnd - elementsCountAroundMid) < elementsCountAroundMid: elementLength = elementLengthAroundOstiumMid - elif distance < (2.0*vesselsSpanMid + halfInterVesselDistance + circumference + interVesselDistance): + elif distance < (2.0 * vesselsSpanMid + halfInterVesselDistance + circumference + interVesselDistance): position = vesselOstiumPositions[-1] - angleRadians = (distance - (2.0*vesselsSpanMid + halfInterVesselDistance + halfCircumference + interVesselDistance))/ostiumRadius + angleRadians = (distance - (2.0 * vesselsSpanMid + halfInterVesselDistance + halfCircumference + + interVesselDistance)) / ostiumRadius w1 = math.sin(angleRadians) w2 = math.cos(angleRadians) - sideDirection = [ (w1*trackDirection1[c] + w2*trackDirection2[c]) for c in range(3) ] + sideDirection = [(w1 * trackDirection1[c] + w2 * trackDirection2[c]) for c in range(3)] else: - position = trackSurface.trackVector(centrePosition, trackDirection1, 0.5*vesselsSpanMid + (circumference + 2.0*(vesselsSpanMid + interVesselDistance)) - distance) + position = \ + trackSurface.trackVector(centrePosition, trackDirection1, + 0.5 * vesselsSpanMid + (circumference + 2.0 * + (vesselsSpanMid + interVesselDistance)) - distance) sideDirection = trackDirection2reverse position = trackSurface.trackVector(position, sideDirection, ostiumRadius) oPositions.append(position) @@ -364,7 +382,7 @@ def generateOstiumMesh(region, options, trackSurface, centrePosition, axis1, sta pd2, pd1, pd3 = calculate_surface_axes(d1, d2, sideDirection) # get outer coordinates opx = px - opd1 = vector.setMagnitude([ -d for d in pd1 ], elementLengthAroundOstiumEnd) + opd1 = vector.setMagnitude([-d for d in pd1], elementLengthAroundOstiumEnd) opd2 = vector.setMagnitude(pd2, elementLengthAroundOstiumEnd) # smoothed later opd3 = vector.setMagnitude(pd3, ostiumWallThickness) @@ -389,14 +407,14 @@ def generateOstiumMesh(region, options, trackSurface, centrePosition, axis1, sta od3[n3].append([opd3[c] * ostiumWallThicknessProportions[n3] for c in range(3)]) distance += elementLength for n3 in range(elementsCountThroughWall + 1): - od1[n3] = interp.smoothCubicHermiteDerivativesLoop(ox[n3], od1[n3], fixAllDirections = True) + od1[n3] = interp.smoothCubicHermiteDerivativesLoop(ox[n3], od1[n3], fixAllDirections=True) - xx = [] + xx = [] xd1 = [] xd2 = [] xd3 = [] if (vesselWallThickness > 0.0) and (ostiumWallThickness > 0.0): - commonOstiumWallThickness = 2.0/(1.0/vesselWallThickness + 1.0/ostiumWallThickness) + commonOstiumWallThickness = 2.0 / (1.0 / vesselWallThickness + 1.0 / ostiumWallThickness) commonOstiumWallThicknessProportions = [] commonOstiumWallTotalXi3 = 0.0 commonOstiumWallThicknessXi3List = [0.0] @@ -413,14 +431,14 @@ def generateOstiumMesh(region, options, trackSurface, centrePosition, axis1, sta # coordinates across common ostium, between vessels nodesCountFreeEnd = elementsCountsAroundVessels[0] + 1 - elementsCountAcross - oinc = 0 if (vesselsCount <= 2) else elementsCountAroundMid//(vesselsCount - 2) + oinc = 0 if (vesselsCount <= 2) else elementsCountAroundMid // (vesselsCount - 2) for iv in range(vesselsCount - 1): - xx .append([ None for n3 in range(elementsCountThroughWall + 1)]) - xd1.append([ None for n3 in range(elementsCountThroughWall + 1)]) - xd2.append([ None for n3 in range(elementsCountThroughWall + 1)]) - xd3.append([ None for n3 in range(elementsCountThroughWall + 1)]) - oa = elementsCountAroundMid - iv*oinc - ob = elementsCountAroundMid + nodesCountFreeEnd - 1 + iv*oinc + xx .append([None for n3 in range(elementsCountThroughWall + 1)]) + xd1.append([None for n3 in range(elementsCountThroughWall + 1)]) + xd2.append([None for n3 in range(elementsCountThroughWall + 1)]) + xd3.append([None for n3 in range(elementsCountThroughWall + 1)]) + oa = elementsCountAroundMid - iv * oinc + ob = elementsCountAroundMid + nodesCountFreeEnd - 1 + iv * oinc nx = [ox[elementsCountThroughWall][oa], ox[elementsCountThroughWall][ob]] nd1 = [[-d for d in od1[elementsCountThroughWall][oa]], od1[elementsCountThroughWall][ob]] nd2 = [[-d for d in od2[elementsCountThroughWall][oa]], od2[elementsCountThroughWall][ob]] @@ -430,28 +448,33 @@ def generateOstiumMesh(region, options, trackSurface, centrePosition, axis1, sta if vesselsCount == 2: position = centrePosition else: - position = trackSurface.trackVector(centrePosition, trackDirection1, (iv/(vesselsCount - 2) - 0.5)*vesselsSpanMid) - mx, d1, d2 = trackSurface.evaluateCoordinates(position, derivatives = True) + position = trackSurface.trackVector(centrePosition, trackDirection1, + (iv / (vesselsCount - 2) - 0.5) * vesselsSpanMid) + mx, d1, d2 = trackSurface.evaluateCoordinates(position, derivatives=True) md1, md2, md3 = calculate_surface_axes(d1, d2, trackDirection1) - nx .insert(1, [ (mx[c] + interVesselHeight*md3[c]) for c in range(3) ]) - nd1.insert(1, vector.setMagnitude(md1, elementLengthAroundOstiumMid if (0 < iv < (vesselsCount - 2)) else elementLengthAroundOstiumEnd)) + nx .insert(1, [(mx[c] + interVesselHeight * md3[c]) for c in range(3)]) + nd1.insert(1, vector.setMagnitude(md1, elementLengthAroundOstiumMid if (0 < iv < (vesselsCount - 2)) else + elementLengthAroundOstiumEnd)) nd2.insert(1, vector.setMagnitude(md2, ostiumRadius)) - nd2 = interp.smoothCubicHermiteDerivativesLine(nx, nd2, fixAllDirections = True) + nd2 = interp.smoothCubicHermiteDerivativesLine(nx, nd2, fixAllDirections=True) px, pd2, pe, pxi = interp.sampleCubicHermiteCurves(nx, nd2, elementsCountAcross)[0:4] pd1 = interp.interpolateSampleLinear(nd1, pe, pxi) - pd3 = [ vector.setMagnitude(vector.crossproduct3(pd1[n2], pd2[n2]), commonOstiumWallThickness) for n2 in range(elementsCountAcross + 1) ] + pd3 = [vector.setMagnitude(vector.crossproduct3(pd1[n2], pd2[n2]), commonOstiumWallThickness) + for n2 in range(elementsCountAcross + 1)] for n3 in range(elementsCountThroughWall + 1): xi3 = 1 - commonOstiumWallThicknessXi3List[n3] lx = [([(px[n2][c] - xi3 * pd3[n2][c]) for c in range(3)]) for n2 in range(elementsCountAcross + 1)] ld2 = interp.smoothCubicHermiteDerivativesLine(lx, pd2, fixAllDirections=True) - xx [iv][n3] = lx [1:elementsCountAcross] + xx[iv][n3] = lx[1:elementsCountAcross] xd1[iv][n3] = copy.deepcopy(pd1[1:elementsCountAcross]) # to be smoothed later xd2[iv][n3] = ld2[1:elementsCountAcross] # set smoothed d2 on ostium circumference od2[n3][oa] = [-d for d in ld2[0]] od2[n3][ob] = ld2[-1] if useCubicHermiteThroughOstiumWall: - pd3Element = [vector.setMagnitude(vector.crossproduct3(pd1[n2], pd2[n2]), commonOstiumWallThickness * commonOstiumWallThicknessProportions[n3]) for n2 in range(elementsCountAcross + 1)] + pd3Element = [vector.setMagnitude(vector.crossproduct3(pd1[n2], pd2[n2]), + commonOstiumWallThickness * commonOstiumWallThicknessProportions[n3]) + for n2 in range(elementsCountAcross + 1)] xd3[iv][n3] = copy.deepcopy(pd3Element[1:elementsCountAcross]) # get positions of vessel end centres and rings @@ -465,14 +488,15 @@ def generateOstiumMesh(region, options, trackSurface, centrePosition, axis1, sta vod3 = [] for v in range(vesselsCount): elementsCountAroundVessel = elementsCountsAroundVessels[v] - radiansPerElementVessel = 2.0*math.pi/elementsCountAroundVessel + radiansPerElementVessel = 2.0 * math.pi / elementsCountAroundVessel useVesselAngleRadians = vesselAngle1Radians if vesselsCount > 1: - useVesselAngleRadians += (v/(vesselsCount - 1) - 0.5)*vesselAngle1SpreadRadians - vx, vd1, vd2, vd3 = getCircleProjectionAxes(ocx[v], ocd1[v], ocd2[v], ocd3[v], ostiumLength, useVesselAngleRadians, vesselAngle2Radians) - vd1 = [ vesselOuterRadius*d for d in vd1 ] - vd2 = [ -vesselOuterRadius*d for d in vd2 ] - vd3 = [ -vesselEndDerivative*d for d in vd3 ] + useVesselAngleRadians += (v / (vesselsCount - 1) - 0.5) * vesselAngle1SpreadRadians + vx, vd1, vd2, vd3 = getCircleProjectionAxes(ocx[v], ocd1[v], ocd2[v], ocd3[v], ostiumLength, + useVesselAngleRadians, vesselAngle2Radians) + vd1 = [vesselOuterRadius * d for d in vd1] + vd2 = [-vesselOuterRadius * d for d in vd2] + vd3 = [-vesselEndDerivative * d for d in vd3] vcx.append(vx) vcd1.append(vd1) vcd2.append(vd2) @@ -486,35 +510,37 @@ def generateOstiumMesh(region, options, trackSurface, centrePosition, axis1, sta vAxis1 = vector.setMagnitude(vd1, radius) vAxis2 = vector.setMagnitude(vd2, radius) if vesselsCount == 1: - startRadians = 0.5*math.pi + startRadians = 0.5 * math.pi else: - startRadians = 0.5*radiansPerElementVessel*elementsCountAcross + startRadians = 0.5 * radiansPerElementVessel * elementsCountAcross if v == (vesselsCount - 1): startRadians -= math.pi px, pd1 = createCirclePoints(vx, vAxis1, vAxis2, elementsCountAroundVessel, startRadians) - vox [-1].append(px) + vox[-1].append(px) vod1[-1].append(pd1) - vod2[-1].append([ vd3 ]*elementsCountAroundVessel) + vod2[-1].append([vd3] * elementsCountAroundVessel) if useCubicHermiteThroughVesselWall: - vod3[-1].append([ vector.setMagnitude(vector.crossproduct3(d1, vd3), vesselWallThickness * vesselWallThicknessProportions[n3]) for d1 in pd1 ]) + vod3[-1].append([vector.setMagnitude(vector.crossproduct3(d1, vd3), + vesselWallThickness * vesselWallThicknessProportions[n3]) + for d1 in pd1]) # calculate common ostium vessel node derivatives map - mvPointsx = [ None ]*vesselsCount - mvPointsd1 = [ None ]*vesselsCount - mvPointsd2 = [ None ]*vesselsCount - mvPointsd3 = [ None ]*vesselsCount - mvDerivativesMap = [ None ]*vesselsCount - mvMeanCount = [ None ]*vesselsCount # stores 1 if first reference to common point between vessels, 2 if second. Otherwise 0. + mvPointsx = [None] * vesselsCount + mvPointsd1 = [None] * vesselsCount + mvPointsd2 = [None] * vesselsCount + mvPointsd3 = [None] * vesselsCount + mvDerivativesMap = [None] * vesselsCount + mvMeanCount = [None] * vesselsCount # stores 1 if first reference to common point between vessels, 2 if second. Otherwise 0. for v in range(vesselsCount): if vesselsCount == 1: mvPointsx[v], mvPointsd1[v], mvPointsd2[v], mvPointsd3[v], mvDerivativesMap[v] = \ ox, od1, od2, od3 if useCubicHermiteThroughOstiumWall else None, None - mvMeanCount[v] = [ 0 ]*elementsCountsAroundVessels[v] + mvMeanCount[v] = [0] * elementsCountsAroundVessels[v] else: iv = max(0, v - 1) - oa = elementsCountAroundMid - iv*oinc - ob = elementsCountAroundMid + nodesCountFreeEnd - 1 + iv*oinc - mvPointsx [v] = [] + oa = elementsCountAroundMid - iv * oinc + ob = elementsCountAroundMid + nodesCountFreeEnd - 1 + iv * oinc + mvPointsx[v] = [] mvPointsd1[v] = [] mvPointsd2[v] = [] mvPointsd3[v] = [] if useCubicHermiteThroughOstiumWall else None @@ -522,118 +548,122 @@ def generateOstiumMesh(region, options, trackSurface, centrePosition, axis1, sta for n3 in range(elementsCountThroughWall + 1): mvPointsd1[v].append([]) mvPointsd2[v].append([]) - mvPointsx [v].append([]) + mvPointsx[v].append([]) if useCubicHermiteThroughOstiumWall: mvPointsd3[v].append([]) mvDerivativesMap[v].append([]) if v == 0: # first end vessel mvPointsd1[v][n3] += od1[n3][oa:ob + 1] mvPointsd2[v][n3] += od2[n3][oa:ob + 1] - mvPointsx [v][n3] += ox [n3][oa:ob + 1] + mvPointsx[v][n3] += ox[n3][oa:ob + 1] if useCubicHermiteThroughOstiumWall: mvPointsd3[v][n3] += od3[n3][oa:ob + 1] - mvDerivativesMap[v][n3].append( ( (0, 1, 0), (-1, 1, 0), None, (1, 0, 0) ) ) + mvDerivativesMap[v][n3].append(((0, 1, 0), (-1, 1, 0), None, (1, 0, 0))) for i in range(nodesCountFreeEnd - 2): - mvDerivativesMap[v][n3].append( ( None, None, None ) ) - mvDerivativesMap[v][n3].append( ( (1, 0, 0), (1, 1, 0), None, (0, -1, 0) ) ) - mvPointsx [v][n3] += reversed(xx [iv][n3]) + mvDerivativesMap[v][n3].append((None, None, None)) + mvDerivativesMap[v][n3].append(((1, 0, 0), (1, 1, 0), None, (0, -1, 0))) + mvPointsx[v][n3] += reversed(xx[iv][n3]) mvPointsd1[v][n3] += reversed(xd1[iv][n3]) mvPointsd2[v][n3] += reversed(xd2[iv][n3]) if useCubicHermiteThroughOstiumWall: mvPointsd3[v][n3] += reversed(xd3[iv][n3]) for i in range(elementsCountAcross - 1): - mvDerivativesMap[v][n3].append( ( (0, -1, 0), (1, 0, 0), None ) ) + mvDerivativesMap[v][n3].append(((0, -1, 0), (1, 0, 0), None)) if n3 == 0: - mvMeanCount[v] = [ 1 ] + [ 0 ]*(nodesCountFreeEnd - 2) + [ 1 ]*elementsCountAcross + mvMeanCount[v] = [1] + [0] * (nodesCountFreeEnd - 2) + [1] * elementsCountAcross elif v < (vesselsCount - 1): # middle vessels # left: - mvPointsx [v][n3] += ox [n3][oa - oinc:oa + 1] + mvPointsx[v][n3] += ox[n3][oa - oinc:oa + 1] mvPointsd1[v][n3] += od1[n3][oa - oinc:oa + 1] mvPointsd2[v][n3] += od2[n3][oa - oinc:oa + 1] if useCubicHermiteThroughOstiumWall: mvPointsd3[v][n3] += od3[n3][oa - oinc:oa + 1] - mvDerivativesMap[v][n3].append( ( (0, 1, 0), (-1, 1, 0), None, (1, 0, 0) ) ) + mvDerivativesMap[v][n3].append(((0, 1, 0), (-1, 1, 0), None, (1, 0, 0))) for i in range(oinc - 1): - mvDerivativesMap[v][n3].append( ( None, None, None ) ) - mvDerivativesMap[v][n3].append( ( (1, 0, 0), (1, 1, 0), None, (0, -1, 0) ) ) + mvDerivativesMap[v][n3].append((None, None, None)) + mvDerivativesMap[v][n3].append(((1, 0, 0), (1, 1, 0), None, (0, -1, 0))) # across - mvPointsx [v][n3] += xx [iv][n3] + mvPointsx[v][n3] += xx[iv][n3] mvPointsd1[v][n3] += xd1[iv][n3] mvPointsd2[v][n3] += xd2[iv][n3] if useCubicHermiteThroughOstiumWall: mvPointsd3[v][n3] += xd3[iv][n3] for i in range(elementsCountAcross - 1): - mvDerivativesMap[v][n3].append( ( (0, 1, 0), (-1, 0, 0), None ) ) + mvDerivativesMap[v][n3].append(((0, 1, 0), (-1, 0, 0), None)) # right - mvPointsx [v][n3] += ox [n3][ob:ob + oinc + 1] + mvPointsx[v][n3] += ox[n3][ob:ob + oinc + 1] mvPointsd1[v][n3] += od1[n3][ob:ob + oinc + 1] mvPointsd2[v][n3] += od2[n3][ob:ob + oinc + 1] if useCubicHermiteThroughOstiumWall: mvPointsd3[v][n3] += od3[n3][ob:ob + oinc + 1] - mvDerivativesMap[v][n3].append( ( (0, 1, 0), (-1, 1, 0), None, (1, 0, 0) ) ) + mvDerivativesMap[v][n3].append(((0, 1, 0), (-1, 1, 0), None, (1, 0, 0))) for i in range(oinc - 1): - mvDerivativesMap[v][n3].append( ( None, None, None ) ) - mvDerivativesMap[v][n3].append( ( (1, 0, 0), (1, 1, 0), None, (0, -1, 0) ) ) + mvDerivativesMap[v][n3].append((None, None, None)) + mvDerivativesMap[v][n3].append(((1, 0, 0), (1, 1, 0), None, (0, -1, 0))) # across reverse - mvPointsx [v][n3] += reversed(xx [iv + 1][n3]) + mvPointsx[v][n3] += reversed(xx[iv + 1][n3]) mvPointsd1[v][n3] += reversed(xd1[iv + 1][n3]) mvPointsd2[v][n3] += reversed(xd2[iv + 1][n3]) if useCubicHermiteThroughOstiumWall: mvPointsd3[v][n3] += reversed(xd3[iv + 1][n3]) for i in range(elementsCountAcross - 1): - mvDerivativesMap[v][n3].append( ( (0, -1, 0), (1, 0, 0), None ) ) + mvDerivativesMap[v][n3].append(((0, -1, 0), (1, 0, 0), None)) if n3 == 0: - mvMeanCount[v] = [ 1 ] + [ 0 ]*(oinc - 1) + [ 2 ]*(elementsCountAcross + 1) + [ 0 ]*(oinc - 1) + [ 1 ]*elementsCountAcross + mvMeanCount[v] = [1] + [0] * (oinc - 1) + [2] * (elementsCountAcross + 1) + [0] * (oinc - 1) + \ + [1] * elementsCountAcross else: # last end vessel - mvPointsx [v][n3] += ox [n3][ob:] + [ ox [n3][0] ] - mvPointsd1[v][n3] += od1[n3][ob:] + [ od1[n3][0] ] - mvPointsd2[v][n3] += od2[n3][ob:] + [ od2[n3][0] ] + mvPointsx[v][n3] += ox[n3][ob:] + [ox[n3][0]] + mvPointsd1[v][n3] += od1[n3][ob:] + [od1[n3][0]] + mvPointsd2[v][n3] += od2[n3][ob:] + [od2[n3][0]] if useCubicHermiteThroughOstiumWall: - mvPointsd3[v][n3] += od3[n3][ob:] + [ od3[n3][0] ] - mvDerivativesMap[v][n3].append( ( (0, 1, 0), (-1, 1, 0), None, (1, 0, 0) ) ) + mvPointsd3[v][n3] += od3[n3][ob:] + [od3[n3][0]] + mvDerivativesMap[v][n3].append(((0, 1, 0), (-1, 1, 0), None, (1, 0, 0))) for i in range(nodesCountFreeEnd - 2): - mvDerivativesMap[v][n3].append( ( None, None, None ) ) - mvDerivativesMap[v][n3].append( ( (1, 0, 0), (1, 1, 0), None, (0, -1, 0) ) ) - mvPointsx [v][n3] += xx [iv][n3] + mvDerivativesMap[v][n3].append((None, None, None)) + mvDerivativesMap[v][n3].append(((1, 0, 0), (1, 1, 0), None, (0, -1, 0))) + mvPointsx[v][n3] += xx[iv][n3] mvPointsd1[v][n3] += xd1[iv][n3] mvPointsd2[v][n3] += xd2[iv][n3] if useCubicHermiteThroughOstiumWall: mvPointsd3[v][n3] += xd3[iv][n3] for i in range(elementsCountAcross - 1): - mvDerivativesMap[v][n3].append( ( (0, 1, 0), (-1, 0, 0), None ) ) + mvDerivativesMap[v][n3].append(((0, 1, 0), (-1, 0, 0), None)) if n3 == 0: - mvMeanCount[v] = [ 2 ] + [ 0 ]*(nodesCountFreeEnd - 2) + [ 2 ]*elementsCountAcross + mvMeanCount[v] = [2] + [0] * (nodesCountFreeEnd - 2) + [2] * elementsCountAcross # calculate derivative 2 around free sides of inlets to fit vessel derivatives for v in range(vesselsCount): - for n3 in [elementsCountThroughWall]: # was range(2), now using curvature for inside: - #print('v',v,'n3',n3,'elementsAround',elementsCountsAroundVessels[v]) - #print('mvPointsx [v][n3]', mvPointsx [v][n3]) - #print('mvPointsd1[v][n3]', mvPointsd1[v][n3]) - #print('mvPointsd2[v][n3]', mvPointsd2[v][n3]) - #print('mvDerivativesMap[v][n3]', mvDerivativesMap[v][n3]) + for n3 in [elementsCountThroughWall]: # was range(2), now using curvature for inside: + # print('v',v,'n3',n3,'elementsAround',elementsCountsAroundVessels[v]) + # print('mvPointsx [v][n3]', mvPointsx [v][n3]) + # print('mvPointsd1[v][n3]', mvPointsd1[v][n3]) + # print('mvPointsd2[v][n3]', mvPointsd2[v][n3]) + # print('mvDerivativesMap[v][n3]', mvDerivativesMap[v][n3]) for n1 in range(elementsCountsAroundVessels[v]): - d2Map = mvDerivativesMap[v][n3][n1][1] if (mvDerivativesMap[v] and mvDerivativesMap[v][n3][n1]) else None + d2Map = mvDerivativesMap[v][n3][n1][1] if (mvDerivativesMap[v] and mvDerivativesMap[v][n3][n1]) \ + else None sf1 = d2Map[0] if d2Map else 0.0 sf2 = d2Map[1] if d2Map else 1.0 - nx = [ vox[v][n3][n1], mvPointsx[v][n3][n1] ] - nd2 = [ [ d*elementsCountAlong for d in vod2[v][n3][n1] ], [ (sf1*mvPointsd1[v][n3][n1][c] + sf2*mvPointsd2[v][n3][n1][c]) for c in range(3) ] ] - nd2f = interp.smoothCubicHermiteDerivativesLine(nx, nd2, fixStartDerivative = True, fixEndDirection = True) - ndf = [ d/elementsCountAlong for d in nd2f[1] ] + nx = [vox[v][n3][n1], mvPointsx[v][n3][n1]] + nd2 = [[d * elementsCountAlong for d in vod2[v][n3][n1]], + [(sf1 * mvPointsd1[v][n3][n1][c] + sf2 * mvPointsd2[v][n3][n1][c]) for c in range(3)]] + nd2f = interp.smoothCubicHermiteDerivativesLine(nx, nd2, fixStartDerivative=True, fixEndDirection=True) + ndf = [d / elementsCountAlong for d in nd2f[1]] # assign components to set original values: if sf1 == 0: for c in range(3): - mvPointsd2[v][n3][n1][c] = sf2*ndf[c] + mvPointsd2[v][n3][n1][c] = sf2 * ndf[c] elif sf2 == 0: if mvMeanCount[v][n1] < 2: for c in range(3): - mvPointsd1[v][n3][n1][c] = sf1*ndf[c] + mvPointsd1[v][n3][n1][c] = sf1 * ndf[c] else: # take mean of values from this and last vessel for c in range(3): - mvPointsd1[v][n3][n1][c] = 0.5*(mvPointsd1[v][n3][n1][c] + sf1*ndf[c]) + mvPointsd1[v][n3][n1][c] = 0.5 * (mvPointsd1[v][n3][n1][c] + sf1 * ndf[c]) else: - #print('v', v, 'n3', n3, 'n1', n1, ':', vector.magnitude(ndf), 'vs.', vector.magnitude(nd2[1]), 'd2Map', d2Map) + # print('v', v, 'n3', n3, 'n1', n1, ':', vector.magnitude(ndf), 'vs.', vector.magnitude(nd2[1]), + # 'd2Map', d2Map) pass # calculate inner d2 derivatives around ostium from outer using track surface curvature @@ -660,7 +690,7 @@ def generateOstiumMesh(region, options, trackSurface, centrePosition, axis1, sta # assign components to set in all lists: for c in range(3): od2[n3][n1][c] = newd2[c] / factor - #for n in [ 0, 1, 2 ]: + # for n in [ 0, 1, 2 ]: # node = nodes.createNode(nodeIdentifier, nodetemplateLinearS3) # cache.setNode(node) # coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_VALUE, 1, tx [n]) @@ -694,9 +724,10 @@ def generateOstiumMesh(region, options, trackSurface, centrePosition, axis1, sta for n3 in range(elementsCountThroughWall + 1): oNodeId.append([]) for n1 in range(elementsCountAroundOstium): - node = nodes.createNode(nodeIdentifier, nodetemplate if useCubicHermiteThroughOstiumWall else nodetemplateLinearS3) + node = nodes.createNode(nodeIdentifier, + nodetemplate if useCubicHermiteThroughOstiumWall else nodetemplateLinearS3) cache.setNode(node) - coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_VALUE, 1, ox [n3][n1]) + coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_VALUE, 1, ox[n3][n1]) coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS1, 1, od1[n3][n1]) coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS2, 1, od2[n3][n1]) if useCubicHermiteThroughOstiumWall: @@ -710,9 +741,10 @@ def generateOstiumMesh(region, options, trackSurface, centrePosition, axis1, sta for n3 in range(elementsCountThroughWall + 1): xNodeId[iv].append([]) for n2 in range(elementsCountAcross - 1): - node = nodes.createNode(nodeIdentifier, nodetemplate if useCubicHermiteThroughOstiumWall else nodetemplateLinearS3) + node = nodes.createNode(nodeIdentifier, + nodetemplate if useCubicHermiteThroughOstiumWall else nodetemplateLinearS3) cache.setNode(node) - coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_VALUE, 1, xx [iv][n3][n2]) + coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_VALUE, 1, xx[iv][n3][n2]) coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS1, 1, xd1[iv][n3][n2]) coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS2, 1, xd2[iv][n3][n2]) if useCubicHermiteThroughOstiumWall: @@ -720,7 +752,7 @@ def generateOstiumMesh(region, options, trackSurface, centrePosition, axis1, sta xNodeId[iv][n3].append(nodeIdentifier) nodeIdentifier += 1 - #for v in range(vesselsCount): + # for v in range(vesselsCount): # node = nodes.createNode(nodeIdentifier, nodetemplate) # cache.setNode(node) # coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_VALUE, 1, vcx [v]) @@ -730,7 +762,8 @@ def generateOstiumMesh(region, options, trackSurface, centrePosition, axis1, sta # nodeIdentifier += 1 # for n3 in range(elementsCountThroughWall + 1): # for n1 in range(elementsCountsAroundVessels[v]): - # node = nodes.createNode(nodeIdentifier, nodetemplate if useCubicHermiteThroughVesselWall else nodetemplateLinearS3) + # node = nodes.createNode(nodeIdentifier, + # nodetemplate if useCubicHermiteThroughVesselWall else nodetemplateLinearS3) # cache.setNode(node) # coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_VALUE, 1, vox [v][n3][n1]) # coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS1, 1, vod1[v][n3][n1]) @@ -741,37 +774,40 @@ def generateOstiumMesh(region, options, trackSurface, centrePosition, axis1, sta # nodeIdentifier += 1 # get identifiers of nodes around each vessel at ostium end - mvNodeId = [ None ]*vesselsCount + mvNodeId = [None] * vesselsCount for v in range(vesselsCount): if vesselsCount == 1: mvNodeId[v] = oNodeId else: iv = max(0, v - 1) - mvNodeId[v] = [ None for n3 in range(elementsCountThroughWall + 1)] - oa = elementsCountAroundMid - iv*oinc - ob = elementsCountAroundMid + nodesCountFreeEnd - 1 + iv*oinc + mvNodeId[v] = [None for n3 in range(elementsCountThroughWall + 1)] + oa = elementsCountAroundMid - iv * oinc + ob = elementsCountAroundMid + nodesCountFreeEnd - 1 + iv * oinc for n3 in range(elementsCountThroughWall + 1): if v == 0: # first end vessel - mvNodeId[v][n3] = oNodeId[n3][oa:ob + 1] + (list(reversed(xNodeId[iv][n3])) if (v == 0) else xNodeId[iv][n3]) + mvNodeId[v][n3] = oNodeId[n3][oa:ob + 1] + \ + (list(reversed(xNodeId[iv][n3])) if (v == 0) else xNodeId[iv][n3]) elif v == (vesselsCount - 1): # last end vessels - mvNodeId[v][n3] = oNodeId[n3][ob:] + [ oNodeId[n3][0] ] + (list(reversed(xNodeId[iv][n3])) if (v == 0) else xNodeId[iv][n3]) + mvNodeId[v][n3] = oNodeId[n3][ob:] + [oNodeId[n3][0]] + \ + (list(reversed(xNodeId[iv][n3])) if (v == 0) else xNodeId[iv][n3]) else: # mid vessels - mvNodeId[v][n3] = oNodeId[n3][oa - oinc:oa + 1] + xNodeId[iv][n3] + oNodeId[n3][ob:ob + oinc + 1] + list(reversed(xNodeId[iv + 1][n3])) + mvNodeId[v][n3] = oNodeId[n3][oa - oinc:oa + 1] + xNodeId[iv][n3] + \ + oNodeId[n3][ob:ob + oinc + 1] + list(reversed(xNodeId[iv + 1][n3])) ################# # Create elements ################# - tricubichermite = eftfactory_tricubichermite(mesh, useCrossDerivatives) - #tricubicHermiteBasis = fm.createElementbasis(3, Elementbasis.FUNCTION_TYPE_CUBIC_HERMITE) - - #eft = tricubichermite.createEftBasic() - #elementtemplate = mesh.createElementtemplate() - #elementtemplate.setElementShapeType(Element.SHAPE_TYPE_CUBE) - #elementtemplate.defineField(coordinates, -1, eft) - - #elementtemplateX = mesh.createElementtemplate() - #elementtemplateX.setElementShapeType(Element.SHAPE_TYPE_CUBE) + # tricubichermite = eftfactory_tricubichermite(mesh, useCrossDerivatives) + # tricubicHermiteBasis = fm.createElementbasis(3, Elementbasis.FUNCTION_TYPE_CUBIC_HERMITE) + # + # eft = tricubichermite.createEftBasic() + # elementtemplate = mesh.createElementtemplate() + # elementtemplate.setElementShapeType(Element.SHAPE_TYPE_CUBE) + # elementtemplate.defineField(coordinates, -1, eft) + # + # elementtemplateX = mesh.createElementtemplate() + # elementtemplateX.setElementShapeType(Element.SHAPE_TYPE_CUBE) for v in range(vesselsCount): if vesselMeshGroups or ostiumMeshGroups: @@ -786,18 +822,19 @@ def generateOstiumMesh(region, options, trackSurface, centrePosition, axis1, sta endPointsx, endPointsd1, endPointsd2, endPointsd3, endNodeId, endDerivativesMap = \ vox[v], vod1[v], vod2[v], vod3[v] if useCubicHermiteThroughVesselWall else None, None, None # reverse order of nodes around: - for px in [ startPointsx, startPointsd1, startPointsd2, startPointsd3, startNodeId, startDerivativesMap, \ - endPointsx, endPointsd1, endPointsd2, endPointsd3, endNodeId, endDerivativesMap ]: + for px in [startPointsx, startPointsd1, startPointsd2, startPointsd3, startNodeId, startDerivativesMap, + endPointsx, endPointsd1, endPointsd2, endPointsd3, endNodeId, endDerivativesMap]: if px: for n3 in range(elementsCountThroughWall + 1): - px[n3] = [ px[n3][0] ] + px[n3][len(px[n3]) - 1:0:-1] + px[n3] = [px[n3][0]] + px[n3][len(px[n3]) - 1:0:-1] if vesselsCount > 1: # must switch in and out xi1 maps around corners in startDerivativesMap for n3 in range(elementsCountThroughWall + 1): for n1 in range(elementsCountsAroundVessels[v]): derivativesMap = startDerivativesMap[n3][n1] if len(derivativesMap) == 4: - startDerivativesMap[n3][n1] = derivativesMap[3], derivativesMap[1], derivativesMap[2], derivativesMap[0] + startDerivativesMap[n3][n1] = derivativesMap[3], derivativesMap[1], \ + derivativesMap[2], derivativesMap[0] if ostiumMeshGroups: rowMeshGroups[0] += ostiumMeshGroups else: @@ -808,22 +845,20 @@ def generateOstiumMesh(region, options, trackSurface, centrePosition, axis1, sta if ostiumMeshGroups: rowMeshGroups[-1] += ostiumMeshGroups - #print('endPointsx ', endPointsx ) - #print('endPointsd1', endPointsd1) - #print('endPointsd2', endPointsd2) - #print('endPointsd3', endPointsd3) - #print('endNodeId', endNodeId) - #print('endDerivativesMap', endDerivativesMap) + # print('endPointsx ', endPointsx ) + # print('endPointsd1', endPointsd1) + # print('endPointsd2', endPointsd2) + # print('endPointsd3', endPointsd3) + # print('endNodeId', endNodeId) + # print('endDerivativesMap', endDerivativesMap) nodeIdentifier, elementIdentifier = createAnnulusMesh3d( nodes, mesh, nodeIdentifier, elementIdentifier, startPointsx, startPointsd1, startPointsd2, startPointsd3, startNodeId, startDerivativesMap, endPointsx, endPointsd1, endPointsd2, endPointsd3, endNodeId, endDerivativesMap, - forceMidLinearXi3 = not useCubicHermiteThroughVesselWall, - maxStartThickness = vesselWallThickness, - maxEndThickness = vesselWallThickness, - elementsCountRadial = elementsCountAlong, - meshGroups = rowMeshGroups, wallAnnotationGroups = wallAnnotationGroups) + forceMidLinearXi3=not useCubicHermiteThroughVesselWall, + maxStartThickness=vesselWallThickness, maxEndThickness=vesselWallThickness, + elementsCountRadial=elementsCountAlong, meshGroups=rowMeshGroups, wallAnnotationGroups=wallAnnotationGroups) fm.endChange() return nodeIdentifier, elementIdentifier, (ox, od1, od2, od3, oNodeId, oPositions) diff --git a/src/scaffoldmaker/meshtypes/meshtype_3d_stomach1.py b/src/scaffoldmaker/meshtypes/meshtype_3d_stomach1.py index bdb1c004..9acc17a5 100644 --- a/src/scaffoldmaker/meshtypes/meshtype_3d_stomach1.py +++ b/src/scaffoldmaker/meshtypes/meshtype_3d_stomach1.py @@ -432,10 +432,10 @@ def getOptionScaffoldTypeParameterSetNames(cls, optionName, scaffoldType): @classmethod def getOptionScaffoldPackage(cls, optionName, scaffoldType, parameterSetName=None): - ''' + """ :param parameterSetName: Name of valid parameter set for option Scaffold, or None for default. :return: ScaffoldPackage. - ''' + """ if parameterSetName: assert parameterSetName in cls.getOptionScaffoldTypeParameterSetNames(optionName, scaffoldType), \ 'Invalid parameter set ' + str(parameterSetName) + ' for scaffold ' + str(scaffoldType.getName()) + \ @@ -482,9 +482,9 @@ def checkOptions(cls, options): @classmethod def updateSubScaffoldOptions(cls, options): - ''' + """ Update ostium sub-scaffold options which depend on parent options. - ''' + """ wallThickness = options['Wall thickness'] ostiumOptions = options['Gastro-esophagal junction'] ostiumSettings = ostiumOptions.getScaffoldSettings() @@ -568,7 +568,8 @@ def generateBaseMesh(cls, region, options): # Extract length of each group along stomach from central path arcLengthOfGroupsAlong = [] - stomachTermsAlong = [None, 'fundus of stomach', 'body of stomach', 'pyloric antrum', 'pyloric canal', 'duodenum'] + stomachTermsAlong = [None, 'fundus of stomach', 'body of stomach', + 'pyloric antrum', 'pyloric canal', 'duodenum'] for i in range(len(stomachTermsAlong)): tmpRegion = region.createRegion() centralPath.generate(tmpRegion) @@ -773,7 +774,8 @@ def generateBaseMesh(cls, region, options): esophagusMucosaGroup = AnnotationGroup(region, get_stomach_term("esophagus mucosa")) esophagusSubmucosaGroup = AnnotationGroup(region, get_stomach_term("submucosa of esophagus")) esophagusCircularGroup = AnnotationGroup(region, get_stomach_term("esophagus smooth muscle circular layer")) - esophagusLongitudinalGroup = AnnotationGroup(region, get_stomach_term("esophagus smooth muscle longitudinal layer")) + esophagusLongitudinalGroup = AnnotationGroup(region, + get_stomach_term("esophagus smooth muscle longitudinal layer")) ostiumWallAnnotationGroups = [[esophagusMucosaGroup, mucosaGroup], [esophagusSubmucosaGroup, submucosaGroup], @@ -788,7 +790,7 @@ def generateBaseMesh(cls, region, options): nodeIdentifier, elementIdentifier, vesselMeshGroups=[[stomachMeshGroup, esophagusMeshGroup]], ostiumMeshGroups=[stomachMeshGroup, esophagogastricJunctionMeshGroup], - wallAnnotationGroups= ostiumWallAnnotationGroups) + wallAnnotationGroups=ostiumWallAnnotationGroups) stomachStartNode = nextNodeIdentifier stomachStartElement = nextElementIdentifier @@ -796,7 +798,8 @@ def generateBaseMesh(cls, region, options): elementIdentifier = nextElementIdentifier fundusEndPosition = trackSurfaceStomach.createPositionProportion(0.0, fundusEndPositionAlongFactor) - xFundusEnd, d1FundusEnd, d2FundusEnd = trackSurfaceStomach.evaluateCoordinates(fundusEndPosition, derivatives=True) + xFundusEnd, d1FundusEnd, d2FundusEnd = trackSurfaceStomach.evaluateCoordinates(fundusEndPosition, + derivatives=True) elementsAlongFundus = elementsAroundQuarterEso + (0 if limitingRidge else 1) # From fundus end to duodenum @@ -843,7 +846,8 @@ def generateBaseMesh(cls, region, options): xAlongGCEsoToFundusEnd, d2AlongGCEsoToFundusEnd = \ interp.sampleCubicHermiteCurvesSmooth(xAlongUpFundus, d2AlongUpFundus, elementsAlongGCFromEsoToFundusEnd, - derivativeMagnitudeStart=vector.magnitude(d2AlongUpFundus[0]))[0:2] + derivativeMagnitudeStart=vector.magnitude( + d2AlongUpFundus[0]))[0:2] # Sample from limiting ridge to duodenum xAlongDownFundus[0] = xAlongGCEsoToFundusEnd[-1] @@ -955,7 +959,8 @@ def generateBaseMesh(cls, region, options): getSmoothedSampledPointsOnTrackSurface(trackSurfaceStomach, 0.0, GCProportion2, endProportion1, endProportion2, elementsAroundHalfDuod + 1, startDerivative=d1GC, endDerivative=d1EndOstium, - endDerivativeMagnitude=cardiaDerivativeFactor * vector.magnitude(d2))[0] + endDerivativeMagnitude=cardiaDerivativeFactor * + vector.magnitude(d2))[0] # Second half ostiumIdx2 = -n2 startPosition = o1_Positions[ostiumIdx2] @@ -983,11 +988,14 @@ def generateBaseMesh(cls, region, options): closestIdxOnCentralPath = interp.getNearestPointIndex(sx, xStart) if 0 < closestIdxOnCentralPath < len(sx) - 1: # Check if xStart is closer to upstream or downstream of closestIdx - xOnGCPrevElem = [sx[closestIdxOnCentralPath - 1][c] + sd2[closestIdxOnCentralPath - 1][c] for c in range(3)] + xOnGCPrevElem = [sx[closestIdxOnCentralPath - 1][c] + sd2[closestIdxOnCentralPath - 1][c] for c in + range(3)] distBetweenXOnGCPrevElem = vector.magnitude([xStart[c] - xOnGCPrevElem[c] for c in range(3)]) - xOnGCNextElem = [sx[closestIdxOnCentralPath + 1][c] + sd2[closestIdxOnCentralPath + 1][c] for c in range(3)] + xOnGCNextElem = [sx[closestIdxOnCentralPath + 1][c] + sd2[closestIdxOnCentralPath + 1][c] for c in + range(3)] distBetweenXOnGCNextElem = vector.magnitude([xStart[c] - xOnGCNextElem[c] for c in range(3)]) - eiLowerLimit = closestIdxOnCentralPath - (1 if distBetweenXOnGCNextElem > distBetweenXOnGCPrevElem else 0) + eiLowerLimit = closestIdxOnCentralPath - ( + 1 if distBetweenXOnGCNextElem > distBetweenXOnGCPrevElem else 0) elif closestIdxOnCentralPath == len(sx) - 1: eiLowerLimit = closestIdxOnCentralPath - 1 elif closestIdxOnCentralPath == 0: @@ -1013,11 +1021,14 @@ def generateBaseMesh(cls, region, options): for iter in range(100): xiGuess = 0.5 * (xiLowerLimit + xiUpperLimit) x = interp.interpolateCubicHermite(xLowerLimit, d1LowerLimit, xUpperLimit, d1UpperLimit, xiGuess) - d2 = interp.interpolateCubicHermite(d2LowerLimit, d12LowerLimit, d2UpperLimit, d12UpperLimit, xiGuess) + d2 = interp.interpolateCubicHermite(d2LowerLimit, d12LowerLimit, d2UpperLimit, d12UpperLimit, + xiGuess) xGuess = [x[c] + d2[c] for c in range(3)] distBetweenXAndXStart = vector.magnitude([xStart[c] - xGuess[c] for c in range(3)]) - distBetweenXOnGCLowerLimitAndXStart = vector.magnitude([xStart[c] - xOnGCLowerLimit[c] for c in range(3)]) - distBetweenXOnGCUpperLimitAndXStart = vector.magnitude([xStart[c] - xOnGCUpperLimit[c] for c in range(3)]) + distBetweenXOnGCLowerLimitAndXStart = vector.magnitude( + [xStart[c] - xOnGCLowerLimit[c] for c in range(3)]) + distBetweenXOnGCUpperLimitAndXStart = vector.magnitude( + [xStart[c] - xOnGCUpperLimit[c] for c in range(3)]) if abs(distBetweenXAndXStart - distBetweenXAndXStartPrev) < tol: xProjection = x @@ -1049,8 +1060,11 @@ def generateBaseMesh(cls, region, options): xAround, d1Around = createEllipsePoints(sx[-1], 2 * math.pi, sd2[-1], sd3[-1], elementsCountAroundDuod, startRadians=0.0) - d1Around = interp.smoothCubicHermiteDerivativesLoop(xAround, d1Around, - magnitudeScalingMode=interp.DerivativeScalingMode.HARMONIC_MEAN) + d1Around = \ + interp.smoothCubicHermiteDerivativesLoop(xAround, d1Around, + magnitudeScalingMode=interp.DerivativeScalingMode.HARMONIC_MEAN + ) + xAroundEllipse.append(xAround) d1AroundEllipse.append(d1Around) @@ -1065,10 +1079,12 @@ def generateBaseMesh(cls, region, options): if n == elementsAroundHalfDuod: endPosition = o1_Positions[elementsAroundHalfEso] else: - endPosition = trackSurfaceStomach.findNearestPosition(xAroundBefore6Pt[n + (0 if n < elementsAroundHalfDuod else 1)]) + endPosition = trackSurfaceStomach.findNearestPosition( + xAroundBefore6Pt[n + (0 if n < elementsAroundHalfDuod else 1)]) endProportion1, endProportion2 = trackSurfaceStomach.getProportion(endPosition) xSampled = getSmoothedSampledPointsOnTrackSurface(trackSurfaceStomach, startProportion1, - startProportion2, endProportion1, endProportion2, 2)[0] + startProportion2, endProportion1, endProportion2, + 2)[0] xAve.append(xSampled[1]) # Find 6 pt junction @@ -1141,7 +1157,8 @@ def generateBaseMesh(cls, region, options): for n1 in range(1, elementsCountAroundDuod + 1): startPosition = trackSurfaceStomach.findNearestPosition(xAlongAround[-1][n1]) startProportion1, startProportion2 = trackSurfaceStomach.getProportion(startPosition) - endPosition = trackSurfaceStomach.findNearestPosition(xAroundEllipse[0][n1 + (0 if n1 < elementsAroundHalfDuod + 1 else -1)]) + endPosition = trackSurfaceStomach.findNearestPosition( + xAroundEllipse[0][n1 + (0 if n1 < elementsAroundHalfDuod + 1 else -1)]) endProportion1, endProportion2 = trackSurfaceStomach.getProportion(endPosition) xSampled = getSmoothedSampledPointsOnTrackSurface(trackSurfaceStomach, startProportion1, startProportion2, endProportion1, endProportion2, 2)[0] @@ -1183,7 +1200,8 @@ def generateBaseMesh(cls, region, options): elementsCountAlongTrackSurface)[1] if GCIdx < nodesCountFromEsoToApex: rotFrame = matrix.getRotationMatrixFromAxisAngle(vector.normalise(d2EsoToDuodGC[GCIdx]), math.pi) - d2GCRot = [rotFrame[j][0] * d2GC[0] + rotFrame[j][1] * d2GC[1] + rotFrame[j][2] * d2GC[2] for j in range(3)] + d2GCRot = [rotFrame[j][0] * d2GC[0] + rotFrame[j][1] * d2GC[1] + rotFrame[j][2] * d2GC[2] for j in + range(3)] d2GC = d2GCRot for nSide in range(2): @@ -1193,16 +1211,19 @@ def generateBaseMesh(cls, region, options): xAlongAround[0][elementsAroundHalfDuod - nLoop][c] for c in range(3)] else: rotFrame = matrix.getRotationMatrixFromAxisAngle(vector.normalise(d2EsoToDuodGC[GCIdx]), math.pi) - d2GCRot = [rotFrame[j][0] * d2GC[0] + rotFrame[j][1] * d2GC[1] + rotFrame[j][2] * d2GC[2] for j in range(3)] + d2GCRot = [rotFrame[j][0] * d2GC[0] + rotFrame[j][1] * d2GC[1] + rotFrame[j][2] * d2GC[2] for j in + range(3)] d2GC = d2GCRot xEnd = xAlongAround[0][elementsAroundHalfDuod + 1 + nLoop] d2End = [xAlongAround[1][elementsAroundHalfDuod + nLoop + 1][c] - - xAlongAround[0][elementsAroundHalfDuod + (1 if elementsCountAroundEso > 8 else 2) + nLoop][c] for c in range(3)] + xAlongAround[0][elementsAroundHalfDuod + + (1 if elementsCountAroundEso > 8 else 2) + nLoop][c] for c in range(3)] nx = [xEsoToDuodGC[GCIdx], xEnd] nd2 = [d2GC, d2End] - x, d2 = interp.sampleCubicHermiteCurves(nx, nd2, elementsAroundQuarterEso + 2, arcLengthDerivatives=True)[0:2] + x, d2 = interp.sampleCubicHermiteCurves(nx, nd2, elementsAroundQuarterEso + 2, + arcLengthDerivatives=True)[0:2] # Find closest sampled points onto track surface xProjectedPoints = [] @@ -1299,7 +1320,8 @@ def generateBaseMesh(cls, region, options): for n in range(len(xLoopsRight) - 1): xAroundRight.append(xLoopsRight[-(1 + n)][loopIdx]) - d1AroundRight.append(findDerivativeBetweenPoints(xLoopsRight[-(1 + n)][loopIdx], xLoopsRight[-(1 + n + 1)][loopIdx])) + d1AroundRight.append(findDerivativeBetweenPoints(xLoopsRight[-(1 + n)][loopIdx], + xLoopsRight[-(1 + n + 1)][loopIdx])) if loopIdx < elementsAroundQuarterEso: # additional elements upstream of triple point xLoop = xLoopsRight[0][loopIdx] @@ -1346,7 +1368,8 @@ def generateBaseMesh(cls, region, options): for n in range(len(xLoopsLeft) - 1): xAroundLeft.append(xLoopsLeft[n][loopIdx]) - d1AroundLeft.append(findDerivativeBetweenPoints(xLoopsLeft[n][loopIdx], xLoopsLeft[n + 1][loopIdx])) + d1AroundLeft.append( + findDerivativeBetweenPoints(xLoopsLeft[n][loopIdx], xLoopsLeft[n + 1][loopIdx])) xOnLastLoopLeft = xLoopsLeft[-1][loopIdx] d1OnLastLoopLeft = findDerivativeBetweenPoints(xLoopsLeft[-2][loopIdx], xLoopsLeft[-1][loopIdx]) @@ -1494,7 +1517,7 @@ def generateBaseMesh(cls, region, options): xAround = [xEsoToDuodGC[i + n2 + 1]] + xRow2Right[1:] + xRow2Left[:-1] d1Around = [d2EsoToDuodGC[i + n2 + 1]] + d1Row2Right[1:] + d1Row2Left[:-1] - elif n2 > 1 and n2 < elementsAroundQuarterEso + 2: + elif 1 < n2 < elementsAroundQuarterEso + 2: xAround = xUp[countUp] if n2 < elementsAroundQuarterEso: # upstream of triple pt d1Around = d1Up[countUp] @@ -1520,7 +1543,8 @@ def generateBaseMesh(cls, region, options): elif n2 == elementsAroundHalfEso + 1: # 6 point junction ring # take smoothed d1 from dSmoothedTripleTo6Pt - startRightIdx = int(len(xBifurcationRings[0]) * 0.5 + elementsAroundQuarterEso + len(xAlongAround[junctionIdx]) * 0.5) + startRightIdx = int(len(xBifurcationRings[0]) * 0.5 + elementsAroundQuarterEso + + len(xAlongAround[junctionIdx]) * 0.5) endRightIdx = startRightIdx + int(len(xAlongAround[junctionIdx]) * 0.5) + 1 startLeftIdx = startRightIdx - int(len(xAlongAround[junctionIdx]) * 0.5) + 1 d1Around = dSmoothLoopTripleTo6Pt[startRightIdx: endRightIdx] + \ @@ -1545,7 +1569,8 @@ def generateBaseMesh(cls, region, options): d1RegularRightLoop.append(d1Outer[idx][int(len(xOuter[idx]) * 0.5 - 1 - n1)]) xRegularLoop.append(xEsoToDuodGC[n1 + 2]) for n2 in range(elementsCountAlong): - xRegularLoop.append(xOuter[n2 + 1][int(len(xOuter[n2 + 1]) * 0.5 + n1 + (1 if n2 >= elementsAroundHalfEso else 2))]) + xRegularLoop.append( + xOuter[n2 + 1][int(len(xOuter[n2 + 1]) * 0.5 + n1 + (1 if n2 >= elementsAroundHalfEso else 2))]) for n in range(len(xRegularLoop) - 1): d = findDerivativeBetweenPoints(xRegularLoop[n], xRegularLoop[n + 1]) @@ -1557,10 +1582,12 @@ def generateBaseMesh(cls, region, options): # Switch direction on right side for n2 in range(elementsCountAlong): - rotAxis = vector.normalise(vector.crossproduct3(vector.normalise(d1RegularRightLoop[n2]), d2SmoothRegularLoop[n2])) + rotAxis = vector.normalise( + vector.crossproduct3(vector.normalise(d1RegularRightLoop[n2]), d2SmoothRegularLoop[n2])) rotFrame = matrix.getRotationMatrixFromAxisAngle(rotAxis, math.pi) d = d2SmoothRegularLoop[n2] - d2SmoothRegularLoop[n2] = [rotFrame[j][0] * d[0] + rotFrame[j][1] * d[1] + rotFrame[j][2] * d[2] for j in range(3)] + d2SmoothRegularLoop[n2] = [rotFrame[j][0] * d[0] + rotFrame[j][1] * d[1] + + rotFrame[j][2] * d[2] for j in range(3)] xRegularLoops.append(xRegularLoop) d2RegularLoops.append(d2SmoothRegularLoop) d2RegularOrderedLoops.append(d2SmoothRegularOrderedLoop) @@ -1652,9 +1679,12 @@ def generateBaseMesh(cls, region, options): # right point on annulus d2 = dSmoothLoopGCTriplePt[int(len(xLoopGCTriplePt) * 0.5) - n2] - rotAxis = vector.normalise(vector.crossproduct3(vector.normalise(d1Outer[n2][int(len(d1Outer[n2]) * 0.5)]), vector.normalise(d2))) + rotAxis = vector.normalise( + vector.crossproduct3(vector.normalise(d1Outer[n2][int(len(d1Outer[n2]) * 0.5)]), + vector.normalise(d2))) rotFrame = matrix.getRotationMatrixFromAxisAngle(rotAxis, math.pi) - d2Around.append([rotFrame[j][0] * d2[0] + rotFrame[j][1] * d2[1] + rotFrame[j][2] * d2[2] for j in range(3)]) + d2Around.append( + [rotFrame[j][0] * d2[0] + rotFrame[j][1] * d2[1] + rotFrame[j][2] * d2[2] for j in range(3)]) # left point on annulus d2Around.append(dSmoothLoopGCTriplePt[int(len(xLoopGCTriplePt) * 0.5) + n2]) @@ -1662,7 +1692,7 @@ def generateBaseMesh(cls, region, options): for n1 in range(nextIdx + 1): d2Around.append(d2RegularLoops[n1][int(len(d2RegularLoops[n1]) * 0.5) + n2]) - elif n2 > 1 and n2 < elementsAroundQuarterEso + 2: + elif 1 < n2 < elementsAroundQuarterEso + 2: # GC before triple point & triple point d2Around.append(d2GC[len(xOuter[0]) + n2]) @@ -1674,10 +1704,12 @@ def generateBaseMesh(cls, region, options): d2Around.append(d2RegularLoops[n1][int(len(d2RegularLoops[n1]) * 0.5) - n2]) # Annulus right - d2 = dSmoothLoopGCTriplePt[int(len(xLoopGCTriplePt) * 0.5) - n2 + (1 if n2 > elementsAroundQuarterEso else 0)] + d2 = dSmoothLoopGCTriplePt[ + int(len(xLoopGCTriplePt) * 0.5) - n2 + (1 if n2 > elementsAroundQuarterEso else 0)] if n2 <= elementsAroundQuarterEso: # Rotate to point towards duodenum - rotAxis = vector.normalise(vector.crossproduct3(vector.normalise(d1Outer[n2][int(len(d1Outer[n2]) * 0.5)]), - vector.normalise(d2))) + rotAxis = vector.normalise( + vector.crossproduct3(vector.normalise(d1Outer[n2][int(len(d1Outer[n2]) * 0.5)]), + vector.normalise(d2))) rotFrame = matrix.getRotationMatrixFromAxisAngle(rotAxis, math.pi) d2Around.append( [rotFrame[j][0] * d2[0] + rotFrame[j][1] * d2[1] + rotFrame[j][2] * d2[2] for j in range(3)]) @@ -1685,7 +1717,8 @@ def generateBaseMesh(cls, region, options): d2Around.append(d2) # just take d2 as-is cos we are going to remove this point later # Annulus left - d2Around.append(dSmoothLoopGCTriplePt[int(len(xLoopGCTriplePt) * 0.5) + n2 - (1 if n2 > elementsAroundQuarterEso else 0)]) + d2Around.append(dSmoothLoopGCTriplePt[ + int(len(xLoopGCTriplePt) * 0.5) + n2 - (1 if n2 > elementsAroundQuarterEso else 0)]) # Regular down left for n1 in range(nextIdx + 1): @@ -1858,7 +1891,7 @@ def generateBaseMesh(cls, region, options): for n1 in range(nextIdx + 1): d2CurvatureAround.append(curvatureRegularLoops[n1][int(len(curvatureRegularLoops[n1]) * 0.5) + n2]) - elif n2 > 1 and n2 < elementsAroundQuarterEso + 2: # Before triple pt & triple point + elif 1 < n2 < elementsAroundQuarterEso + 2: # Before triple pt & triple point xAround = xOuter[n2] if n2 < elementsAroundQuarterEso: # upstream of triple pt d1Around = d1Outer[n2] @@ -1884,7 +1917,8 @@ def generateBaseMesh(cls, region, options): d2CurvatureAround.append(curvatureRegularLoops[n1][int(len(curvatureRegularLoops[n1]) * 0.5) - n2]) # Annulus right d2CurvatureAround.append(curvatureLoopGCTriplePt[ - int(len(curvatureLoopGCTriplePt) * 0.5) - n2 + (1 if n2 > elementsAroundQuarterEso else 0)]) + int(len(curvatureLoopGCTriplePt) * 0.5) - n2 + + (1 if n2 > elementsAroundQuarterEso else 0)]) # Annulus left d2CurvatureAround.append(curvatureLoopGCTriplePt[int(len(curvatureLoopGCTriplePt) * 0.5) + n2 - ( 1 if n2 > elementsAroundQuarterEso else 0)]) @@ -1914,7 +1948,8 @@ def generateBaseMesh(cls, region, options): if n2 > elementsAroundHalfEso + 1: # closed rings beyond 6 point junction xLoop = xAround[int(len(xAround) * 0.5 + 1):] + xAround[: int(len(xAround) * 0.5 + 1)] d1Loop = d1Around[int(len(d1Around) * 0.5 + 1):] + d1Around[: int(len(d1Around) * 0.5 + 1)] - normsLoop = normsAround[int(len(normsAround) * 0.5 + 1):] + normsAround[: int(len(normsAround) * 0.5 + 1)] + normsLoop = normsAround[int(len(normsAround) * 0.5 + 1):] + \ + normsAround[: int(len(normsAround) * 0.5 + 1)] curvature = findCurvatureAroundLoop(xLoop, d1Loop, normsLoop) # Rearrange to correct order d1CurvatureAround = curvature[int(len(xLoop) * 0.5) - 1:] + curvature[: int(len(xAround) * 0.5) - 1] @@ -1966,7 +2001,7 @@ def generateBaseMesh(cls, region, options): for n2 in range(elementsCountAlong + 1): idxThroughWall = [] for n3 in range(elementsCountThroughWall + 1): - xi3 = xi3List[n3] if elementsCountThroughWall > 1 else 1.0/elementsCountThroughWall * n3 + xi3 = xi3List[n3] if elementsCountThroughWall > 1 else 1.0 / elementsCountThroughWall * n3 idxAround = [] for n1 in range(len(xOuter[n2])): # Coordinates @@ -1988,7 +2023,8 @@ def generateBaseMesh(cls, region, options): d2List.append(d2) # d3 - d3 = [c * wallThickness * (thicknessProportions[n3+1] if elementsCountThroughWall > 1 else 1.0) for c in norm] + d3 = [c * wallThickness * (thicknessProportions[n3 + 1] if elementsCountThroughWall > 1 else 1.0) + for c in norm] d3List.append(d3) idxAround.append(nodeIdx) @@ -2047,7 +2083,7 @@ def generateBaseMesh(cls, region, options): elementtemplateStandard = mesh.createElementtemplate() elementtemplateStandard.setElementShapeType(Element.SHAPE_TYPE_CUBE) - result = elementtemplateStandard.defineField(coordinates, -1, eftStandard) + elementtemplateStandard.defineField(coordinates, -1, eftStandard) elementtemplateX = mesh.createElementtemplate() elementtemplateX.setElementShapeType(Element.SHAPE_TYPE_CUBE) @@ -2108,13 +2144,14 @@ def generateBaseMesh(cls, region, options): bni22 = bni21 + 1 nodeIdentifiers = [bni11, bni12, bni21, bni22, bni11 + elementsCountAround1, - bni12 + (elementsCountAround1 if e1 < elementsCountAround1 * 2 else elementsCountAround2), + bni12 + (elementsCountAround1 if e1 < elementsCountAround1 * 2 + else elementsCountAround2), bni21 + elementsCountAround2, bni22 + elementsCountAround2] element = mesh.createElement(elementIdentifier, elementtemplate1) - result2 = element.setNodesByIdentifier(eft1, nodeIdentifiers) + element.setNodesByIdentifier(eft1, nodeIdentifiers) if scaleFactors: - result3 = element.setScaleFactors(eft1, scaleFactors) + element.setScaleFactors(eft1, scaleFactors) if limitingRidge and elementsCountThroughWall > 1 and e3 == 0: fundusMucosaElementIdentifiers.append(elementIdentifier) elementIdxAround.append(elementIdentifier) @@ -2152,10 +2189,14 @@ def generateBaseMesh(cls, region, options): bni22 + elementsCountAround2] eft1 = eftfactory.createEftNoCrossDerivatives() setEftScaleFactorIds(eft1, [1], []) - remapEftNodeValueLabel(eft1, [1, 5], Node.VALUE_LABEL_D_DS1, [(Node.VALUE_LABEL_D_DS2, [1])]) - remapEftNodeValueLabel(eft1, [1, 5], Node.VALUE_LABEL_D_DS2, [(Node.VALUE_LABEL_D_DS1, [])]) - remapEftNodeValueLabel(eft1, [2, 6], Node.VALUE_LABEL_D_DS2, [(Node.VALUE_LABEL_D_DS1, [1])]) - remapEftNodeValueLabel(eft1, [2, 6], Node.VALUE_LABEL_D_DS1, [(Node.VALUE_LABEL_D_DS2, [])]) + remapEftNodeValueLabel(eft1, [1, 5], Node.VALUE_LABEL_D_DS1, + [(Node.VALUE_LABEL_D_DS2, [1])]) + remapEftNodeValueLabel(eft1, [1, 5], Node.VALUE_LABEL_D_DS2, + [(Node.VALUE_LABEL_D_DS1, [])]) + remapEftNodeValueLabel(eft1, [2, 6], Node.VALUE_LABEL_D_DS2, + [(Node.VALUE_LABEL_D_DS1, [1])]) + remapEftNodeValueLabel(eft1, [2, 6], Node.VALUE_LABEL_D_DS1, + [(Node.VALUE_LABEL_D_DS2, [])]) elif e1 == 1: # Bottom right wedge scaleFactors = [-1.0] nodeIdentifiers = [bni11, bni21, bni22, @@ -2166,11 +2207,12 @@ def generateBaseMesh(cls, region, options): elementtemplateX.defineField(coordinates, -1, eft1) elementtemplate1 = elementtemplateX - elif e1 > 1 and e1 < elementsCountAround1: + elif 1 < e1 < elementsCountAround1: bni11 = startNode + e3 * elementsCountAround1 + e1 - 1 bni12 = startNode + e3 * elementsCountAround1 + e1 % elementsCountAround1 bni21 = startNode + elementsAroundThroughWall + e1 + elementsCountAround2 * e3 - bni22 = startNode + elementsAroundThroughWall + (e1 + 1) % elementsCountAround2 + elementsCountAround2 * e3 + bni22 = startNode + elementsAroundThroughWall + ( + e1 + 1) % elementsCountAround2 + elementsCountAround2 * e3 nodeIdentifiers = [bni11, bni12, bni21, bni22, bni11 + elementsCountAround1, bni12 + elementsCountAround1, bni21 + elementsCountAround2, bni22 + elementsCountAround2] @@ -2179,7 +2221,8 @@ def generateBaseMesh(cls, region, options): bni11 = startNode + e3 * elementsCountAround1 + e1 - 2 bni12 = startNode + e3 * elementsCountAround1 + (e1 - 1) % elementsCountAround1 bni21 = startNode + elementsAroundThroughWall + e1 + elementsCountAround2 * e3 - bni22 = startNode + elementsAroundThroughWall + (e1 + 1) % elementsCountAround2 + elementsCountAround2 * e3 + bni22 = startNode + elementsAroundThroughWall + ( + e1 + 1) % elementsCountAround2 + elementsCountAround2 * e3 if e1 == elementsCountAround1: # Bottom left wedge nodeIdentifiers = [bni12, bni21, bni22, bni12 + elementsCountAround1, @@ -2195,17 +2238,19 @@ def generateBaseMesh(cls, region, options): bni22 + elementsCountAround2] eft1 = eftfactory.createEftNoCrossDerivatives() setEftScaleFactorIds(eft1, [1], []) - remapEftNodeValueLabel(eft1, [1, 2, 5, 6], Node.VALUE_LABEL_D_DS1, [(Node.VALUE_LABEL_D_DS2, [1])]) - remapEftNodeValueLabel(eft1, [1, 2, 5, 6], Node.VALUE_LABEL_D_DS2, [(Node.VALUE_LABEL_D_DS1, [])]) + remapEftNodeValueLabel(eft1, [1, 2, 5, 6], Node.VALUE_LABEL_D_DS1, + [(Node.VALUE_LABEL_D_DS2, [1])]) + remapEftNodeValueLabel(eft1, [1, 2, 5, 6], Node.VALUE_LABEL_D_DS2, + [(Node.VALUE_LABEL_D_DS1, [])]) elementtemplateX.defineField(coordinates, -1, eft1) elementtemplate1 = elementtemplateX element = mesh.createElement(elementIdentifier, elementtemplate1) - result2 = element.setNodesByIdentifier(eft1, nodeIdentifiers) + element.setNodesByIdentifier(eft1, nodeIdentifiers) if scaleFactors: - result3 = element.setScaleFactors(eft1, scaleFactors) + element.setScaleFactors(eft1, scaleFactors) elementIdxAround.append(elementIdentifier) - if limitingRidge and elementsCountThroughWall> 1 and e3 == 0: + if limitingRidge and elementsCountThroughWall > 1 and e3 == 0: fundusMucosaElementIdentifiers.append(elementIdentifier) elementIdentifier += 1 annotationGroups = annotationGroupsAlong[e2] + annotationGroupsThroughWall[e3] @@ -2218,7 +2263,7 @@ def generateBaseMesh(cls, region, options): elementIdxMat.append(elementIdxThroughWall) # Additional elements between second and upstream bifurcation ring - elif e2 > 1 and e2 < elementsAroundQuarterEso: + elif 1 < e2 < elementsAroundQuarterEso: elementIdxThroughWall = [] for e3 in range(elementsCountThroughWall): elementIdxAround = [] @@ -2230,16 +2275,17 @@ def generateBaseMesh(cls, region, options): bni11 = startNode + e3 * elementsCountAround1 + e1 bni12 = startNode + e3 * elementsCountAround1 + (e1 + 1) % elementsCountAround1 bni21 = startNode + elementsAroundThroughWall + e1 + elementsCountAround2 * e3 - bni22 = startNode + elementsAroundThroughWall + (e1 + 1) % elementsCountAround2 + elementsCountAround2 * e3 + bni22 = startNode + elementsAroundThroughWall + \ + (e1 + 1) % elementsCountAround2 + elementsCountAround2 * e3 nodeIdentifiers = [bni11, bni12, bni21, bni22, bni11 + elementsCountAround1, bni12 + elementsCountAround1, bni21 + elementsCountAround2, bni22 + elementsCountAround2] element = mesh.createElement(elementIdentifier, elementtemplate1) - result2 = element.setNodesByIdentifier(eft1, nodeIdentifiers) + element.setNodesByIdentifier(eft1, nodeIdentifiers) if scaleFactors: - result3 = element.setScaleFactors(eft1, scaleFactors) - if limitingRidge and elementsCountThroughWall> 1 and e3 == 0: + element.setScaleFactors(eft1, scaleFactors) + if limitingRidge and elementsCountThroughWall > 1 and e3 == 0: fundusMucosaElementIdentifiers.append(elementIdentifier) elementIdxAround.append(elementIdentifier) elementIdentifier += 1 @@ -2265,7 +2311,8 @@ def generateBaseMesh(cls, region, options): bni11 = startNode + e3 * elementsCountAround1 + e1 bni12 = startNode + e3 * elementsCountAround1 + (e1 + 1) % elementsCountAround1 bni21 = startNode + elementsAroundThroughWall + e1 + elementsCountAround2 * e3 - bni22 = startNode + elementsAroundThroughWall + (e1 + 1) % elementsCountAround2 + elementsCountAround2 * e3 + bni22 = startNode + elementsAroundThroughWall + \ + (e1 + 1) % elementsCountAround2 + elementsCountAround2 * e3 if e1 < int(elementsCountAround1 * 0.5) - 1: nodeIdentifiers = [bni11, bni12, bni21, bni22, @@ -2291,15 +2338,16 @@ def generateBaseMesh(cls, region, options): elif e1 > int(elementsCountAround1 * 0.5) + 1: bni21 = bni21 - 2 - bni22 = startNode + elementsAroundThroughWall + (e1 - 1) % elementsCountAround2 + elementsCountAround2 * e3 + bni22 = startNode + elementsAroundThroughWall + \ + (e1 - 1) % elementsCountAround2 + elementsCountAround2 * e3 nodeIdentifiers = [bni11, bni12, bni21, bni22, bni11 + elementsCountAround1, bni12 + elementsCountAround1, bni21 + elementsCountAround2, bni22 + elementsCountAround2] element = mesh.createElement(elementIdentifier, elementtemplate1) - result2 = element.setNodesByIdentifier(eft1, nodeIdentifiers) + element.setNodesByIdentifier(eft1, nodeIdentifiers) if scaleFactors: - result3 = element.setScaleFactors(eft1, scaleFactors) + element.setScaleFactors(eft1, scaleFactors) if e3 == 0 and e1 == 0: fundusBodyJunctionInnerElementIdentifier = elementIdentifier elementIdxAround.append(elementIdentifier) @@ -2325,34 +2373,41 @@ def generateBaseMesh(cls, region, options): if e1 < int(elementsCountAround1 * 0.5) + 1: bni11 = startNode + e3 * elementsCountAround1 + e1 elif e1 == int(elementsCountAround1 * 0.5) + 1: - bni11 = startNode - len(xOuter[e2 - 1]) * (elementsCountThroughWall + 1) + e3 * len(xOuter[e2 - 1]) + e1 + 1 + bni11 = startNode - len(xOuter[e2 - 1]) * (elementsCountThroughWall + 1) + \ + e3 * len(xOuter[e2 - 1]) + e1 + 1 elif e1 > int(elementsCountAround1 * 0.5) + 1: bni11 = startNode + e3 * elementsCountAround1 + e1 - 1 if e1 < int(elementsCountAround1 * 0.5): bni12 = startNode + e3 * elementsCountAround1 + (e1 + 1) % elementsCountAround1 elif e1 == int(elementsCountAround1 * 0.5): - bni12 = startNode - len(xOuter[e2 - 1]) * (elementsCountThroughWall + 1) + e3 * len(xOuter[e2 - 1]) + e1 + 1 + bni12 = startNode - len(xOuter[e2 - 1]) * (elementsCountThroughWall + 1) + \ + e3 * len(xOuter[e2 - 1]) + e1 + 1 elif e1 > int(elementsCountAround1 * 0.5): bni12 = startNode + e3 * elementsCountAround1 + e1 % elementsCountAround1 if e1 > int(elementsCountAround1 * 0.5): bni21 = startNode + elementsAroundThroughWall + e1 + elementsCountAround2 * e3 + 1 - bni22 = startNode + elementsAroundThroughWall + (e1 + 2) % elementsCountAround2 + elementsCountAround2 * e3 + bni22 = startNode + elementsAroundThroughWall + \ + (e1 + 2) % elementsCountAround2 + elementsCountAround2 * e3 else: bni21 = startNode + elementsAroundThroughWall + e1 + elementsCountAround2 * e3 - bni22 = startNode + elementsAroundThroughWall + (e1 + 1) % elementsCountAround2 + elementsCountAround2 * e3 + bni22 = startNode + elementsAroundThroughWall + \ + (e1 + 1) % elementsCountAround2 + elementsCountAround2 * e3 nodeIdentifiers = [bni11, bni12, bni21, bni22, - bni11 + (len(xOuter[e2 - 1]) if e1 == int(elementsCountAround1 * 0.5) + 1 else elementsCountAround1), - bni12 + (len(xOuter[e2 - 1]) if e1 == int(elementsCountAround1 * 0.5) else elementsCountAround1), + bni11 + (len(xOuter[e2 - 1]) if e1 == int( + elementsCountAround1 * 0.5) + 1 else elementsCountAround1), + bni12 + (len(xOuter[e2 - 1]) if e1 == int( + elementsCountAround1 * 0.5) else elementsCountAround1), bni21 + elementsCountAround2, bni22 + elementsCountAround2] if e1 == int(elementsCountAround1 * 0.5): scaleFactors = [-1.0] eft1 = eftfactory.createEftNoCrossDerivatives() setEftScaleFactorIds(eft1, [1], []) - remapEftNodeValueLabel(eft1, [2, 6], Node.VALUE_LABEL_D_DS1, [(Node.VALUE_LABEL_D_DS2, [1])]) + remapEftNodeValueLabel(eft1, [2, 6], Node.VALUE_LABEL_D_DS1, + [(Node.VALUE_LABEL_D_DS2, [1])]) remapEftNodeValueLabel(eft1, [2, 6], Node.VALUE_LABEL_D_DS2, [(Node.VALUE_LABEL_D_DS1, [])]) elementtemplateX.defineField(coordinates, -1, eft1) elementtemplate1 = elementtemplateX @@ -2361,15 +2416,16 @@ def generateBaseMesh(cls, region, options): scaleFactors = [-1.0] eft1 = eftfactory.createEftNoCrossDerivatives() setEftScaleFactorIds(eft1, [1], []) - remapEftNodeValueLabel(eft1, [1, 5], Node.VALUE_LABEL_D_DS2, [(Node.VALUE_LABEL_D_DS1, [1])]) + remapEftNodeValueLabel(eft1, [1, 5], Node.VALUE_LABEL_D_DS2, + [(Node.VALUE_LABEL_D_DS1, [1])]) remapEftNodeValueLabel(eft1, [1, 5], Node.VALUE_LABEL_D_DS1, [(Node.VALUE_LABEL_D_DS2, [])]) elementtemplateX.defineField(coordinates, -1, eft1) elementtemplate1 = elementtemplateX element = mesh.createElement(elementIdentifier, elementtemplate1) - result2 = element.setNodesByIdentifier(eft1, nodeIdentifiers) + element.setNodesByIdentifier(eft1, nodeIdentifiers) if scaleFactors: - result3 = element.setScaleFactors(eft1, scaleFactors) + element.setScaleFactors(eft1, scaleFactors) elementIdxAround.append(elementIdentifier) elementIdentifier += 1 annotationGroups = annotationGroupsAlong[e2] + annotationGroupsThroughWall[e3] @@ -2387,15 +2443,20 @@ def generateBaseMesh(cls, region, options): for e3 in range(elementsCountThroughWall): elementIdxAround = [] for e1 in range(elementsCountAround1 - 1): - bni11 = startNode + e3 * elementsCountAround1 + e1 + (0 if e1 < int(elementsCountAround1 * 0.5) else 1) - bni12 = startNode + e3 * elementsCountAround1 + (e1 + (1 if e1 < int(elementsCountAround1 * 0.5) else 2)) % elementsCountAround1 - bni21 = startNode + elementsAroundThroughWall + e1 + elementsCountAround2 * e3 + (0 if e1 < int(elementsCountAround1 * 0.5) else 1) - bni22 = startNode + elementsAroundThroughWall + (e1 + (1 if e1 < int(elementsCountAround1 * 0.5) else 2)) % elementsCountAround2 + elementsCountAround2 * e3 + bni11 = startNode + e3 * elementsCountAround1 + e1 +\ + (0 if e1 < int(elementsCountAround1 * 0.5) else 1) + bni12 = startNode + e3 * elementsCountAround1 + \ + (e1 + (1 if e1 < int(elementsCountAround1 * 0.5) else 2)) % elementsCountAround1 + bni21 = startNode + elementsAroundThroughWall + e1 + \ + elementsCountAround2 * e3 + (0 if e1 < int(elementsCountAround1 * 0.5) else 1) + bni22 = startNode + elementsAroundThroughWall + \ + (e1 + (1 if e1 < int(elementsCountAround1 * 0.5) else 2)) % elementsCountAround2 + \ + elementsCountAround2 * e3 nodeIdentifiers = [bni11, bni12, bni21, bni22, bni11 + elementsCountAround1, bni12 + elementsCountAround1, bni21 + elementsCountAround2, bni22 + elementsCountAround2] element = mesh.createElement(elementIdentifier, elementtemplateStandard) - result = element.setNodesByIdentifier(eftStandard, nodeIdentifiers) + element.setNodesByIdentifier(eftStandard, nodeIdentifiers) elementIdxAround.append(elementIdentifier) elementIdentifier = elementIdentifier + 1 annotationGroups = annotationGroupsAlong[e2] + annotationGroupsThroughWall[e3] @@ -2417,15 +2478,19 @@ def generateBaseMesh(cls, region, options): eft1 = eftStandard elementtemplate1 = elementtemplateStandard if e2 == elementsAroundHalfEso: - bni11 = startNode + e3 * elementsCountAround1 + e1 + (0 if e1 < int(elementsCountAround1 * 0.5) else 1) - bni12 = startNode + e3 * elementsCountAround1 + (e1 + (1 if e1 < int(elementsCountAround1 * 0.5) else 2)) % elementsCountAround1 + bni11 = startNode + e3 * elementsCountAround1 + \ + e1 + (0 if e1 < int(elementsCountAround1 * 0.5) else 1) + bni12 = startNode + e3 * elementsCountAround1 + \ + (e1 + (1 if e1 < int(elementsCountAround1 * 0.5) else 2)) % elementsCountAround1 # Remap elements next to annulus if e1 == int(elementsCountAround1 * 0.5) - 1: scaleFactors = [-1.0] eft1 = eftfactory.createEftNoCrossDerivatives() setEftScaleFactorIds(eft1, [1], []) - remapEftNodeValueLabel(eft1, [4, 8], Node.VALUE_LABEL_D_DS1, [(Node.VALUE_LABEL_D_DS2, [1])]) - remapEftNodeValueLabel(eft1, [4, 8], Node.VALUE_LABEL_D_DS2, ([(Node.VALUE_LABEL_D_DS1, [])])) + remapEftNodeValueLabel(eft1, [4, 8], Node.VALUE_LABEL_D_DS1, + [(Node.VALUE_LABEL_D_DS2, [1])]) + remapEftNodeValueLabel(eft1, [4, 8], Node.VALUE_LABEL_D_DS2, + ([(Node.VALUE_LABEL_D_DS1, [])])) elementtemplateX.defineField(coordinates, -1, eft1) elementtemplate1 = elementtemplateX @@ -2433,8 +2498,8 @@ def generateBaseMesh(cls, region, options): bni11 = startNode + e3 * elementsCountAround1 + e1 bni12 = startNode + e3 * elementsCountAround1 + (e1 + 1) % elementsCountAround1 bni21 = startNode + elementsAroundThroughWall + e1 + elementsCountAround2 * e3 - bni22 = startNode + elementsAroundThroughWall + ( - e1 + 1) % elementsCountAround2 + elementsCountAround2 * e3 + bni22 = startNode + elementsAroundThroughWall + \ + (e1 + 1) % elementsCountAround2 + elementsCountAround2 * e3 nodeIdentifiers = [bni11, bni12, bni21, bni22, bni11 + elementsCountAround1, bni12 + elementsCountAround1, bni21 + elementsCountAround2, bni22 + elementsCountAround2] @@ -2444,21 +2509,24 @@ def generateBaseMesh(cls, region, options): scaleFactors = [-1.0] eft1 = eftfactory.createEftNoCrossDerivatives() setEftScaleFactorIds(eft1, [1], []) - remapEftNodeValueLabel(eft1, [2, 6], Node.VALUE_LABEL_D_DS1, [(Node.VALUE_LABEL_D_DS2, [1])]) - remapEftNodeValueLabel(eft1, [2, 6], Node.VALUE_LABEL_D_DS2, ([(Node.VALUE_LABEL_D_DS1, []), (Node.VALUE_LABEL_D_DS2, [])])) + remapEftNodeValueLabel(eft1, [2, 6], Node.VALUE_LABEL_D_DS1, + [(Node.VALUE_LABEL_D_DS2, [1])]) + remapEftNodeValueLabel(eft1, [2, 6], Node.VALUE_LABEL_D_DS2, + ([(Node.VALUE_LABEL_D_DS1, []), (Node.VALUE_LABEL_D_DS2, [])])) elementtemplateX.defineField(coordinates, -1, eft1) elementtemplate1 = elementtemplateX elif e1 == int(elementsCountAround1 * 0.5): eft1 = eftfactory.createEftNoCrossDerivatives() - remapEftNodeValueLabel(eft1, [1, 5], Node.VALUE_LABEL_D_DS2, ([(Node.VALUE_LABEL_D_DS1, []), (Node.VALUE_LABEL_D_DS2, [])])) + remapEftNodeValueLabel(eft1, [1, 5], Node.VALUE_LABEL_D_DS2, + ([(Node.VALUE_LABEL_D_DS1, []), (Node.VALUE_LABEL_D_DS2, [])])) elementtemplateX.defineField(coordinates, -1, eft1) elementtemplate1 = elementtemplateX element = mesh.createElement(elementIdentifier, elementtemplate1) - result2 = element.setNodesByIdentifier(eft1, nodeIdentifiers) + element.setNodesByIdentifier(eft1, nodeIdentifiers) if scaleFactors: - result3 = element.setScaleFactors(eft1, scaleFactors) + element.setScaleFactors(eft1, scaleFactors) elementIdxAround.append(elementIdentifier) elementIdentifier += 1 annotationGroups = annotationGroupsAlong[e2] + annotationGroupsThroughWall[e3] @@ -2528,7 +2596,8 @@ def generateBaseMesh(cls, region, options): stomachWallAnnotationGroups = [] if elementsCountThroughWall == 4: - stomachWallAnnotationGroups = [[mucosaGroup], [submucosaGroup], [circularMuscleGroup], [longitudinalMuscleGroup]] + stomachWallAnnotationGroups = [[mucosaGroup], [submucosaGroup], [circularMuscleGroup], + [longitudinalMuscleGroup]] # Remove mucosa layer from annulus if elementsCountThroughWall == 4 and limitingRidge: @@ -2576,13 +2645,14 @@ def generateBaseMesh(cls, region, options): "limiting ridge along the greater curvature on luminal surface" if limitingRidge else "fundus-body junction along the greater curvature on luminal surface"]] if elementsCountThroughWall == 4: - markerNames.append(["esophagogastric junction along the greater curvature on circular-longitudinal muscle interface", - "esophagogastric junction along the lesser curvature on circular-longitudinal muscle interface", - "gastroduodenal junction along the greater curvature on circular-longitudinal muscle interface", - "gastroduodenal junction along the lesser curvature on circular-longitudinal muscle interface", - "body-antrum junction along the greater curvature on circular-longitudinal muscle interface", - "limiting ridge along the greater curvature on circular-longitudinal muscle interface" if limitingRidge else - "fundus-body junction along the greater curvature on circular-longitudinal muscle interface"]) + markerNames.append( + ["esophagogastric junction along the greater curvature on circular-longitudinal muscle interface", + "esophagogastric junction along the lesser curvature on circular-longitudinal muscle interface", + "gastroduodenal junction along the greater curvature on circular-longitudinal muscle interface", + "gastroduodenal junction along the lesser curvature on circular-longitudinal muscle interface", + "body-antrum junction along the greater curvature on circular-longitudinal muscle interface", + "limiting ridge along the greater curvature on circular-longitudinal muscle interface" if limitingRidge + else "fundus-body junction along the greater curvature on circular-longitudinal muscle interface"]) markerNames.append(["esophagogastric junction along the greater curvature on serosa", "esophagogastric junction along the lesser curvature on serosa", "gastroduodenal junction along the greater curvature on serosa", @@ -2592,10 +2662,15 @@ def generateBaseMesh(cls, region, options): "fundus-body junction along the greater curvature on serosa"]) markerInnerElementIdentifiers = [stomachStartElement - elementsCountThroughWall * elementsCountAroundEso, - stomachStartElement - (elementsCountThroughWall - 1) * elementsCountAroundEso - elementsAroundHalfEso, - lastDuodenumElementIdentifier - elementsCountThroughWall * elementsCountAroundDuod * (elementsCountAlongGroups[-1] + 1), - lastDuodenumElementIdentifier - elementsCountThroughWall * elementsCountAroundDuod * (elementsCountAlongGroups[-1] + 1) + elementsAroundHalfDuod, - lastDuodenumElementIdentifier - elementsCountThroughWall * elementsCountAroundDuod * (sum(elementsCountAlongGroups[-3:]) + 1), + stomachStartElement - (elementsCountThroughWall - 1) * elementsCountAroundEso - + elementsAroundHalfEso, + lastDuodenumElementIdentifier - elementsCountThroughWall * + elementsCountAroundDuod * (elementsCountAlongGroups[-1] + 1), + lastDuodenumElementIdentifier - elementsCountThroughWall * + elementsCountAroundDuod * (elementsCountAlongGroups[-1] + 1) + + elementsAroundHalfDuod, + lastDuodenumElementIdentifier - elementsCountThroughWall * + elementsCountAroundDuod * (sum(elementsCountAlongGroups[-3:]) + 1), fundusBodyJunctionInnerElementIdentifier] elementsCountAroundLayer = [elementsCountAroundEso, elementsCountAroundEso, @@ -2604,8 +2679,12 @@ def generateBaseMesh(cls, region, options): for n3 in range(len(markerNames)): for n in range(len(markerNames[n3])): - markerGroup = findOrCreateAnnotationGroupForTerm(allAnnotationGroups, region, get_stomach_term(markerNames[n3][n])) - markerElementIdentifier = markerInnerElementIdentifiers[n] + (0 if n3 == 0 or elementsCountThroughWall == 1 else elementsCountAroundLayer[n] * (elementsCountThroughWall - 1)) + markerGroup = findOrCreateAnnotationGroupForTerm(allAnnotationGroups, region, + get_stomach_term(markerNames[n3][n])) + markerElementIdentifier = \ + markerInnerElementIdentifiers[n] + \ + (0 if n3 == 0 or elementsCountThroughWall == 1 else elementsCountAroundLayer[n] * + (elementsCountThroughWall - 1)) markerElement = mesh.findElementByIdentifier(markerElementIdentifier) markerXi = [0.0, 1.0, 0.0 if n3 != len(markerNames) - 1 else 1.0] if n < len(markerNames[n3]) - 1 else \ [0.0, 0.0 if limitingRidge else 1.0, 0.0 if n3 != len(markerNames) - 1 else 1.0] @@ -2642,9 +2721,12 @@ def generateBaseMesh(cls, region, options): for n2 in range(elementsAlongEsophagus + 1): for n3 in range(elementsThroughEsophagusWall + 1): - nodeIdxOnGCMargin = 1 + n2 * (elementsThroughEsophagusWall + 1) * elementsCountAroundEso + n3 * elementsCountAroundEso + nodeIdxOnGCMargin = 1 + n2 * (elementsThroughEsophagusWall + 1) * elementsCountAroundEso + \ + n3 * elementsCountAroundEso nodesOnSplitMargin.append(nodeIdxOnGCMargin) - nodeIdxOnLCMargin = 1 + elementsAroundHalfEso + n2 * (elementsThroughEsophagusWall + 1) * elementsCountAroundEso + n3 * elementsCountAroundEso + nodeIdxOnLCMargin = 1 + elementsAroundHalfEso + \ + n2 * (elementsThroughEsophagusWall + 1) * elementsCountAroundEso + \ + n3 * elementsCountAroundEso nodesOnSplitMargin.append(nodeIdxOnLCMargin) nodesOnLCMargin.append(nodeIdxOnLCMargin) nodesOnSplitMargin += nodeIdxGC + nodeIdxLC @@ -2763,14 +2845,14 @@ def generateBaseMesh(cls, region, options): eft2 = eft remapEftNodeValueLabelsVersion(eft2, lnRemapV2, allValueLabels, 2) - result1 = splitElementtemplate2.defineField(splitCoordinates, -1, eft2) - result2 = element.merge(splitElementtemplate2) + splitElementtemplate2.defineField(splitCoordinates, -1, eft2) + element.merge(splitElementtemplate2) element.setNodesByIdentifier(eft2, nodeIdentifiers) if eft2.getNumberOfLocalScaleFactors() > 0: element.setScaleFactor(eft2, 1, -1.0) else: - result1 = splitElementtemplate1.defineField(splitCoordinates, -1, eft) - result2 = element.merge(splitElementtemplate1) + splitElementtemplate1.defineField(splitCoordinates, -1, eft) + element.merge(splitElementtemplate1) element.setNodesByIdentifier(eft, nodeIdentifiers) if eft.getNumberOfLocalScaleFactors() == 1: element.setScaleFactors(eft, [-1.0]) @@ -2798,14 +2880,14 @@ def refineMesh(cls, meshrefinement, options): @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. - ''' + """ limitingRidge = options['Limiting ridge'] elementsCountThroughWall = options['Number of elements through wall'] @@ -2876,8 +2958,10 @@ def defineFaceAnnotations(cls, region, options, annotationGroups): is_curvatures = fm.createFieldAnd(is_dorsal, is_ventral) CMLMInterfaceGroup = findOrCreateAnnotationGroupForTerm(annotationGroups, region, get_stomach_term( "circular-longitudinal muscle interface of stomach")) - circularMuscleGroup = getAnnotationGroupForTerm(annotationGroups,get_stomach_term("circular muscle layer of stomach")) - longitudinalMuscleGroup = getAnnotationGroupForTerm(annotationGroups, get_stomach_term("longitudinal muscle layer of stomach")) + circularMuscleGroup = getAnnotationGroupForTerm(annotationGroups, + get_stomach_term("circular muscle layer of stomach")) + longitudinalMuscleGroup = \ + getAnnotationGroupForTerm(annotationGroups, get_stomach_term("longitudinal muscle layer of stomach")) is_CM = circularMuscleGroup.getGroup() is_LM = longitudinalMuscleGroup.getGroup() is_CMLMInterface = fm.createFieldAnd(is_CM, is_LM) @@ -2892,29 +2976,30 @@ def defineFaceAnnotations(cls, region, options, annotationGroups): is_ventral_CMLM = fm.createFieldAnd(is_ventral, is_CMLMInterface) ventralStomach_CMLMGroup.getMeshGroup(mesh2d).addElementsConditional(is_ventral_CMLM) - gastroduod_CMLMGroup = findOrCreateAnnotationGroupForTerm(annotationGroups, region, get_stomach_term("circular-longitudinal muscle interface of gastroduodenal junction")) + gastroduod_CMLMGroup = findOrCreateAnnotationGroupForTerm(annotationGroups, region, get_stomach_term( + "circular-longitudinal muscle interface of gastroduodenal junction")) is_gastroduod_CMLM = fm.createFieldAnd(is_gastroduod, is_CMLMInterface) gastroduod_CMLMGroup.getMeshGroup(mesh1d).addElementsConditional(is_gastroduod_CMLM) is_curvatures_CMLM = fm.createFieldAnd(is_curvatures, is_CMLMInterface) - bodyCurvaturesCMLMGroup = findOrCreateAnnotationGroupForTerm(annotationGroups, region, - get_stomach_term( - "circular-longitudinal muscle interface of body of stomach along the gastric-omentum attachment")) - duodenumCurvaturesCMLMGroup = findOrCreateAnnotationGroupForTerm(annotationGroups, region, - get_stomach_term( - "circular-longitudinal muscle interface of duodenum along the gastric-omentum attachment")) - esoCurvaturesCMLMGroup = findOrCreateAnnotationGroupForTerm(annotationGroups, region, - get_stomach_term( - "circular-longitudinal muscle interface of esophagus along the cut margin")) - fundusCurvaturesCMLMGroup = findOrCreateAnnotationGroupForTerm(annotationGroups, region, - get_stomach_term( - "circular-longitudinal muscle interface of fundus of stomach along the gastric-omentum attachment")) - antrumCurvaturesCMLMGroup = findOrCreateAnnotationGroupForTerm(annotationGroups, region, - get_stomach_term( - "circular-longitudinal muscle interface of pyloric antrum along the gastric-omentum attachment")) - pylorusCurvaturesCMLMGroup = findOrCreateAnnotationGroupForTerm(annotationGroups, region, - get_stomach_term( - "circular-longitudinal muscle interface of pyloric canal along the gastric-omentum attachment")) + bodyCurvaturesCMLMGroup = \ + findOrCreateAnnotationGroupForTerm(annotationGroups, region, get_stomach_term( + "circular-longitudinal muscle interface of body of stomach along the gastric-omentum attachment")) + duodenumCurvaturesCMLMGroup = \ + findOrCreateAnnotationGroupForTerm(annotationGroups, region, get_stomach_term( + "circular-longitudinal muscle interface of duodenum along the gastric-omentum attachment")) + esoCurvaturesCMLMGroup = \ + findOrCreateAnnotationGroupForTerm(annotationGroups, region, get_stomach_term( + "circular-longitudinal muscle interface of esophagus along the cut margin")) + fundusCurvaturesCMLMGroup =\ + findOrCreateAnnotationGroupForTerm(annotationGroups, region, get_stomach_term( + "circular-longitudinal muscle interface of fundus of stomach along the gastric-omentum attachment")) + antrumCurvaturesCMLMGroup = \ + findOrCreateAnnotationGroupForTerm(annotationGroups, region, get_stomach_term( + "circular-longitudinal muscle interface of pyloric antrum along the gastric-omentum attachment")) + pylorusCurvaturesCMLMGroup = \ + findOrCreateAnnotationGroupForTerm(annotationGroups, region, get_stomach_term( + "circular-longitudinal muscle interface of pyloric canal along the gastric-omentum attachment")) sectionCurvaturesCMLMGroups = [None, bodyCurvaturesCMLMGroup, None, duodenumCurvaturesCMLMGroup, esoCurvaturesCMLMGroup, fundusCurvaturesCMLMGroup, antrumCurvaturesCMLMGroup, @@ -2942,9 +3027,14 @@ def defineFaceAnnotations(cls, region, options, annotationGroups): sectionCurvaturesCMLMGroups[i].getMeshGroup(mesh1d).addElementsConditional(is_sectionCurvaturesCMLM) if limitingRidge: - limitingRidgeGroup = findOrCreateAnnotationGroupForTerm(annotationGroups, region, get_stomach_term("forestomach-glandular stomach junction")) - innerLimitingRidgeGroup = findOrCreateAnnotationGroupForTerm(annotationGroups, region, get_stomach_term("limiting ridge on luminal surface")) - outerLimitingRidgeGroup = findOrCreateAnnotationGroupForTerm(annotationGroups, region, get_stomach_term("limiting ridge on serosa")) + limitingRidgeGroup = \ + findOrCreateAnnotationGroupForTerm(annotationGroups, region, + get_stomach_term("forestomach-glandular stomach junction")) + innerLimitingRidgeGroup = \ + findOrCreateAnnotationGroupForTerm(annotationGroups, region, + get_stomach_term("limiting ridge on luminal surface")) + outerLimitingRidgeGroup = findOrCreateAnnotationGroupForTerm(annotationGroups, region, + get_stomach_term("limiting ridge on serosa")) is_antrum = antrumGroup.getGroup() is_body = bodyGroup.getGroup() @@ -2964,23 +3054,31 @@ def defineFaceAnnotations(cls, region, options, annotationGroups): is_antrumMucosa = fm.createFieldAnd(is_antrum, is_mucosa) is_bodyAntrumMucosa = fm.createFieldOr(is_bodyMucosa, is_antrumMucosa) - is_xi1Interior = fm.createFieldAnd(fm.createFieldIsOnFace(Element.FACE_TYPE_XI1_0), fm.createFieldIsOnFace(Element.FACE_TYPE_XI1_1)) - is_xi1All = fm.createFieldOr(fm.createFieldIsOnFace(Element.FACE_TYPE_XI1_0), fm.createFieldIsOnFace(Element.FACE_TYPE_XI1_1)) + is_xi1Interior = fm.createFieldAnd(fm.createFieldIsOnFace(Element.FACE_TYPE_XI1_0), + fm.createFieldIsOnFace(Element.FACE_TYPE_XI1_1)) + is_xi1All = fm.createFieldOr(fm.createFieldIsOnFace(Element.FACE_TYPE_XI1_0), + fm.createFieldIsOnFace(Element.FACE_TYPE_XI1_1)) is_xi1BodyAntrumMucosaAll = fm.createFieldAnd(is_bodyAntrumMucosa, is_xi1All) - is_limitingRidgeAroundCardia = fm.createFieldAnd(is_xi1BodyAntrumMucosaAll, fm.createFieldNot(is_xi1Interior)) + is_limitingRidgeAroundCardia = fm.createFieldAnd(is_xi1BodyAntrumMucosaAll, + fm.createFieldNot(is_xi1Interior)) - is_xi2Interior = fm.createFieldAnd(fm.createFieldIsOnFace(Element.FACE_TYPE_XI2_0), fm.createFieldIsOnFace(Element.FACE_TYPE_XI2_1)) + is_xi2Interior = fm.createFieldAnd(fm.createFieldIsOnFace(Element.FACE_TYPE_XI2_0), + fm.createFieldIsOnFace(Element.FACE_TYPE_XI2_1)) is_xi2ZeroBodyMucosa = fm.createFieldAnd(fm.createFieldIsOnFace(Element.FACE_TYPE_XI2_0), is_bodyMucosa) - is_limitingRidgeBodyBoundary = fm.createFieldAnd(is_xi2ZeroBodyMucosa, fm.createFieldNot(is_xi2Interior)) + is_limitingRidgeBodyBoundary = fm.createFieldAnd(is_xi2ZeroBodyMucosa, + fm.createFieldNot(is_xi2Interior)) is_limitingRidgeMucosa = fm.createFieldOr(is_limitingRidgeAroundCardia, is_limitingRidgeBodyBoundary) is_limitingRidge = fm.createFieldOr(is_limitingRidge, is_limitingRidgeMucosa) limitingRidgeGroup.getMeshGroup(mesh2d).addElementsConditional(is_limitingRidge) - is_xi3Interior = fm.createFieldAnd(fm.createFieldIsOnFace(Element.FACE_TYPE_XI3_0), fm.createFieldIsOnFace(Element.FACE_TYPE_XI3_1)) - is_xi3ZeroLimitingRidge = fm.createFieldAnd(is_limitingRidge, fm.createFieldIsOnFace(Element.FACE_TYPE_XI3_0)) - is_xi3OneLimitingRidge = fm.createFieldAnd(is_limitingRidge, fm.createFieldIsOnFace(Element.FACE_TYPE_XI3_1)) + is_xi3Interior = fm.createFieldAnd(fm.createFieldIsOnFace(Element.FACE_TYPE_XI3_0), + fm.createFieldIsOnFace(Element.FACE_TYPE_XI3_1)) + is_xi3ZeroLimitingRidge = fm.createFieldAnd(is_limitingRidge, + fm.createFieldIsOnFace(Element.FACE_TYPE_XI3_0)) + is_xi3OneLimitingRidge = fm.createFieldAnd(is_limitingRidge, + fm.createFieldIsOnFace(Element.FACE_TYPE_XI3_1)) is_limitingRidgeInner = fm.createFieldAnd(is_xi3ZeroLimitingRidge, fm.createFieldNot(is_xi3Interior)) innerLimitingRidgeGroup.getMeshGroup(mesh1d).addElementsConditional(is_limitingRidgeInner) @@ -2994,6 +3092,7 @@ def defineFaceAnnotations(cls, region, options, annotationGroups): is_limitingRidgeCMLM = fm.createFieldAnd(is_CMLMInterface, is_limitingRidge) limitingRidge_CMLMGroup.getMeshGroup(mesh1d).addElementsConditional(is_limitingRidgeCMLM) + def findClosestPositionAndDerivativeOnTrackSurface(x, nx, trackSurface, nxProportion1, elementsCountAlongTrackSurface): """ Find the closest position and derivative around the tracksurface of a point sitting near the fundus of stomach. @@ -3007,7 +3106,8 @@ def findClosestPositionAndDerivativeOnTrackSurface(x, nx, trackSurface, nxPropor :return: position and derivative of point around track surface """ closestIdxOnNx = interp.getNearestPointIndex(nx, x) - closestPositionToPoint = trackSurface.createPositionProportion(nxProportion1, closestIdxOnNx / elementsCountAlongTrackSurface) + closestPositionToPoint = trackSurface.createPositionProportion(nxProportion1, + closestIdxOnNx / elementsCountAlongTrackSurface) xPosition = trackSurface.findNearestPosition(x, closestPositionToPoint) d = trackSurface.evaluateCoordinates(xPosition, derivatives=True)[1] @@ -3028,7 +3128,7 @@ def getSmoothedSampledPointsOnTrackSurface(trackSurface, startProportion1, start :param startDerivative, endDerivative: optional derivative vectors in 3-D world coordinates to match at the start and end of the curves. If omitted, fits in with other derivative or is in a straight line from a to b - :param derivativeMagnitudeStart, derivativeMagnitudeEnd: optional magnitude of derivatives to match at the start and + :param startDerivativeMagnitude, endDerivativeMagnitude: optional magnitude of derivatives to match at the start and end of the curves :return: coordinates and derivative of sampled points """ diff --git a/src/scaffoldmaker/utils/annulusmesh.py b/src/scaffoldmaker/utils/annulusmesh.py index 4dc0fe18..78a34a28 100644 --- a/src/scaffoldmaker/utils/annulusmesh.py +++ b/src/scaffoldmaker/utils/annulusmesh.py @@ -1,6 +1,6 @@ -''' +""" Utility functions for generating annulus mesh between start and end loops of points. -''' +""" from __future__ import division import copy @@ -16,72 +16,74 @@ from scaffoldmaker.utils.eftfactory_tricubichermite import eftfactory_tricubichermite -def derivativeSignsToExpressionTerms(valueLabels, signs, scaleFactorIdx = None): - ''' - Return remap expression terms for summing derivative[i]*sign[i]*scaleFactor +def derivativeSignsToExpressionTerms(valueLabels, signs, scaleFactorIdx=None): + """ + Return remap expression terms for summing derivative[i] * sign[i] * scaleFactor :param valueLabels: List of node value labels to possibly include. :param signs: List of 1 (no scaling), -1 (scale by scale factor 1) or 0 (no term). - :param scaleFactorIdx: Optional index of local scale factor to scale all non-zero terms. Default None means no extra scaling. - ''' + :param scaleFactorIdx: Optional index of local scale factor to scale all non-zero terms. Default None means no + extra scaling. + """ expressionTerms = [] for i in range(len(valueLabels)): if signs[i] == 1: - expressionTerms.append((valueLabels[i], ([ scaleFactorIdx ] if scaleFactorIdx else []))) + expressionTerms.append((valueLabels[i], ([scaleFactorIdx] if scaleFactorIdx else []))) elif signs[i] == -1: expressionTerms.append((valueLabels[i], ([1, scaleFactorIdx] if scaleFactorIdx else [1]))) return expressionTerms + def getMappedD1D2(gds, derivativesMaps): - ''' + """ Get vector combinations of d1In, d2In, d3In indicated by derivativesMap. :param gds: List of global d1, d2 and optionally d3. :param derivativesMaps: List over d1, d2, d3, and optionally d1b (for different d1 exiting global node) of list of 3 weights of gds, each limited to -1.0, 0.0, or -1.0. :return: Effective d1, d2. Where d1 is around, d2 is radial. - ''' + """ dslimit = len(gds) if not (derivativesMaps and derivativesMaps[0]): d1 = gds[0] else: derivativesMap = derivativesMaps[0] - d1 = [ 0.0, 0.0, 0.0 ] + d1 = [0.0, 0.0, 0.0] for ds in range(dslimit): if derivativesMap[ds] != 0.0: for c in range(3): - d1[c] += derivativesMap[ds]*gds[ds][c] + d1[c] += derivativesMap[ds] * gds[ds][c] if len(derivativesMaps) > 3: # average with d1 map for other side derivativesMap = derivativesMaps[3] - d1 = [ 0.5*d for d in d1 ] + d1 = [0.5 * d for d in d1] if not derivativesMap: for c in range(3): - d1[c] += 0.5*gds[0][c] + d1[c] += 0.5 * gds[0][c] else: for ds in range(dslimit): if derivativesMap[ds] != 0.0: for c in range(3): - d1[c] += 0.5*derivativesMap[ds]*gds[ds][c] + d1[c] += 0.5 * derivativesMap[ds] * gds[ds][c] if not (derivativesMaps and derivativesMaps[1]): d2 = gds[1] else: derivativesMap = derivativesMaps[1] - d2 = [ 0.0, 0.0, 0.0 ] + d2 = [0.0, 0.0, 0.0] for ds in range(dslimit): if derivativesMap[ds] != 0.0: for c in range(3): - d2[c] += derivativesMap[ds]*gds[ds][c] + d2[c] += derivativesMap[ds] * gds[ds][c] return d1, d2 -def createAnnulusMesh3d(nodes, mesh, nextNodeIdentifier, nextElementIdentifier, - startPointsx, startPointsd1, startPointsd2, startPointsd3, startNodeId, startDerivativesMap, - endPointsx, endPointsd1, endPointsd2, endPointsd3, endNodeId, endDerivativesMap, - forceStartLinearXi3 = False, forceMidLinearXi3 = False, forceEndLinearXi3 = False, - maxStartThickness = None, maxEndThickness = None, useCrossDerivatives = False, - elementsCountRadial=1, meshGroups=None, wallAnnotationGroups=None, tracksurface=None, - startProportions=None, endProportions=None, - rescaleStartDerivatives = False, rescaleEndDerivatives = False, sampleBlend = 0.0): +def createAnnulusMesh3d(nodes, mesh, nextNodeIdentifier, nextElementIdentifier, startPointsx, startPointsd1, + startPointsd2, startPointsd3, startNodeId, startDerivativesMap, endPointsx, endPointsd1, + endPointsd2, endPointsd3, endNodeId, endDerivativesMap, + forceStartLinearXi3=False, forceMidLinearXi3=False, forceEndLinearXi3=False, + maxStartThickness=None, maxEndThickness=None, useCrossDerivatives=False, + elementsCountRadial=1, meshGroups=None, wallAnnotationGroups=None, + tracksurface=None, startProportions=None, endProportions=None, + rescaleStartDerivatives=False, rescaleEndDerivatives=False, sampleBlend=0.0): """ Create an annulus mesh from a loop of start points/nodes with specified derivative mappings to a loop of end points/nodes with specified derivative mappings. @@ -94,16 +96,17 @@ def createAnnulusMesh3d(nodes, mesh, nextNodeIdentifier, nextElementIdentifier, :param mesh: The mesh to create elements in. :param nextNodeIdentifier, nextElementIdentifier: Next identifiers to use and increment. :param startPointsx, startPointsd1, startPointsd2, startPointsd3, endPointsx, endPointsd1, endPointsd2, endPointsd3: - List array[n3][n1][c] or start/point coordinates and derivatives. To linearise through the wall, pass None to d3. - If both ends are linear through the wall, interior points are linear through the wall. + List array[n3][n1][c] or start/point coordinates and derivatives. To linearise through the wall, pass None to + d3. If both ends are linear through the wall, interior points are linear through the wall. :param startNodeId, endNodeId: List array [n3][n1] of existing node identifiers to use at start/end. Pass None for argument if no nodes are specified at end. These arguments are 'all or nothing'. - :param startDerivativesMap, endDerivativesMap: List array[n3][n1] of mappings for d/dxi1, d/dxi2, d/dxi3 at start/end of form: - ( (1, -1, 0), (1, 0, 0), None ) where the first tuple means d/dxi1 = d/ds1 - d/ds2. Only 0, 1 and -1 may be used. + :param startDerivativesMap, endDerivativesMap: List array[n3][n1] of mappings for d/dxi1, d/dxi2, d/dxi3 at + start/end of form: + ( (1, -1, 0), (1, 0, 0), None ) where the first tuple means d/dxi1 = d/ds1 - d/ds2. Only 0, 1 and -1 may be + used. None means use default e.g. d/dxi2 = d/ds2. Pass None for the entire argument to use the defaults d/dxi1 = d/ds1, d/dxi2 = d/ds2, d/dxi3 = d/ds3. Pass a 4th mapping to apply to d/dxi1 on other side of node; if not supplied first mapping applies both sides. - :param nodetemplate: Full tricubic Hermite node template, can omit cross derivatives. :param forceStartLinearXi3, forceMidLinearXi3, forceEndLinearXi3: Force start, middle or end elements to be linear through the wall, even if d3 is supplied at either end. Can only use forceMidLinearXi3 only if at least one end is linear in d3. @@ -122,11 +125,11 @@ def createAnnulusMesh3d(nodes, mesh, nextNodeIdentifier, nextElementIdentifier, around as for startPoints. Values only given for tracksurface for outer layer (xi3 == 1). :param endProportions: Proportion around and along of endPoints on track surface. These vary with nodes around as for endPoints. Values only given for tracksurface for outer layer (xi3 == 1). - :param rescaleStartDerivatives, rescaleEndDerivatives: Optional flags to compute and multiply additional scale factors - on start, end or both radial derivatives to fit arc length, needed if derivatives are of the wrong scale for the radial - distances and the chosen elementsCountRadial. If either is True, derivatives and sampled radial nodes are spaced for a - gradual change of derivative from that at the other end. If both are True, scaling is set to give even sampling and arc - length derivatives. + :param rescaleStartDerivatives, rescaleEndDerivatives: Optional flags to compute and multiply additional scale + factors on start, end or both radial derivatives to fit arc length, needed if derivatives are of the wrong scale + for the radial distances and the chosen elementsCountRadial. If either is True, derivatives and sampled radial + nodes are spaced for a gradual change of derivative from that at the other end. If both are True, scaling is set to + give even sampling and arclength derivatives. :param sampleBlend: Real value varying from 0.0 to 1.0 controlling weighting of start and end derivatives when interpolating extra points in-between, where 0.0 = sample with equal end derivatives, and 1.0 = proportional to current magnitudes, interpolated in between. @@ -138,44 +141,51 @@ def createAnnulusMesh3d(nodes, mesh, nextNodeIdentifier, nextElementIdentifier, midLinearXi3 = (startLinearXi3 and endLinearXi3) or ((startLinearXi3 or endLinearXi3) and forceMidLinearXi3) # get list whether each row of nodes in elements is linear in Xi3 # this is for element use; start/end nodes may have d3 even if element is linear - rowLinearXi3 = [ startLinearXi3 ] + [ midLinearXi3 ]*(elementsCountRadial - 1) + [ endLinearXi3 ] + rowLinearXi3 = [startLinearXi3] + [midLinearXi3] * (elementsCountRadial - 1) + [endLinearXi3] assert (not useCrossDerivatives) or ((not startDerivativesMap) and (not endDerivativesMap)), \ 'createAnnulusMesh3d: Cannot use cross derivatives with derivatives map' nodesCountWall = len(startPointsx) assert (len(startPointsd1) == nodesCountWall) and (len(startPointsd2) == nodesCountWall) and \ (startLinearXi3 or (len(startPointsd3) == nodesCountWall)) and \ - (len(endPointsx) == nodesCountWall) and (len(endPointsd1) == nodesCountWall) and (len(endPointsd2) == nodesCountWall) and \ - (endLinearXi3 or (len(endPointsd3) == nodesCountWall)) and \ - ((startNodeId is None) or (len(startNodeId) == nodesCountWall)) and \ - ((endNodeId is None) or (len(endNodeId) == nodesCountWall)) and \ - ((startDerivativesMap is None) or (len(startDerivativesMap) == nodesCountWall)) and \ - ((endDerivativesMap is None) or (len(endDerivativesMap) == nodesCountWall)), \ + (len(endPointsx) == nodesCountWall) and (len(endPointsd1) == nodesCountWall) and \ + (len(endPointsd2) == nodesCountWall) and (endLinearXi3 or (len(endPointsd3) == nodesCountWall)) and \ + ((startNodeId is None) or (len(startNodeId) == nodesCountWall)) and \ + ((endNodeId is None) or (len(endNodeId) == nodesCountWall)) and \ + ((startDerivativesMap is None) or (len(startDerivativesMap) == nodesCountWall)) and \ + ((endDerivativesMap is None) or (len(endDerivativesMap) == nodesCountWall)),\ 'createAnnulusMesh3d: Mismatch in number of layers through wall' elementsCountAround = nodesCountAround = len(startPointsx[0]) assert (nodesCountAround > 1), 'createAnnulusMesh3d: Invalid number of points/nodes around annulus' for n3 in range(nodesCountWall): - assert (len(startPointsx[n3]) == nodesCountAround) and (len(startPointsd1[n3]) == nodesCountAround) and (len(startPointsd2[n3]) == nodesCountAround) and \ - (startLinearXi3 or (len(startPointsd3[n3]) == nodesCountAround)) and \ - (len(endPointsx[n3]) == nodesCountAround) and (len(endPointsd1[n3]) == nodesCountAround) and (len(endPointsd2[n3]) == nodesCountAround) and \ - (endLinearXi3 or (len(endPointsd3[n3]) == nodesCountAround)) and \ - ((startNodeId is None) or (len(startNodeId[n3]) == nodesCountAround)) and \ - ((endNodeId is None) or (len(endNodeId[n3]) == nodesCountAround)) and \ - ((startDerivativesMap is None) or (len(startDerivativesMap[n3]) == nodesCountAround)) and \ - ((endDerivativesMap is None) or (len(endDerivativesMap[n3]) == nodesCountAround)), \ + assert (len(startPointsx[n3]) == nodesCountAround) and (len(startPointsd1[n3]) == nodesCountAround) and \ + (len(startPointsd2[n3]) == nodesCountAround) and \ + (startLinearXi3 or (len(startPointsd3[n3]) == nodesCountAround)) and\ + (len(endPointsx[n3]) == nodesCountAround) and (len(endPointsd1[n3]) == nodesCountAround) and \ + (len(endPointsd2[n3]) == nodesCountAround) and \ + (endLinearXi3 or (len(endPointsd3[n3]) == nodesCountAround)) and \ + ((startNodeId is None) or (len(startNodeId[n3]) == nodesCountAround)) and\ + ((endNodeId is None) or (len(endNodeId[n3]) == nodesCountAround)) and \ + ((startDerivativesMap is None) or (len(startDerivativesMap[n3]) == nodesCountAround)) and \ + ((endDerivativesMap is None) or (len(endDerivativesMap[n3]) == nodesCountAround)), \ 'createAnnulusMesh3d: Mismatch in number of points/nodes in layers through wall' rowMeshGroups = meshGroups if meshGroups: assert isinstance(meshGroups, Sequence), 'createAnnulusMesh3d: Mesh groups is not a sequence' if (len(meshGroups) == 0) or (not isinstance(meshGroups[0], Sequence)): - rowMeshGroups = [ meshGroups ]*elementsCountRadial + rowMeshGroups = [meshGroups] * elementsCountRadial else: - assert len(meshGroups) == elementsCountRadial, 'createAnnulusMesh3d: Length of meshGroups sequence does not equal elementsCountRadial' + assert len(meshGroups) == elementsCountRadial, \ + 'createAnnulusMesh3d: Length of meshGroups sequence does not equal elementsCountRadial' if wallAnnotationGroups: - assert len(wallAnnotationGroups) == nodesCountWall - 1, 'createAnnulusMesh3d: Length of wallAnnotationGroups sequence does not equal elementsCountThroughWall' + assert len(wallAnnotationGroups) == nodesCountWall - 1, \ + 'createAnnulusMesh3d: Length of wallAnnotationGroups sequence does not equal elementsCountThroughWall' if tracksurface: - assert startProportions and endProportions, 'createAnnulusMesh3d: Missing start and/or end proportions for use with tracksurface' - assert len(startProportions) == nodesCountAround, 'createAnnulusMesh3d: Length of startProportions does not equal nodesCountAround' - assert len(endProportions) == nodesCountAround, 'createAnnulusMesh3d: Length of endProportions does not equal nodesCountAround' + assert startProportions and endProportions, \ + 'createAnnulusMesh3d: Missing start and/or end proportions for use with tracksurface' + assert len(startProportions) == nodesCountAround, \ + 'createAnnulusMesh3d: Length of startProportions does not equal nodesCountAround' + assert len(endProportions) == nodesCountAround, \ + 'createAnnulusMesh3d: Length of endProportions does not equal nodesCountAround' fm = mesh.getFieldmodule() fm.beginChange() @@ -191,20 +201,26 @@ def createAnnulusMesh3d(nodes, mesh, nextNodeIdentifier, nextElementIdentifier, # Find total wall thickness thicknessProportions = [] thicknesses = [] - thicknesses.append([vector.magnitude([(startPointsx[nodesCountWall - 1][n1][c] - startPointsx[0][n1][c]) for c in range(3)]) for n1 in range(nodesCountAround)]) + thicknesses.append([vector.magnitude([(startPointsx[nodesCountWall - 1][n1][c] - startPointsx[0][n1][c]) + for c in range(3)]) for n1 in range(nodesCountAround)]) for n2 in range(1, elementsCountRadial): thicknesses.append([None] * nodesCountAround) - thicknesses.append([vector.magnitude([(endPointsx[nodesCountWall - 1][n1][c] - endPointsx[0][n1][c]) for c in range(3)]) for n1 in range(nodesCountAround)]) + thicknesses.append([vector.magnitude([(endPointsx[nodesCountWall - 1][n1][c] - endPointsx[0][n1][c]) + for c in range(3)]) for n1 in range(nodesCountAround)]) for n3 in range(nodesCountWall): - px [n3] = [ startPointsx [n3], endPointsx [n3] ] - pd1[n3] = [ startPointsd1[n3], endPointsd1[n3] ] - pd2[n3] = [ startPointsd2[n3], endPointsd2[n3] ] - pd3[n3] = [ startPointsd3[n3] if (startPointsd3 is not None) else None, \ - endPointsd3[n3] if (endPointsd3 is not None) else None ] - - startThicknessList = [vector.magnitude([(startPointsx[n3][n1][c] - startPointsx[n3 - (1 if n3 > 0 else 0)][n1][c]) for c in range(3)]) for n1 in range(len(startPointsx[n3]))] - endThicknessList = [vector.magnitude([(endPointsx[n3][n1][c] - endPointsx[n3 - (1 if n3 > 0 else 0)][n1][c]) for c in range(3)]) for n1 in range(len(endPointsx[n3]))] + px[n3] = [startPointsx[n3], endPointsx[n3]] + pd1[n3] = [startPointsd1[n3], endPointsd1[n3]] + pd2[n3] = [startPointsd2[n3], endPointsd2[n3]] + pd3[n3] = [startPointsd3[n3] if (startPointsd3 is not None) else None, + endPointsd3[n3] if (endPointsd3 is not None) else None] + + startThicknessList = \ + [vector.magnitude([(startPointsx[n3][n1][c] - startPointsx[n3 - (1 if n3 > 0 else 0)][n1][c]) + for c in range(3)]) for n1 in range(len(startPointsx[n3]))] + endThicknessList = \ + [vector.magnitude([(endPointsx[n3][n1][c] - endPointsx[n3 - (1 if n3 > 0 else 0)][n1][c]) + for c in range(3)]) for n1 in range(len(endPointsx[n3]))] thicknessList = [startThicknessList, endThicknessList] # thickness of each layer startThicknessProportions = [thicknessList[0][c] / thicknesses[0][c] for c in range(nodesCountAround)] @@ -212,17 +228,17 @@ def createAnnulusMesh3d(nodes, mesh, nextNodeIdentifier, nextElementIdentifier, thicknessProportions.append([startThicknessProportions, endThicknessProportions]) if rescaleStartDerivatives: - scaleFactorMapStart = [ [] for n3 in range(nodesCountWall) ] + scaleFactorMapStart = [[] for n3 in range(nodesCountWall)] if rescaleEndDerivatives: - scaleFactorMapEnd = [ [] for n3 in range(nodesCountWall) ] + scaleFactorMapEnd = [[] for n3 in range(nodesCountWall)] # following code adds in-between points, but also handles rescaling for 1 radial element for n3 in range(nodesCountWall): for n2 in range(1, elementsCountRadial): - px [n3].insert(n2, [ None ]*nodesCountAround) - pd1[n3].insert(n2, [ None ]*nodesCountAround) - pd2[n3].insert(n2, [ None ]*nodesCountAround) - pd3[n3].insert(n2, None if midLinearXi3 else [ None ]*nodesCountAround) + px[n3].insert(n2, [None] * nodesCountAround) + pd1[n3].insert(n2, [None] * nodesCountAround) + pd2[n3].insert(n2, [None] * nodesCountAround) + pd3[n3].insert(n2, None if midLinearXi3 else [None] * nodesCountAround) thicknessProportions[n3].insert(n2, [None] * nodesCountAround) if maxStartThickness: @@ -233,34 +249,39 @@ def createAnnulusMesh3d(nodes, mesh, nextNodeIdentifier, nextElementIdentifier, thicknesses[-1][n1] = min(thicknesses[-1][n1], maxEndThickness) n3 = nodesCountWall - 1 for n1 in range(nodesCountAround): - ax = startPointsx[n3][n1] - ad1, ad2 = getMappedD1D2([ startPointsd1[n3][n1], startPointsd2[n3][n1] ] + ([ startPointsd3[n3][n1] ] if startPointsd3 else []), + ax = startPointsx[n3][n1] + ad1, ad2 = getMappedD1D2([startPointsd1[n3][n1], startPointsd2[n3][n1]] + + ([startPointsd3[n3][n1]] if startPointsd3 else []), startDerivativesMap[n3][n1] if startDerivativesMap else None) - bx = endPointsx[n3][n1] - bd1, bd2 = getMappedD1D2([ endPointsd1[n3][n1], endPointsd2[n3][n1] ] + ([ endPointsd3[n3][n1] ] if endPointsd3 else []), + bx = endPointsx[n3][n1] + bd1, bd2 = getMappedD1D2([endPointsd1[n3][n1], endPointsd2[n3][n1]] + + ([endPointsd3[n3][n1]] if endPointsd3 else []), endDerivativesMap[n3][n1] if endDerivativesMap else None) # sample between start and end points and derivatives # scaling end derivatives to arc length gives even curvature along the curve aMag = vector.magnitude(ad2) bMag = vector.magnitude(bd2) - ad2Scaled = vector.setMagnitude(ad2, 0.5*((1.0 + sampleBlend)*aMag + (1.0 - sampleBlend)*bMag)) - bd2Scaled = vector.setMagnitude(bd2, 0.5*((1.0 + sampleBlend)*bMag + (1.0 - sampleBlend)*aMag)) + ad2Scaled = vector.setMagnitude(ad2, 0.5 * ((1.0 + sampleBlend) * aMag + (1.0 - sampleBlend) * bMag)) + bd2Scaled = vector.setMagnitude(bd2, 0.5 * ((1.0 + sampleBlend) * bMag + (1.0 - sampleBlend) * aMag)) scaling = interp.computeCubicHermiteDerivativeScaling(ax, ad2Scaled, bx, bd2Scaled) - ad2Scaled = [ d*scaling for d in ad2Scaled ] - bd2Scaled = [ d*scaling for d in bd2Scaled ] + ad2Scaled = [d * scaling for d in ad2Scaled] + bd2Scaled = [d * scaling for d in bd2Scaled] derivativeMagnitudeStart = None if rescaleStartDerivatives else vector.magnitude(ad2) derivativeMagnitudeEnd = None if rescaleEndDerivatives else vector.magnitude(bd2) if tracksurface: mx, md2, md1, md3, mProportions = \ tracksurface.createHermiteCurvePoints(startProportions[n1][0], startProportions[n1][1], - endProportions[n1][0], endProportions[n1][1], - elementsCountRadial, - derivativeStart=[ d/elementsCountRadial for d in ad2Scaled ], - derivativeEnd=[ d/elementsCountRadial for d in bd2Scaled ]) - mx, md2, md1 = tracksurface.resampleHermiteCurvePointsSmooth(mx, md2, md1, md3, mProportions, derivativeMagnitudeStart, derivativeMagnitudeEnd)[0:3] + endProportions[n1][0], endProportions[n1][1], elementsCountRadial, + derivativeStart=[d / elementsCountRadial for d in ad2Scaled], + derivativeEnd=[d / elementsCountRadial for d in bd2Scaled]) + mx, md2, md1 = \ + tracksurface.resampleHermiteCurvePointsSmooth(mx, md2, md1, md3, mProportions, + derivativeMagnitudeStart, derivativeMagnitudeEnd)[0:3] # interpolate thicknesses using xi calculated from radial arclength distances to points - arcLengthInsideToRadialPoint = [ 0.0 ] + [ interp.getCubicHermiteArcLength(mx[n2], md2[n2], mx[n2 + 1], md2[n2 + 1]) for n2 in range(elementsCountRadial) ] + arcLengthInsideToRadialPoint = \ + [0.0] + [interp.getCubicHermiteArcLength(mx[n2], md2[n2], mx[n2 + 1], md2[n2 + 1]) + for n2 in range(elementsCountRadial)] arclengthInsideToOutside = sum(arcLengthInsideToRadialPoint) thi = [] for n2 in range(elementsCountRadial + 1): @@ -271,27 +292,30 @@ def createAnnulusMesh3d(nodes, mesh, nextNodeIdentifier, nextElementIdentifier, thiProportionRadial = [] for n2 in range(elementsCountRadial + 1): xi2 = arcLengthInsideToRadialPoint[n2 - 1] / arclengthInsideToOutside - thiProportionRadial.append(thicknessProportions[m3][-1][n1] * xi2 + thicknessProportions[m3][0][n1] * (1.0 - xi2)) + thiProportionRadial.append(thicknessProportions[m3][-1][n1] * xi2 + + thicknessProportions[m3][0][n1] * (1.0 - xi2)) thiProportion.append(thiProportionRadial) else: - mx, md2, me, mxi = interp.sampleCubicHermiteCurvesSmooth([ ax, bx ], [ ad2Scaled, bd2Scaled ], elementsCountRadial, - derivativeMagnitudeStart, derivativeMagnitudeEnd)[0:4] - md1 = interp.interpolateSampleLinear([ ad1, bd1 ], me, mxi) - thi = interp.interpolateSampleLinear([ thicknesses[0][n1], thicknesses[-1][n1] ], me, mxi) + mx, md2, me, mxi = interp.sampleCubicHermiteCurvesSmooth([ax, bx], [ad2Scaled, bd2Scaled], + elementsCountRadial, derivativeMagnitudeStart, + derivativeMagnitudeEnd)[0:4] + md1 = interp.interpolateSampleLinear([ad1, bd1], me, mxi) + thi = interp.interpolateSampleLinear([thicknesses[0][n1], thicknesses[-1][n1]], me, mxi) thiProportion = [] for m3 in range(nodesCountWall): - thiProportion.append(interp.interpolateSampleLinear([thicknessProportions[m3][0][n1], thicknessProportions[m3][-1][n1]], me, mxi)) + thiProportion.append(interp.interpolateSampleLinear([thicknessProportions[m3][0][n1], + thicknessProportions[m3][-1][n1]], me, mxi)) # set scalefactors if rescaling, make same on inside for now if rescaleStartDerivatives: - scaleFactor = vector.magnitude(md2[0])/vector.magnitude(ad2) + scaleFactor = vector.magnitude(md2[0]) / vector.magnitude(ad2) scaleFactorMapStart[n3].append(scaleFactor) if rescaleEndDerivatives: - scaleFactor = vector.magnitude(md2[-1])/vector.magnitude(bd2) + scaleFactor = vector.magnitude(md2[-1]) / vector.magnitude(bd2) scaleFactorMapEnd[n3].append(scaleFactor) for n2 in range(1, elementsCountRadial): - px [n3][n2][n1] = mx [n2] + px[n3][n2][n1] = mx[n2] pd1[n3][n2][n1] = md1[n2] pd2[n3][n2][n1] = md2[n2] thicknesses[n2][n1] = thi[n2] @@ -310,7 +334,9 @@ def createAnnulusMesh3d(nodes, mesh, nextNodeIdentifier, nextElementIdentifier, # now get inner positions from normal and thickness, derivatives from curvature for n2 in range(1, elementsCountRadial): # first smooth derivative 1 around outer loop - pd1[-1][n2] = interp.smoothCubicHermiteDerivativesLoop(px[-1][n2], pd1[-1][n2], magnitudeScalingMode=interp.DerivativeScalingMode.HARMONIC_MEAN) + pd1[-1][n2] = \ + interp.smoothCubicHermiteDerivativesLoop(px[-1][n2], pd1[-1][n2], + magnitudeScalingMode=interp.DerivativeScalingMode.HARMONIC_MEAN) for n3 in range(0, nodesCountWall - 1): for n1 in range(nodesCountAround): @@ -323,44 +349,54 @@ def createAnnulusMesh3d(nodes, mesh, nextNodeIdentifier, nextElementIdentifier, n1m = n1 - 1 n1p = (n1 + 1) % nodesCountAround curvature = 0.5 * ( - interp.getCubicHermiteCurvature(px[-1][n2][n1m], pd1[-1][n2][n1m], px[-1][n2][n1], pd1[-1][n2][n1], normal, 1.0) + - interp.getCubicHermiteCurvature(px[-1][n2][n1], pd1[-1][n2][n1], px[-1][n2][n1p], pd1[-1][n2][n1p], normal, 0.0)) + interp.getCubicHermiteCurvature(px[-1][n2][n1m], pd1[-1][n2][n1m], + px[-1][n2][n1], pd1[-1][n2][n1], normal, 1.0) + + interp.getCubicHermiteCurvature(px[-1][n2][n1], pd1[-1][n2][n1], px[-1][n2][n1p], + pd1[-1][n2][n1p], normal, 0.0)) factor = 1.0 + curvature * thickness pd1[n3][n2][n1] = [factor * d for d in pd1[-1][n2][n1]] # calculate inner d2 from curvature radially n2m = n2 - 1 n2p = n2 + 1 curvature = 0.5 * ( - interp.getCubicHermiteCurvature(px[-1][n2m][n1], pd2[-1][n2m][n1], px[-1][n2][n1], pd2[-1][n2][n1], normal, 1.0) + - interp.getCubicHermiteCurvature(px[-1][n2][n1], pd2[-1][n2][n1], px[-1][n2p][n1], pd2[-1][n2p][n1], normal, 0.0)) + interp.getCubicHermiteCurvature(px[-1][n2m][n1], pd2[-1][n2m][n1], + px[-1][n2][n1], pd2[-1][n2][n1], normal, 1.0) + + interp.getCubicHermiteCurvature(px[-1][n2][n1], pd2[-1][n2][n1], + px[-1][n2p][n1], pd2[-1][n2p][n1], normal, 0.0)) factor = 1.0 + curvature * thickness pd2[n3][n2][n1] = [factor * d for d in pd2[-1][n2][n1]] d2Scaled = [factor * d for d in pd2[-1][n2][n1]] if vector.dotproduct(vector.normalise(pd2[-1][n2][n1]), vector.normalise(d2Scaled)) == -1: pd2[n3][n2][n1] = [-factor * d for d in pd2[-1][n2][n1]] if not midLinearXi3: - pd3[n3][n2][n1] = pd3[-1][n2][n1] = [d * thicknesses[n2][n1] * thicknessProportions[n3 + 1][n2][n1] for d in normal] + pd3[n3][n2][n1] = pd3[-1][n2][n1] = \ + [d * thicknesses[n2][n1] * thicknessProportions[n3 + 1][n2][n1] for d in normal] # smooth derivative 1 around inner loop - pd1[n3][n2] = interp.smoothCubicHermiteDerivativesLoop(px[n3][n2], pd1[n3][n2], magnitudeScalingMode=interp.DerivativeScalingMode.HARMONIC_MEAN) + pd1[n3][n2] = interp.smoothCubicHermiteDerivativesLoop(px[n3][n2], pd1[n3][n2], + magnitudeScalingMode=interp.DerivativeScalingMode. + HARMONIC_MEAN) for n3 in range(0, nodesCountWall): # smooth derivative 2 radially/along annulus for n1 in range(nodesCountAround): - mx = [ px [n3][n2][n1] for n2 in range(elementsCountRadial + 1) ] - md2 = [ pd2[n3][n2][n1] for n2 in range(elementsCountRadial + 1) ] + mx = [px[n3][n2][n1] for n2 in range(elementsCountRadial + 1)] + md2 = [pd2[n3][n2][n1] for n2 in range(elementsCountRadial + 1)] # replace mapped start/end d2 - md2[0] = getMappedD1D2([ startPointsd1[n3][n1], startPointsd2[n3][n1] ] + ([ startPointsd3[n3][n1] ] if startPointsd3 else []), + md2[0] = getMappedD1D2([startPointsd1[n3][n1], startPointsd2[n3][n1]] + + ([startPointsd3[n3][n1]] if startPointsd3 else []), startDerivativesMap[n3][n1] if startDerivativesMap else None)[1] - md2[-1] = getMappedD1D2([ endPointsd1[n3][n1], endPointsd2[n3][n1] ] + ([ endPointsd3[n3][n1] ] if endPointsd3 else []), + md2[-1] = getMappedD1D2([endPointsd1[n3][n1], endPointsd2[n3][n1]] + + ([endPointsd3[n3][n1]] if endPointsd3 else []), endDerivativesMap[n3][n1] if endDerivativesMap else None)[1] - sd2 = interp.smoothCubicHermiteDerivativesLine(mx, md2, fixAllDirections = True, - fixStartDerivative = not rescaleStartDerivatives, - fixStartDirection = rescaleStartDerivatives, - fixEndDerivative = not rescaleEndDerivatives, - fixEndDirection = rescaleEndDerivatives, - magnitudeScalingMode = interp.DerivativeScalingMode.HARMONIC_MEAN) + sd2 = interp.smoothCubicHermiteDerivativesLine(mx, md2, fixAllDirections=True, + fixStartDerivative=not rescaleStartDerivatives, + fixStartDirection=rescaleStartDerivatives, + fixEndDerivative=not rescaleEndDerivatives, + fixEndDirection=rescaleEndDerivatives, + magnitudeScalingMode=interp.DerivativeScalingMode. + HARMONIC_MEAN) if rescaleStartDerivatives: scaleFactor = vector.magnitude(sd2[0]) / vector.magnitude(md2[0]) scaleFactorMapStart[n3].append(scaleFactor) @@ -442,7 +478,8 @@ def createAnnulusMesh3d(nodes, mesh, nextNodeIdentifier, nextElementIdentifier, mapStartLinearDerivativeXi3 = nonlinearXi3 and rowLinearXi3[e2] mapEndDerivatives = (e2 == (elementsCountRadial - 1)) and (endDerivativesMap or rescaleEndDerivatives) mapEndLinearDerivativeXi3 = nonlinearXi3 and rowLinearXi3[e2 + 1] - mapDerivatives = mapStartDerivatives or mapStartLinearDerivativeXi3 or mapEndDerivatives or mapEndLinearDerivativeXi3 + mapDerivatives = mapStartDerivatives or mapStartLinearDerivativeXi3 or \ + mapEndDerivatives or mapEndLinearDerivativeXi3 for e3 in range(elementsCountWall): for e1 in range(elementsCountAround): en = (e1 + 1) % elementsCountAround @@ -485,17 +522,25 @@ def createAnnulusMesh3d(nodes, mesh, nextNodeIdentifier, nextElementIdentifier, n3Idx = n3 + e3 if mapStartDerivatives and rescaleStartDerivatives: for i in range(2): - derivativesMap = (startDerivativesMap[n3Idx][e1][1] if (i == 0) else startDerivativesMap[n3Idx][en][1]) if startDerivativesMap else None - nodeScaleFactorIds.append(getQuadrantID(derivativesMap if derivativesMap else (0, 1, 0))) + derivativesMap = (startDerivativesMap[n3Idx][e1][1] if (i == 0) else + startDerivativesMap[n3Idx][en][1]) if startDerivativesMap else None + nodeScaleFactorIds.append(getQuadrantID(derivativesMap if derivativesMap else + (0, 1, 0))) if mapEndDerivatives and rescaleEndDerivatives: for i in range(2): - derivativesMap = (endDerivativesMap[n3Idx][e1][1] if (i == 0) else endDerivativesMap[n3Idx][en][1]) if endDerivativesMap else None - nodeScaleFactorIds.append(getQuadrantID(derivativesMap if derivativesMap else (0, 1, 0))) + derivativesMap = (endDerivativesMap[n3Idx][e1][1] if (i == 0) else + endDerivativesMap[n3Idx][en][1]) if endDerivativesMap else None + nodeScaleFactorIds.append(getQuadrantID(derivativesMap if derivativesMap else + (0, 1, 0))) setEftScaleFactorIds(eft1, [1] if scaleMinus1 else [], nodeScaleFactorIds) firstNodeScaleFactorIndex = 2 if scaleMinus1 else 1 - firstStartNodeScaleFactorIndex = firstNodeScaleFactorIndex if (mapStartDerivatives and rescaleStartDerivatives) else None - firstEndNodeScaleFactorIndex = (firstNodeScaleFactorIndex + (2 if firstStartNodeScaleFactorIndex else 0)) if (mapEndDerivatives and rescaleEndDerivatives) else None - layerNodeScaleFactorIndexOffset = 4 if (firstStartNodeScaleFactorIndex and firstEndNodeScaleFactorIndex) else 2 + firstStartNodeScaleFactorIndex = \ + firstNodeScaleFactorIndex if (mapStartDerivatives and rescaleStartDerivatives) else None + firstEndNodeScaleFactorIndex = \ + (firstNodeScaleFactorIndex + (2 if firstStartNodeScaleFactorIndex else 0)) \ + if (mapEndDerivatives and rescaleEndDerivatives) else None + layerNodeScaleFactorIndexOffset = \ + 4 if (firstStartNodeScaleFactorIndex and firstEndNodeScaleFactorIndex) else 2 if scaleMinus1: scaleFactors.append(-1.0) for n3 in range(2): @@ -508,64 +553,92 @@ def createAnnulusMesh3d(nodes, mesh, nextNodeIdentifier, nextElementIdentifier, scaleFactors.append(scaleFactorMapEnd[n3Idx][en]) if mapStartLinearDerivativeXi3: - eftFactory.setEftLinearDerivative2(eft1, [1, 5, 2, 6], Node.VALUE_LABEL_D_DS3, [Node.VALUE_LABEL_D2_DS1DS3]) + eftFactory.setEftLinearDerivative2(eft1, [1, 5, 2, 6], Node.VALUE_LABEL_D_DS3, + [Node.VALUE_LABEL_D2_DS1DS3]) if mapStartDerivatives: for i in range(2): lns = [1, 5] if (i == 0) else [2, 6] for n3 in range(2): n3Idx = n3 + e3 - derivativesMap = (startDerivativesMap[n3Idx][e1] if (i == 0) else startDerivativesMap[n3Idx][en]) if startDerivativesMap else (None, None, None) + derivativesMap = \ + (startDerivativesMap[n3Idx][e1] if (i == 0) else startDerivativesMap[n3Idx][en]) \ + if startDerivativesMap else (None, None, None) # handle different d1 on each side of node - d1Map = derivativesMap[0] if ((i == 1) or (len(derivativesMap) < 4)) else derivativesMap[3] + d1Map = \ + derivativesMap[0] if ((i == 1) or (len(derivativesMap) < 4)) else derivativesMap[3] d2Map = derivativesMap[1] if derivativesMap[1] else (0, 1, 0) d3Map = derivativesMap[2] # use temporary to safely swap DS1 and DS2: ln = [lns[n3]] if d1Map: - remapEftNodeValueLabel(eft1, ln, Node.VALUE_LABEL_D_DS1, [(Node.VALUE_LABEL_D2_DS1DS2, [])]) + remapEftNodeValueLabel(eft1, ln, Node.VALUE_LABEL_D_DS1, + [(Node.VALUE_LABEL_D2_DS1DS2, [])]) if d3Map: - remapEftNodeValueLabel(eft1, ln, Node.VALUE_LABEL_D_DS3, [(Node.VALUE_LABEL_D2_DS2DS3, [])]) + remapEftNodeValueLabel(eft1, ln, Node.VALUE_LABEL_D_DS3, + [(Node.VALUE_LABEL_D2_DS2DS3, [])]) if d2Map: remapEftNodeValueLabel(eft1, ln, Node.VALUE_LABEL_D_DS2, derivativeSignsToExpressionTerms((Node.VALUE_LABEL_D_DS1, Node.VALUE_LABEL_D_DS2, - Node.VALUE_LABEL_D_DS3), d2Map, + Node.VALUE_LABEL_D_DS3), + d2Map, (firstStartNodeScaleFactorIndex + i + n3 * layerNodeScaleFactorIndexOffset) if rescaleStartDerivatives else None)) if d1Map: - remapEftNodeValueLabel(eft1, ln, Node.VALUE_LABEL_D2_DS1DS2, \ - derivativeSignsToExpressionTerms((Node.VALUE_LABEL_D_DS1, Node.VALUE_LABEL_D_DS2, Node.VALUE_LABEL_D_DS3), d1Map)) + remapEftNodeValueLabel(eft1, ln, Node.VALUE_LABEL_D2_DS1DS2, + derivativeSignsToExpressionTerms((Node.VALUE_LABEL_D_DS1, + Node.VALUE_LABEL_D_DS2, + Node.VALUE_LABEL_D_DS3), + d1Map)) if d3Map: - remapEftNodeValueLabel(eft1, ln, Node.VALUE_LABEL_D2_DS2DS3, \ - derivativeSignsToExpressionTerms((Node.VALUE_LABEL_D_DS1, Node.VALUE_LABEL_D_DS2, Node.VALUE_LABEL_D_DS3), d3Map)) + remapEftNodeValueLabel(eft1, ln, Node.VALUE_LABEL_D2_DS2DS3, + derivativeSignsToExpressionTerms((Node.VALUE_LABEL_D_DS1, + Node.VALUE_LABEL_D_DS2, + Node.VALUE_LABEL_D_DS3), + d3Map)) if mapEndLinearDerivativeXi3: - eftFactory.setEftLinearDerivative2(eft1, [3, 7, 4, 8], Node.VALUE_LABEL_D_DS3, [Node.VALUE_LABEL_D2_DS1DS3]) + eftFactory.setEftLinearDerivative2(eft1, [3, 7, 4, 8], Node.VALUE_LABEL_D_DS3, + [Node.VALUE_LABEL_D2_DS1DS3]) if mapEndDerivatives: for i in range(2): lns = [3, 7] if (i == 0) else [4, 8] for n3 in range(2): n3Idx = n3 + e3 - derivativesMap = (endDerivativesMap[n3Idx][e1] if (i == 0) else endDerivativesMap[n3Idx][en]) if endDerivativesMap else (None, None, None) + derivativesMap = \ + (endDerivativesMap[n3Idx][e1] if (i == 0) else endDerivativesMap[n3Idx][en]) \ + if endDerivativesMap else (None, None, None) # handle different d1 on each side of node - d1Map = derivativesMap[0] if ((i == 1) or (len(derivativesMap) < 4)) else derivativesMap[3] + d1Map = derivativesMap[0] if ((i == 1) or (len(derivativesMap) < 4)) else \ + derivativesMap[3] d2Map = derivativesMap[1] if derivativesMap[1] else (0, 1, 0) d3Map = derivativesMap[2] # use temporary to safely swap DS1 and DS2: ln = [lns[n3]] if d1Map: - remapEftNodeValueLabel(eft1, ln, Node.VALUE_LABEL_D_DS1, [(Node.VALUE_LABEL_D2_DS1DS2, [])]) + remapEftNodeValueLabel(eft1, ln, Node.VALUE_LABEL_D_DS1, + [(Node.VALUE_LABEL_D2_DS1DS2, [])]) if d3Map: - remapEftNodeValueLabel(eft1, ln, Node.VALUE_LABEL_D_DS3, [(Node.VALUE_LABEL_D2_DS2DS3, [])]) + remapEftNodeValueLabel(eft1, ln, Node.VALUE_LABEL_D_DS3, + [(Node.VALUE_LABEL_D2_DS2DS3, [])]) if d2Map: remapEftNodeValueLabel(eft1, ln, Node.VALUE_LABEL_D_DS2, derivativeSignsToExpressionTerms((Node.VALUE_LABEL_D_DS1, Node.VALUE_LABEL_D_DS2, - Node.VALUE_LABEL_D_DS3), d2Map, + Node.VALUE_LABEL_D_DS3), + d2Map, (firstEndNodeScaleFactorIndex + i + n3 * layerNodeScaleFactorIndexOffset) if rescaleEndDerivatives else None)) if d1Map: - remapEftNodeValueLabel(eft1, ln, Node.VALUE_LABEL_D2_DS1DS2, derivativeSignsToExpressionTerms((Node.VALUE_LABEL_D_DS1, Node.VALUE_LABEL_D_DS2, Node.VALUE_LABEL_D_DS3), d1Map)) + remapEftNodeValueLabel(eft1, ln, Node.VALUE_LABEL_D2_DS1DS2, + derivativeSignsToExpressionTerms((Node.VALUE_LABEL_D_DS1, + Node.VALUE_LABEL_D_DS2, + Node.VALUE_LABEL_D_DS3), + d1Map)) if d3Map: - remapEftNodeValueLabel(eft1, ln, Node.VALUE_LABEL_D2_DS2DS3, derivativeSignsToExpressionTerms((Node.VALUE_LABEL_D_DS1, Node.VALUE_LABEL_D_DS2, Node.VALUE_LABEL_D_DS3), d3Map)) + remapEftNodeValueLabel(eft1, ln, Node.VALUE_LABEL_D2_DS2DS3, + derivativeSignsToExpressionTerms((Node.VALUE_LABEL_D_DS1, + Node.VALUE_LABEL_D_DS2, + Node.VALUE_LABEL_D_DS3), + d3Map)) elementtemplateX.defineField(coordinates, -1, eft1) elementtemplate1 = elementtemplateX @@ -577,7 +650,8 @@ def createAnnulusMesh3d(nodes, mesh, nextNodeIdentifier, nextElementIdentifier, result2 = element.setNodesByIdentifier(eft1, nids) if scaleFactors: result3 = element.setScaleFactors(eft1, scaleFactors) - # print('create element annulus', element.isValid(), elementIdentifier, eft1.validate(), result2, result3 if scaleFactors else None, nids) + # print('create element annulus', element.isValid(), elementIdentifier, eft1.validate(), + # result2, result3 if scaleFactors else None, nids) elementIdentifier += 1 if rowMeshGroups: @@ -593,6 +667,7 @@ def createAnnulusMesh3d(nodes, mesh, nextNodeIdentifier, nextElementIdentifier, return nodeIdentifier, elementIdentifier + def getQuadrantID(d): """ Returns a scale factor ID based on direction of the derivative. Index starts @@ -602,7 +677,8 @@ def getQuadrantID(d): """ maps = [(1, 0, 0), (1, 1, 0), (0, 1, 0), (-1, 1, 0), (-1, 0, 0), (-1, -1, 0), (0, -1, 0), (1, -1, 0), # d3 = 0 (1, 0, 1), (1, 1, 1), (0, 1, 1), (-1, 1, 1), (-1, 0, 1), (-1, -1, 1), (0, -1, 1), (1, -1, 1), (0, 0, 1), # d3 = 1 - (1, 0, -1), (1, 1, -1), (0, 1, -1), (-1, 1, -1), (-1, 0, -1), (-1, -1, -1), (0, -1, -1), (1, -1, -1), (0, 0, -1)] # d3 = -1 + (1, 0, -1), (1, 1, -1), (0, 1, -1), (-1, 1, -1), (-1, 0, -1), (-1, -1, -1), (0, -1, -1), (1, -1, -1), + (0, 0, -1)] # d3 = -1 for i in range(len(maps)): if d == maps[i]: return i + 1 diff --git a/tests/test_bladder.py b/tests/test_bladder.py index 5054e1cc..9d51085d 100644 --- a/tests/test_bladder.py +++ b/tests/test_bladder.py @@ -24,7 +24,7 @@ def test_bladderurethra1(self): """ scaffold = MeshType_3d_bladderurethra1 parameterSetNames = MeshType_3d_bladderurethra1.getParameterSetNames() - self.assertEqual(parameterSetNames, ["Default", "Cat 1", "Human 1", "Mouse 1", "Pig 1", "Rat 1",]) + self.assertEqual(parameterSetNames, ["Default", "Cat 1", "Human 1", "Mouse 1", "Pig 1", "Rat 1"]) options = scaffold.getDefaultOptions("Cat 1") self.assertEqual(26, len(options)) self.assertEqual(12, options.get("Number of elements along bladder")) @@ -111,7 +111,7 @@ def test_bladderurethra1(self): cache.setNode(node) element, xi = markerLocation.evaluateMeshLocation(cache, 3) self.assertEqual(1, element.getIdentifier()) - assertAlmostEqualList(self, xi, [ 0.0, 0.0, 0.0 ], 1.0E-10) + assertAlmostEqualList(self, xi, [0.0, 0.0, 0.0], 1.0E-10) apexGroup = getAnnotationGroupForTerm(annotationGroups, get_bladder_term("apex of urinary bladder")) self.assertTrue(apexGroup.getNodesetGroup(nodes).containsNode(node)) @@ -119,7 +119,8 @@ def test_bladderurethra1(self): # first remove any surface 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)): + if (not annotationGroup.hasMeshGroup(mesh3d)) and \ + (annotationGroup.hasMeshGroup(mesh2d) or annotationGroup.hasMeshGroup(mesh1d)): removeAnnotationGroups.append(annotationGroup) for annotationGroup in removeAnnotationGroups: @@ -179,7 +180,8 @@ def test_bladderurethra1(self): cache.setNode(node) element, xi = markerLocation.evaluateMeshLocation(cache, 3) self.assertEqual(1, element.getIdentifier()) - assertAlmostEqualList(self, xi, [ 0.0, 0.0, 0.0 ], 1.0E-10) + assertAlmostEqualList(self, xi, [0.0, 0.0, 0.0], 1.0E-10) + if __name__ == "__main__": unittest.main() diff --git a/tests/test_cecum.py b/tests/test_cecum.py index f83571d6..715d6a83 100644 --- a/tests/test_cecum.py +++ b/tests/test_cecum.py @@ -19,7 +19,7 @@ def test_cecum1(self): Test creation of cecum scaffold. """ parameterSetNames = MeshType_3d_cecum1.getParameterSetNames() - self.assertEqual(parameterSetNames, [ "Default", "Pig 1" ]) + self.assertEqual(parameterSetNames, ["Default", "Pig 1"]) options = MeshType_3d_cecum1.getDefaultOptions("Pig 1") self.assertEqual(30, len(options)) self.assertEqual(5, options.get("Number of segments")) @@ -87,5 +87,6 @@ def test_cecum1(self): self.assertEqual(result, RESULT_OK) self.assertAlmostEqual(volume, 127905.28250502056, delta=1.0E-6) + if __name__ == "__main__": unittest.main() diff --git a/tests/test_colon.py b/tests/test_colon.py index 51f03c8d..9537c220 100644 --- a/tests/test_colon.py +++ b/tests/test_colon.py @@ -24,12 +24,13 @@ def test_colon1(self): Test creation of colon scaffold. """ parameterSetNames = MeshType_3d_colon1.getParameterSetNames() - self.assertEqual(parameterSetNames, ["Default", "Cattle 1", "Human 1", "Human 2", "Mouse 1", "Mouse 2", "Pig 1", "Pig 2"]) + self.assertEqual(parameterSetNames, ["Default", "Cattle 1", "Human 1", "Human 2", "Mouse 1", "Mouse 2", "Pig 1", + "Pig 2"]) centralPathDefaultScaffoldPackages = { 'Test line': ScaffoldPackage(MeshType_1d_path1, { 'scaffoldSettings': { 'Coordinate dimensions': 3, - 'D2 derivatives':True, + 'D2 derivatives': True, 'Length': 1.0, 'Number of elements': 1 }, @@ -89,8 +90,8 @@ def test_colon1(self): centralPath.generate(tmpRegion) cx = extractPathParametersFromRegion(tmpRegion, [Node.VALUE_LABEL_VALUE])[0] self.assertEqual(2, len(cx)) - assertAlmostEqualList(self, cx[0], [ 163.7, -25.2, 12.2 ], 1.0E-6) - assertAlmostEqualList(self, cx[1], [ 117.2, 32.8, -2.6 ], 1.0E-6) + assertAlmostEqualList(self, cx[0], [163.7, -25.2, 12.2], 1.0E-6) + assertAlmostEqualList(self, cx[1], [117.2, 32.8, -2.6], 1.0E-6) del tmpRegion annotationGroups = MeshType_3d_colon1.generateBaseMesh(region, options) @@ -115,19 +116,19 @@ def test_colon1(self): coordinates = fieldmodule.findFieldByName("coordinates").castFiniteElement() self.assertTrue(coordinates.isValid()) minimums, maximums = evaluateFieldNodesetRange(coordinates, nodes) - assertAlmostEqualList(self, minimums, [ 108.02506479907721, -36.405037279268456, -25.89741158484918 ], 1.0E-6) - assertAlmostEqualList(self, maximums, [ 185.46457506076914, 48.1011574894518, 34.05259862880112 ], 1.0E-6) + assertAlmostEqualList(self, minimums, [108.02506479907721, -36.405037279268456, -25.89741158484918], 1.0E-6) + assertAlmostEqualList(self, maximums, [185.46457506076914, 48.1011574894518, 34.05259862880112], 1.0E-6) flatCoordinates = fieldmodule.findFieldByName("flat coordinates").castFiniteElement() self.assertTrue(flatCoordinates.isValid()) minimums, maximums = evaluateFieldNodesetRange(flatCoordinates, nodes) - assertAlmostEqualList(self, minimums, [ 0.0, 0.0, 0.0 ], 1.0E-6) - assertAlmostEqualList(self, maximums, [ 186.72988844629867, 77.41781871321301, 2.2 ], 1.0E-6) + assertAlmostEqualList(self, minimums, [0.0, 0.0, 0.0], 1.0E-6) + assertAlmostEqualList(self, maximums, [186.72988844629867, 77.41781871321301, 2.2], 1.0E-6) colonCoordinates = fieldmodule.findFieldByName("colon coordinates").castFiniteElement() minimums, maximums = evaluateFieldNodesetRange(colonCoordinates, nodes) assertAlmostEqualList(self, minimums, [-0.6, 0.0, -0.6], 1.0E-4) - assertAlmostEqualList(self, maximums, [ 0.6, 24.0, 0.625], 1.0E-4) + assertAlmostEqualList(self, maximums, [0.6, 24.0, 0.625], 1.0E-4) with ChangeManager(fieldmodule): one = fieldmodule.createFieldConstant(1.0) @@ -186,5 +187,6 @@ def test_mousecolon1(self): self.assertEqual(result, RESULT_OK) self.assertAlmostEqual(colonVolume, 8.290058800222006, delta=1.0E-6) + if __name__ == "__main__": unittest.main() diff --git a/tests/test_colonsegment.py b/tests/test_colonsegment.py index 6f498f2b..786181d3 100644 --- a/tests/test_colonsegment.py +++ b/tests/test_colonsegment.py @@ -19,7 +19,7 @@ def test_humancolonsegment1(self): Test creation of human colon segment scaffold. """ parameterSetNames = MeshType_3d_colonsegment1.getParameterSetNames() - self.assertEqual(parameterSetNames, [ "Default", "Cattle 1", "Human 1", "Mouse 1", "Pig 1" ]) + self.assertEqual(parameterSetNames, ["Default", "Cattle 1", "Human 1", "Mouse 1", "Pig 1"]) options = MeshType_3d_colonsegment1.getDefaultOptions("Human 1") self.assertEqual(31, len(options)) self.assertEqual(0.0, options.get("Start phase")) @@ -59,13 +59,13 @@ def test_humancolonsegment1(self): coordinates = fieldmodule.findFieldByName("coordinates").castFiniteElement() self.assertTrue(coordinates.isValid()) minimums, maximums = evaluateFieldNodesetRange(coordinates, nodes) - assertAlmostEqualList(self, minimums, [ -2.172286248499807e-15, -58.95670186936737, -55.54662267827035 ], 1.0E-6) - assertAlmostEqualList(self, maximums, [ 50.0, 50.52621132610023, 55.54662267827035 ], 1.0E-6) + assertAlmostEqualList(self, minimums, [-2.172286248499807e-15, -58.95670186936737, -55.54662267827035], 1.0E-6) + assertAlmostEqualList(self, maximums, [50.0, 50.52621132610023, 55.54662267827035], 1.0E-6) flatCoordinates = fieldmodule.findFieldByName("flat coordinates").castFiniteElement() self.assertTrue(flatCoordinates.isValid()) minimums, maximums = evaluateFieldNodesetRange(flatCoordinates, nodes) - assertAlmostEqualList(self, minimums, [ 0.0, 0.0, 0.0 ], 1.0E-6) + assertAlmostEqualList(self, minimums, [0.0, 0.0, 0.0], 1.0E-6) assertAlmostEqualList(self, maximums, [397.2736607240895, 50.0, 2.2], 1.0E-6) with ChangeManager(fieldmodule): @@ -119,5 +119,6 @@ def test_mousecolonsegment1(self): self.assertEqual(result, RESULT_OK) self.assertAlmostEqual(flatSurfaceArea, surfaceArea, delta=1.0E-3) + if __name__ == "__main__": unittest.main() diff --git a/tests/test_smallintestine.py b/tests/test_smallintestine.py index 2ce42950..5e5e1025 100644 --- a/tests/test_smallintestine.py +++ b/tests/test_smallintestine.py @@ -68,8 +68,8 @@ def test_smallintestine1(self): centralPath.generate(tmpRegion) cx = extractPathParametersFromRegion(tmpRegion, [Node.VALUE_LABEL_VALUE])[0] self.assertEqual(4, len(cx)) - assertAlmostEqualList(self, cx[0], [ -2.3, 18.5, -4.4 ], 1.0E-6) - assertAlmostEqualList(self, cx[2], [ -18.3, 12.6, -1.5 ], 1.0E-6) + assertAlmostEqualList(self, cx[0], [-2.3, 18.5, -4.4], 1.0E-6) + assertAlmostEqualList(self, cx[2], [-18.3, 12.6, -1.5], 1.0E-6) del tmpRegion annotationGroups = MeshType_3d_smallintestine1.generateBaseMesh(region, options) @@ -91,14 +91,14 @@ def test_smallintestine1(self): coordinates = fieldmodule.findFieldByName("coordinates").castFiniteElement() self.assertTrue(coordinates.isValid()) minimums, maximums = evaluateFieldNodesetRange(coordinates, nodes) - assertAlmostEqualList(self, minimums, [ -20.06978981419564, 11.406595205949705, -7.1653294859433965 ], 1.0E-6) - assertAlmostEqualList(self, maximums, [ -1.8300388314851923, 19.193885338090105, 0.9772071374844936 ], 1.0E-6) + assertAlmostEqualList(self, minimums, [-20.06978981419564, 11.406595205949705, -7.1653294859433965], 1.0E-6) + assertAlmostEqualList(self, maximums, [-1.8300388314851923, 19.193885338090105, 0.9772071374844936], 1.0E-6) flatCoordinates = fieldmodule.findFieldByName("flat coordinates").castFiniteElement() self.assertTrue(flatCoordinates.isValid()) minimums, maximums = evaluateFieldNodesetRange(flatCoordinates, nodes) - assertAlmostEqualList(self, minimums, [ -1.39038154442654, 0.0, 0.0 ], 1.0E-6) - assertAlmostEqualList(self, maximums, [ 4.891237158967401, 25.293706698841913, 0.1 ], 1.0E-6) + assertAlmostEqualList(self, minimums, [-1.39038154442654, 0.0, 0.0], 1.0E-6) + assertAlmostEqualList(self, maximums, [4.891237158967401, 25.293706698841913, 0.1], 1.0E-6) with ChangeManager(fieldmodule): one = fieldmodule.createFieldConstant(1.0) @@ -121,5 +121,6 @@ def test_smallintestine1(self): self.assertEqual(result, RESULT_OK) self.assertAlmostEqual(flatSurfaceArea, 171.37026123844635, delta=1.0E-3) + if __name__ == "__main__": unittest.main() diff --git a/tests/test_stomach.py b/tests/test_stomach.py index 50a7414e..90c1dbe2 100644 --- a/tests/test_stomach.py +++ b/tests/test_stomach.py @@ -24,7 +24,7 @@ def test_stomach1(self): """ scaffold = MeshType_3d_stomach1 parameterSetNames = scaffold.getParameterSetNames() - self.assertEqual(parameterSetNames, [ "Default", "Human 1", "Mouse 1", "Rat 1" ]) + self.assertEqual(parameterSetNames, ["Default", "Human 1", "Mouse 1", "Rat 1"]) options = scaffold.getDefaultOptions("Rat 1") self.assertEqual(19, len(options)) self.assertEqual(12, options.get("Number of elements around esophagus")) @@ -91,12 +91,12 @@ def test_stomach1(self): # check some annotationGroups: expectedSizes3d = { - "body of stomach" : 112, - "cardia of stomach" : 36, - "duodenum" : 56, - "esophagus" : 96, - "fundus of stomach" : 114, - "pyloric antrum" : 112, + "body of stomach": 112, + "cardia of stomach": 36, + "duodenum": 56, + "esophagus": 96, + "fundus of stomach": 114, + "pyloric antrum": 112, "pyloric canal": 56, "stomach": 582 } @@ -109,7 +109,8 @@ def test_stomach1(self): # first remove any surface 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)): + if (not annotationGroup.hasMeshGroup(mesh3d)) and \ + (annotationGroup.hasMeshGroup(mesh2d) or annotationGroup.hasMeshGroup(mesh1d)): removeAnnotationGroups.append(annotationGroup) for annotationGroup in removeAnnotationGroups: @@ -161,12 +162,14 @@ def test_stomach1(self): markerLocation = refineFieldmodule.findFieldByName("marker_location") self.assertTrue(markerLocation.isValid()) cache = refineFieldmodule.createFieldcache() - node = findNodeWithName(markerNodes, markerName, "esophagogastric junction along the lesser curvature on serosa") + node = findNodeWithName(markerNodes, markerName, + "esophagogastric junction along the lesser curvature on serosa") self.assertTrue(node.isValid()) cache.setNode(node) element, xi = markerLocation.evaluateMeshLocation(cache, 3) self.assertEqual(5821, element.getIdentifier()) - assertAlmostEqualList(self, xi, [ 0.0, 1.0, 1.0 ], 1.0E-10) + assertAlmostEqualList(self, xi, [0.0, 1.0, 1.0], 1.0E-10) + if __name__ == "__main__": unittest.main() From e8cdfbad03d939a89a869f20fedeb745d60c00ac Mon Sep 17 00:00:00 2001 From: Mabelle Lin Date: Wed, 10 Nov 2021 10:58:52 +1300 Subject: [PATCH 19/22] Add interlex identifiers to stomach terms --- src/scaffoldmaker/annotation/stomach_terms.py | 84 +++++++++---------- 1 file changed, 42 insertions(+), 42 deletions(-) diff --git a/src/scaffoldmaker/annotation/stomach_terms.py b/src/scaffoldmaker/annotation/stomach_terms.py index a5f8577a..f4b003d6 100644 --- a/src/scaffoldmaker/annotation/stomach_terms.py +++ b/src/scaffoldmaker/annotation/stomach_terms.py @@ -5,61 +5,61 @@ # convention: preferred name, preferred id, followed by any other ids and alternative names stomach_terms = [ ("body of stomach", "UBERON:0001161", " FMA:14560", "ILX:0724929"), - ("body-antrum junction along the greater curvature on circular-longitudinal muscle interface", "None"), - ("body-antrum junction along the greater curvature on luminal surface", "None"), - ("body-antrum junction along the greater curvature on serosa", "None"), + ("body-antrum junction along the greater curvature on circular-longitudinal muscle interface", "ILX:0793127"), + ("body-antrum junction along the greater curvature on luminal surface", "ILX:0793128"), + ("body-antrum junction along the greater curvature on serosa", "ILX:0793087"), ("cardia of stomach", "UBERON:0001162", " FMA:14561", "ILX:0729096"), ("circular muscle layer of stomach", "ILX:0774731"), - ("circular-longitudinal muscle interface of body of stomach along the gastric-omentum attachment", "None"), - ("circular-longitudinal muscle interface of dorsal stomach", "None"), - ("circular-longitudinal muscle interface of duodenum along the gastric-omentum attachment", "None"), - ("circular-longitudinal muscle interface of esophagus along the cut margin", "None"), - ("circular-longitudinal muscle interface of fundus of stomach along the gastric-omentum attachment", "None"), - ("circular-longitudinal muscle interface of gastroduodenal junction", "None"), - ("circular-longitudinal muscle interface of pyloric antrum along the gastric-omentum attachment", "None"), - ("circular-longitudinal muscle interface of pyloric canal along the gastric-omentum attachment", "None"), - ("circular-longitudinal muscle interface of stomach", "None"), - ("circular-longitudinal muscle interface of ventral stomach", "None"), + ("circular-longitudinal muscle interface of body of stomach along the gastric-omentum attachment", "ILX:0793088"), + ("circular-longitudinal muscle interface of dorsal stomach", "ILX:0793089"), + ("circular-longitudinal muscle interface of duodenum along the gastric-omentum attachment", "ILX:0793090"), + ("circular-longitudinal muscle interface of esophagus along the cut margin", "ILX:0793091"), + ("circular-longitudinal muscle interface of fundus of stomach along the gastric-omentum attachment", "ILX:0793092"), + ("circular-longitudinal muscle interface of gastroduodenal junction", "ILX:0793093"), + ("circular-longitudinal muscle interface of pyloric antrum along the gastric-omentum attachment", "ILX:0793094"), + ("circular-longitudinal muscle interface of pyloric canal along the gastric-omentum attachment", "ILX:0793095"), + ("circular-longitudinal muscle interface of stomach", "ILX:0793096"), + ("circular-longitudinal muscle interface of ventral stomach", "ILX:0793097"), ("dorsal stomach", "ILX:0793086"), ("duodenum", "UBERON:0002114", " FMA:7206", "ILX:0726125"), ("esophagogastric junction", "UBERON:0007650", "FMA: 9434", "ILX:0733910"), - ("esophagogastric junction along the greater curvature on circular-longitudinal muscle interface", "None"), - ("esophagogastric junction along the greater curvature on luminal surface", "None"), - ("esophagogastric junction along the greater curvature on serosa", "None"), - ("esophagogastric junction along the lesser curvature on circular-longitudinal muscle interface", "None"), - ("esophagogastric junction along the lesser curvature on luminal surface", "None"), - ("esophagogastric junction along the lesser curvature on serosa", "None"), + ("esophagogastric junction along the greater curvature on circular-longitudinal muscle interface", "ILX:0793098"), + ("esophagogastric junction along the greater curvature on luminal surface", "ILX:0793099"), + ("esophagogastric junction along the greater curvature on serosa", "ILX:0793100"), + ("esophagogastric junction along the lesser curvature on circular-longitudinal muscle interface", "ILX:0793101"), + ("esophagogastric junction along the lesser curvature on luminal surface", "ILX:0793102"), + ("esophagogastric junction along the lesser curvature on serosa", "ILX:0793103"), ("esophagus", "UBERON:0001043", "FMA:7131", "ILX:0735017"), ("esophagus mucosa", "UBERON:0002469", "FMA:62996", "ILX:0725079"), ("esophagus smooth muscle circular layer", "UBERON:0009960", "FMA:67605", "ILX:0735992"), ("esophagus smooth muscle longitudinal layer", "UBERON:0009961", "FMA:63573", "ILX:0727608"), ("forestomach-glandular stomach junction", "UBERON:0012270", "ILX:0729974"), ("fundus of stomach", "UBERON:0001160", " FMA:14559", "ILX:0724443"), - ("fundus-body junction along the greater curvature on circular-longitudinal muscle interface", "None"), - ("fundus-body junction along the greater curvature on luminal surface", "None"), - ("fundus-body junction along the greater curvature on serosa", "None"), + ("fundus-body junction along the greater curvature on circular-longitudinal muscle interface", "ILX:0793104"), + ("fundus-body junction along the greater curvature on luminal surface", "ILX:0793105"), + ("fundus-body junction along the greater curvature on serosa", "ILX:0793106"), ("gastroduodenal junction", "UBERON:0012650", "FMA:17046", "ILX:0725406"), - ("gastroduodenal junction along the greater curvature on circular-longitudinal muscle interface", "None"), - ("gastroduodenal junction along the greater curvature on luminal surface", "None"), - ("gastroduodenal junction along the greater curvature on serosa", "None"), - ("gastroduodenal junction along the lesser curvature on circular-longitudinal muscle interface", "None"), - ("gastroduodenal junction along the lesser curvature on luminal surface", "None"), - ("gastroduodenal junction along the lesser curvature on serosa", "None"), - ("limiting ridge along the greater curvature on circular-longitudinal muscle interface", "None"), - ("limiting ridge along the greater curvature on luminal surface", "None"), - ("limiting ridge along the greater curvature on serosa", "None"), - ("limiting ridge on circular-longitudinal muscle interface", "None"), - ("limiting ridge on luminal surface", "None"), - ("limiting ridge on serosa", "None"), + ("gastroduodenal junction along the greater curvature on circular-longitudinal muscle interface", "ILX:0793107"), + ("gastroduodenal junction along the greater curvature on luminal surface", "ILX:0793108"), + ("gastroduodenal junction along the greater curvature on serosa", "ILX:0793109"), + ("gastroduodenal junction along the lesser curvature on circular-longitudinal muscle interface", "ILX:0793110"), + ("gastroduodenal junction along the lesser curvature on luminal surface", "ILX:0793111"), + ("gastroduodenal junction along the lesser curvature on serosa", "ILX:0793112"), + ("limiting ridge along the greater curvature on circular-longitudinal muscle interface", "ILX:0793113"), + ("limiting ridge along the greater curvature on luminal surface", "ILX:0793114"), + ("limiting ridge along the greater curvature on serosa", "ILX:0793115"), + ("limiting ridge on circular-longitudinal muscle interface", "ILX:0793116"), + ("limiting ridge on luminal surface", "ILX:0793117"), + ("limiting ridge on serosa", "ILX:0793118"), ("longitudinal muscle layer of stomach", "ILX:0772619"), - ("luminal surface of body of stomach", "None"), - ("luminal surface of cardia of stomach", "None"), - ("luminal surface of duodenum", "None"), - ("luminal surface of esophagus", "None"), - ("luminal surface of fundus of stomach", "None"), - ("luminal surface of pyloric antrum", "None"), - ("luminal surface of pyloric canal", "None"), - ("luminal surface of stomach", "None"), + ("luminal surface of body of stomach", "ILX:0793119"), + ("luminal surface of cardia of stomach", "ILX:0793120"), + ("luminal surface of duodenum", "ILX:0793121"), + ("luminal surface of esophagus", "ILX:0793122"), + ("luminal surface of fundus of stomach", "ILX:0793123"), + ("luminal surface of pyloric antrum", "ILX:0793124"), + ("luminal surface of pyloric canal", "ILX:0793125"), + ("luminal surface of stomach", "ILX:0793126"), ("mucosa of stomach", "UBERON:0001199", "FMA:14907", "ILX:0736669"), ("pyloric antrum", "UBERON:0001165", " FMA:14579", "ILX:0728672"), ("pyloric canal", "UBERON:0008858", "FMA:14580", "ILX:0735898"), From c35f2f42e1ccc8f0a589c4e6d0a05577dc9f0ec5 Mon Sep 17 00:00:00 2001 From: Mabelle Lin Date: Fri, 12 Nov 2021 16:56:21 +1300 Subject: [PATCH 20/22] Re-calculate thickness proportions from relative Thicknesses input --- src/scaffoldmaker/meshtypes/meshtype_3d_ostium1.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/scaffoldmaker/meshtypes/meshtype_3d_ostium1.py b/src/scaffoldmaker/meshtypes/meshtype_3d_ostium1.py index a217703b..c1b65f34 100644 --- a/src/scaffoldmaker/meshtypes/meshtype_3d_ostium1.py +++ b/src/scaffoldmaker/meshtypes/meshtype_3d_ostium1.py @@ -256,7 +256,7 @@ def generateOstiumMesh(region, options, trackSurface, centrePosition, axis1, sta ostiumRadius = 0.5 * unitScale * options['Ostium diameter'] ostiumLength = unitScale * options['Ostium length'] ostiumWallThickness = unitScale * options['Ostium wall thickness'] - ostiumWallThicknessProportions = copy.deepcopy(options['Ostium wall relative thicknesses']) + ostiumWallThicknessProportionsUI = copy.deepcopy(options['Ostium wall relative thicknesses']) interVesselHeight = unitScale * options['Ostium inter-vessel height'] interVesselDistance = unitScale * options['Ostium inter-vessel distance'] if (vesselsCount > 1) else 0.0 halfInterVesselDistance = 0.5 * interVesselDistance @@ -264,7 +264,7 @@ def generateOstiumMesh(region, options, trackSurface, centrePosition, axis1, sta vesselEndDerivative = ostiumLength * options['Vessel end length factor'] / elementsCountAlong vesselInnerRadius = 0.5 * unitScale * options['Vessel inner diameter'] vesselWallThickness = unitScale * options['Vessel wall thickness'] - vesselWallThicknessProportions = copy.deepcopy(options['Vessel wall relative thicknesses']) + vesselWallThicknessProportionsUI = copy.deepcopy(options['Vessel wall relative thicknesses']) vesselOuterRadius = vesselInnerRadius + vesselWallThickness vesselAngle1Radians = math.radians(options['Vessel angle 1 degrees']) vesselAngle1SpreadRadians = math.radians(options['Vessel angle 1 spread degrees']) @@ -341,6 +341,11 @@ def generateOstiumMesh(region, options, trackSurface, centrePosition, axis1, sta od2 = [[] for n3 in range(elementsCountThroughWall + 1)] od3 = [[] for n3 in range(elementsCountThroughWall + 1)] oPositions = [] + ostiumWallThicknessProportions = [ostiumWallThicknessProportion / sum(ostiumWallThicknessProportionsUI) + for ostiumWallThicknessProportion in ostiumWallThicknessProportionsUI] + vesselWallThicknessProportions = [vesselWallThicknessProportion / sum(vesselWallThicknessProportionsUI) + for vesselWallThicknessProportion in vesselWallThicknessProportionsUI] + ostiumWallThicknessProportions.append(ostiumWallThicknessProportions[-1]) vesselWallThicknessProportions.append(vesselWallThicknessProportions[-1]) From 677db9e667ec0b0de830ec2cf0713840708d1499 Mon Sep 17 00:00:00 2001 From: Mabelle Lin Date: Fri, 12 Nov 2021 17:25:30 +1300 Subject: [PATCH 21/22] Calculate thickness proportions from input relative thicknesses --- src/scaffoldmaker/meshtypes/meshtype_3d_stomach1.py | 6 ++++-- tests/test_stomach.py | 8 ++++---- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/src/scaffoldmaker/meshtypes/meshtype_3d_stomach1.py b/src/scaffoldmaker/meshtypes/meshtype_3d_stomach1.py index 9acc17a5..5deefe77 100644 --- a/src/scaffoldmaker/meshtypes/meshtype_3d_stomach1.py +++ b/src/scaffoldmaker/meshtypes/meshtype_3d_stomach1.py @@ -1990,8 +1990,10 @@ def generateBaseMesh(cls, region, options): idxMat = [] if elementsCountThroughWall > 1: - thicknessProportions = [0.0, mucosaRelThickness, submucosaRelThickness, circularRelThickness, - longitudinalRelThickness, longitudinalRelThickness] + thicknessProportionsUI = [0.0, mucosaRelThickness, submucosaRelThickness, circularRelThickness, + longitudinalRelThickness, longitudinalRelThickness] + thicknessProportions = [thicknessProportion / sum(thicknessProportionsUI) + for thicknessProportion in thicknessProportionsUI] xi3List = [] xi3 = 0.0 for i in range(len(thicknessProportions) - 1): diff --git a/tests/test_stomach.py b/tests/test_stomach.py index 90c1dbe2..b6429a73 100644 --- a/tests/test_stomach.py +++ b/tests/test_stomach.py @@ -71,8 +71,8 @@ def test_stomach1(self): coordinates = fieldmodule.findFieldByName("coordinates").castFiniteElement() self.assertTrue(coordinates.isValid()) minimums, maximums = evaluateFieldNodesetRange(coordinates, nodes) - assertAlmostEqualList(self, minimums, [-18.238549598577396, -16.033751319943754, -8.905924748773598], 1.0E-6) - assertAlmostEqualList(self, maximums, [18.285156743233415, 15.214807824088728, 8.905433142848109], 1.0E-6) + assertAlmostEqualList(self, minimums, [-18.214909076136756, -16.010042251585798, -8.882419863214968], 1.0E-6) + assertAlmostEqualList(self, maximums, [18.262047678184306, 15.214807824088728, 8.881936264327805], 1.0E-6) with ChangeManager(fieldmodule): one = fieldmodule.createFieldConstant(1.0) @@ -84,10 +84,10 @@ def test_stomach1(self): fieldcache = fieldmodule.createFieldcache() result, surfaceArea = surfaceAreaField.evaluateReal(fieldcache, 1) self.assertEqual(result, RESULT_OK) - self.assertAlmostEqual(surfaceArea, 2557.832902256128, delta=1.0E-6) + self.assertAlmostEqual(surfaceArea, 2548.511860089027, delta=1.0E-6) result, volume = volumeField.evaluateReal(fieldcache, 1) self.assertEqual(result, RESULT_OK) - self.assertAlmostEqual(volume, 809.0349219398828, delta=1.0E-6) + self.assertAlmostEqual(volume, 769.8802118839903, delta=1.0E-6) # check some annotationGroups: expectedSizes3d = { From 2f3173709b7855307c18f6ed27172db4a4a0a7a0 Mon Sep 17 00:00:00 2001 From: Mabelle Lin Date: Fri, 12 Nov 2021 18:29:34 +1300 Subject: [PATCH 22/22] Fix error in calculating relative thicknesses --- src/scaffoldmaker/meshtypes/meshtype_3d_stomach1.py | 3 ++- tests/test_stomach.py | 8 ++++---- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/scaffoldmaker/meshtypes/meshtype_3d_stomach1.py b/src/scaffoldmaker/meshtypes/meshtype_3d_stomach1.py index 5deefe77..721ed3c1 100644 --- a/src/scaffoldmaker/meshtypes/meshtype_3d_stomach1.py +++ b/src/scaffoldmaker/meshtypes/meshtype_3d_stomach1.py @@ -1992,8 +1992,9 @@ def generateBaseMesh(cls, region, options): if elementsCountThroughWall > 1: thicknessProportionsUI = [0.0, mucosaRelThickness, submucosaRelThickness, circularRelThickness, longitudinalRelThickness, longitudinalRelThickness] - thicknessProportions = [thicknessProportion / sum(thicknessProportionsUI) + thicknessProportions = [thicknessProportion / sum(thicknessProportionsUI[:-1]) for thicknessProportion in thicknessProportionsUI] + xi3List = [] xi3 = 0.0 for i in range(len(thicknessProportions) - 1): diff --git a/tests/test_stomach.py b/tests/test_stomach.py index b6429a73..90c1dbe2 100644 --- a/tests/test_stomach.py +++ b/tests/test_stomach.py @@ -71,8 +71,8 @@ def test_stomach1(self): coordinates = fieldmodule.findFieldByName("coordinates").castFiniteElement() self.assertTrue(coordinates.isValid()) minimums, maximums = evaluateFieldNodesetRange(coordinates, nodes) - assertAlmostEqualList(self, minimums, [-18.214909076136756, -16.010042251585798, -8.882419863214968], 1.0E-6) - assertAlmostEqualList(self, maximums, [18.262047678184306, 15.214807824088728, 8.881936264327805], 1.0E-6) + assertAlmostEqualList(self, minimums, [-18.238549598577396, -16.033751319943754, -8.905924748773598], 1.0E-6) + assertAlmostEqualList(self, maximums, [18.285156743233415, 15.214807824088728, 8.905433142848109], 1.0E-6) with ChangeManager(fieldmodule): one = fieldmodule.createFieldConstant(1.0) @@ -84,10 +84,10 @@ def test_stomach1(self): fieldcache = fieldmodule.createFieldcache() result, surfaceArea = surfaceAreaField.evaluateReal(fieldcache, 1) self.assertEqual(result, RESULT_OK) - self.assertAlmostEqual(surfaceArea, 2548.511860089027, delta=1.0E-6) + self.assertAlmostEqual(surfaceArea, 2557.832902256128, delta=1.0E-6) result, volume = volumeField.evaluateReal(fieldcache, 1) self.assertEqual(result, RESULT_OK) - self.assertAlmostEqual(volume, 769.8802118839903, delta=1.0E-6) + self.assertAlmostEqual(volume, 809.0349219398828, delta=1.0E-6) # check some annotationGroups: expectedSizes3d = {