From d784de1952f01b7aad6b7345168f759f68bbb82a Mon Sep 17 00:00:00 2001 From: Richard Christie Date: Wed, 24 Oct 2018 16:19:45 +1300 Subject: [PATCH 01/25] Modify atria aorta incline region Centre atria model about LV outlet/aorta. Rotate cfb about centre of aorta, not +/-60 degree points Thicken atria by aorta to reduce distortion. Reduce default base slope to 15 degrees, to reduce distortion. --- .../meshtypes/meshtype_3d_heartatria1.py | 61 +++++++++---------- 1 file changed, 29 insertions(+), 32 deletions(-) diff --git a/scaffoldmaker/meshtypes/meshtype_3d_heartatria1.py b/scaffoldmaker/meshtypes/meshtype_3d_heartatria1.py index 770e600f..87663942 100644 --- a/scaffoldmaker/meshtypes/meshtype_3d_heartatria1.py +++ b/scaffoldmaker/meshtypes/meshtype_3d_heartatria1.py @@ -39,8 +39,8 @@ def getDefaultOptions(): 'Atrial septum thickness' : 0.08, 'Atrial free wall thickness' : 0.02, 'Atrial base wall thickness' : 0.05, - 'Atrial base slope degrees' : 30.0, - 'Aorta outer diameter' : 0.35, + 'Atrial base slope degrees' : 15.0, + 'Aorta outer plus diameter' : 0.35, 'Atrial base front incline degrees' : 30.0, 'Atrial base back incline degrees' : 30.0, 'Atrial base side incline degrees' : 10.0, @@ -87,7 +87,7 @@ def getOrderedOptionNames(): 'Atrial free wall thickness', 'Atrial base wall thickness', 'Atrial base slope degrees', - 'Aorta outer diameter', + 'Aorta outer plus diameter', 'Atrial base front incline degrees', 'Atrial base back incline degrees', 'Atrial base side incline degrees', @@ -167,8 +167,8 @@ def checkOptions(options): options[key] = 0.1 elif options[key] > 0.9: options[key] = 0.9 - if options['Aorta outer diameter'] < options['Atrial septum thickness']: - options['Aorta outer diameter'] = options['Atrial septum thickness'] + if options['Aorta outer plus diameter'] < options['Atrial septum thickness']: + options['Aorta outer plus diameter'] = options['Atrial septum thickness'] for key in [ 'Atria major axis rotation degrees']: if options[key] < -75.0: @@ -198,7 +198,7 @@ def generateBaseMesh(cls, region, options): aBaseInnerMinorMag = 0.5*options['Atria base inner minor axis length'] aMajorAxisRadians = math.radians(options['Atria major axis rotation degrees']) aOuterHeight = options['Atria outer height'] - aortaOuterRadius = 0.5*options['Aorta outer diameter'] + aortaOuterPlusRadius = 0.5*options['Aorta outer plus diameter'] aBaseFrontInclineRadians = math.radians(options['Atrial base front incline degrees']) aBaseSideInclineRadians = math.radians(options['Atrial base side incline degrees']) aBaseBackInclineRadians = math.radians(options['Atrial base back incline degrees']) @@ -277,7 +277,7 @@ def generateBaseMesh(cls, region, options): getLeftAtriumBasePoints(elementsCountAroundAtrialFreeWall, elementsCountAroundAtrialSeptum, aBaseInnerMajorMag, aBaseInnerMinorMag, aMajorAxisRadians, aBaseWallThickness, aBaseSlopeHeight, aBaseSlopeLength, aSeptumThickness, - aortaOuterRadius, aBaseFrontInclineRadians, aBaseSideInclineRadians, aBaseBackInclineRadians) + aortaOuterPlusRadius, aBaseFrontInclineRadians, aBaseSideInclineRadians, aBaseBackInclineRadians) laInnerx = [ laBaseInnerx ] laInnerd1 = [ laBaseInnerd1 ] @@ -1702,7 +1702,7 @@ def generateMesh(cls, region, options): def getLeftAtriumBasePoints(elementsCountAroundAtrialFreeWall, elementsCountAroundAtrialSeptum, aBaseInnerMajorMag, aBaseInnerMinorMag, aMajorAxisRadians, aBaseWallThickness, aBaseSlopeHeight, aBaseSlopeLength, aSeptumThickness, - aortaOuterRadius, aBaseFrontInclineRadians, aBaseSideInclineRadians, aBaseBackInclineRadians): + aortaOuterPlusRadius, aBaseFrontInclineRadians, aBaseSideInclineRadians, aBaseBackInclineRadians): """ Get points around left atrium based on an ellipse. Points start from central fibrous body and wind anticlockwise around LA. Both the cfb and crux are collapsed at the septum. @@ -1724,32 +1724,28 @@ def getLeftAtriumBasePoints(elementsCountAroundAtrialFreeWall, elementsCountArou # (aSeptumBaseLength/elementsCountAroundAtrialSeptum)*(0.5*elementsCountAroundAtrialSeptum + 1.0)) axOuter = aBaseOuterMajorMag*math.cos(aMajorAxisRadians) bxOuter = aBaseOuterMinorMag*math.sin(aMajorAxisRadians) - cfbSideOffset = aortaOuterRadius*math.sin(math.pi/3.0) - laCfbLeftRadians = getEllipseRadiansToX(axOuter, bxOuter, -cfbSideOffset - laCentreX, math.pi*0.5) - #print('axInner',axInner,'bxInner',bxInner,'laCentreX',laCentreX) - #print('laSeptumRadians',laSeptumRadians,'laCfbLeftRadians',laCfbLeftRadians) - laCentreY = 0.0 - laCentreZ = 0.0 # get points on central fibrous body centre and cfbLeft (60 degrees clockwise around aorta) - # incline rotates about cfb-Left-Right axis, so cfb centre goes up - cfbLeftX = -cfbSideOffset - cfbLeftY = laCentreY + math.cos(laCfbLeftRadians)*aBaseOuterMajorMag*math.sin(-aMajorAxisRadians) \ - + math.sin(laCfbLeftRadians)*aBaseOuterMinorMag*math.cos(-aMajorAxisRadians) - cfbLeftZ = 0.0 + # rotates about centre of aorta by aBaseFrontInclineRadians cosFrontInclineRadians = math.cos(aBaseFrontInclineRadians) sinFrontInclineRadians = math.sin(aBaseFrontInclineRadians) - r = aortaOuterRadius*(1.0 - math.cos(math.pi/3.0)) - cfbX = 0.0 - cfbY = cfbLeftY - r*math.cos(lvOutletFrontInclineRadians) - cfbZ = cfbLeftZ + r*math.sin(lvOutletFrontInclineRadians) - pi_3 = math.pi/3.0 - lvOutletDerivativeAround = aortaOuterRadius*pi_3 - cfbLeftDerivative1 = [ \ - -lvOutletDerivativeAround*math.cos(pi_3), - lvOutletDerivativeAround*math.sin(pi_3)*cosFrontInclineRadians, - -lvOutletDerivativeAround*math.sin(pi_3)*sinFrontInclineRadians ] + cosPi_3 = math.cos(pi_3) + sinPi_3 = math.sin(pi_3) + cfbSideOffset = aortaOuterPlusRadius*sinPi_3 + rLeft = aortaOuterPlusRadius*cosPi_3 + cfbLeftX = -cfbSideOffset + cfbLeftY = -rLeft*cosFrontInclineRadians + cfbLeftZ = rLeft*sinFrontInclineRadians + cfbX = 0.0 + cfbY = -aortaOuterPlusRadius*cosFrontInclineRadians + cfbZ = aortaOuterPlusRadius*sinFrontInclineRadians + lvOutletDerivativeAround = aortaOuterPlusRadius*pi_3 + laCfbLeftRadians = getEllipseRadiansToX(axOuter, bxOuter, cfbLeftX - laCentreX, math.pi*0.5) + aBaseOuterMinorMagPlus = aBaseOuterMinorMag + 0.5*aBaseSlopeLength # GRC fudge factor + laCentreY = cfbLeftY - math.cos(laCfbLeftRadians)*aBaseOuterMajorMag*math.sin(-aMajorAxisRadians) \ + - math.sin(laCfbLeftRadians)*aBaseOuterMinorMagPlus*math.cos(-aMajorAxisRadians) + laCentreZ = 0.0 # compute radians around based on base outer major and minor axis sizes atrialPerimeterLength = getApproximateEllipsePerimeter(aBaseOuterMajorMag, aBaseOuterMinorMag) @@ -1782,7 +1778,7 @@ def getLeftAtriumBasePoints(elementsCountAroundAtrialFreeWall, elementsCountArou laBaseOuterd1 = [] laBaseOuterd2 = [] - baseDerivative2Scale = aortaOuterRadius + baseDerivative2Scale = aortaOuterPlusRadius sinMajorAxisRadians = math.sin(-aMajorAxisRadians) cosMajorAxisRadians = math.cos(-aMajorAxisRadians) @@ -1802,7 +1798,8 @@ def getLeftAtriumBasePoints(elementsCountAroundAtrialFreeWall, elementsCountArou aMinorX = -aMinorMag*sinMajorAxisRadians aMinorY = aMinorMag*cosMajorAxisRadians - finalArcLength = prevArcLength = getEllipseArcLength(aMajorMag, aMinorMag, laRadians[-1] - 2.0*math.pi, laRadians[0]) + twoPi = 2.0*math.pi + finalArcLength = prevArcLength = getEllipseArcLength(aMajorMag, aMinorMag, laRadians[-1] - twoPi, laRadians[0]) n1Limit = elementsCountAroundAtrium if (n3 == 0) else (elementsCountAroundAtrialFreeWall + 1) for n1 in range(n1Limit): radiansAround = laRadians[n1] @@ -1826,7 +1823,7 @@ def getLeftAtriumBasePoints(elementsCountAroundAtrialFreeWall, elementsCountArou d2 = [ 0.0, baseDerivative2Scale*sinFrontInclineRadians, baseDerivative2Scale*cosFrontInclineRadians ] elif (n3 == 1) and (n1 == 1): x = [ cfbLeftX, cfbLeftY, cfbLeftZ ] - d1 = cfbLeftDerivative1 + d1 = [ -lvOutletDerivativeAround*cosPi_3, lvOutletDerivativeAround*sinPi_3*cosFrontInclineRadians, -lvOutletDerivativeAround*sinPi_3*sinFrontInclineRadians ] d2 = [ 0.0, baseDerivative2Scale*sinFrontInclineRadians, baseDerivative2Scale*cosFrontInclineRadians ] else: x = [ From daac31caa77a6463caa0293df2742fd51bb6c3df Mon Sep 17 00:00:00 2001 From: Richard Christie Date: Fri, 26 Oct 2018 15:29:25 +1300 Subject: [PATCH 02/25] Add LV, RV outlets, AV fibrous ring nodes Tweak parameters to make them fit ventricles better. --- .../meshtypes/meshtype_3d_heartatria1.py | 2 +- .../meshtypes/meshtype_3d_heartventricles1.py | 9 +- .../meshtype_3d_heartventriclesbase1.py | 229 +++++++++++++++++- 3 files changed, 226 insertions(+), 14 deletions(-) diff --git a/scaffoldmaker/meshtypes/meshtype_3d_heartatria1.py b/scaffoldmaker/meshtypes/meshtype_3d_heartatria1.py index 87663942..98a36794 100644 --- a/scaffoldmaker/meshtypes/meshtype_3d_heartatria1.py +++ b/scaffoldmaker/meshtypes/meshtype_3d_heartatria1.py @@ -41,7 +41,7 @@ def getDefaultOptions(): 'Atrial base wall thickness' : 0.05, 'Atrial base slope degrees' : 15.0, 'Aorta outer plus diameter' : 0.35, - 'Atrial base front incline degrees' : 30.0, + 'Atrial base front incline degrees' : 15.0, 'Atrial base back incline degrees' : 30.0, 'Atrial base side incline degrees' : 10.0, 'Atrial element size ratio anterior/posterior' : 1.5, diff --git a/scaffoldmaker/meshtypes/meshtype_3d_heartventricles1.py b/scaffoldmaker/meshtypes/meshtype_3d_heartventricles1.py index d62bd986..07fe9e93 100644 --- a/scaffoldmaker/meshtypes/meshtype_3d_heartventricles1.py +++ b/scaffoldmaker/meshtypes/meshtype_3d_heartventricles1.py @@ -40,15 +40,15 @@ def getDefaultOptions(): 'LV free wall thickness' : 0.12, 'LV apex thickness' : 0.06, 'RV inner height fraction' : 0.75, - 'RV arc around degrees' : 180.0, + 'RV arc around degrees' : 155.0, 'RV arc apex fraction' : 0.6, 'RV free wall thickness' : 0.04, - 'RV width' : 0.4, + 'RV width' : 0.45, 'RV width growth factor' : 0.5, 'RV side extension' : 0.12, 'RV side extension growth factor' : 0.5, 'Ventricular septum thickness' : 0.1, - 'Ventricular septum base radial displacement' : 0.15, + 'Ventricular septum base radial displacement' : 0.1, 'Use cross derivatives' : False, 'Refine' : False, 'Refine number of elements surface' : 4, @@ -110,8 +110,7 @@ def checkOptions(options): 'RV free wall thickness', 'RV width', 'RV side extension', - 'Ventricular septum thickness', - 'Ventricular septum base radial displacement']: + 'Ventricular septum thickness']: if options[key] < 0.0: options[key] = 0.0 for key in [ diff --git a/scaffoldmaker/meshtypes/meshtype_3d_heartventriclesbase1.py b/scaffoldmaker/meshtypes/meshtype_3d_heartventriclesbase1.py index 8bccd3f9..2852a3c6 100644 --- a/scaffoldmaker/meshtypes/meshtype_3d_heartventriclesbase1.py +++ b/scaffoldmaker/meshtypes/meshtype_3d_heartventriclesbase1.py @@ -7,6 +7,7 @@ from __future__ import division import math from scaffoldmaker.annotation.annotationgroup import AnnotationGroup, findAnnotationGroupByName +from scaffoldmaker.meshtypes.meshtype_3d_heartatria1 import getLeftAtriumBasePoints from scaffoldmaker.meshtypes.meshtype_3d_heartventricles1 import MeshType_3d_heartventricles1 from scaffoldmaker.utils.eft_utils import * from scaffoldmaker.utils.geometry import * @@ -48,18 +49,22 @@ def getDefaultOptions(): options['Atria major axis rotation degrees'] = 40.0 options['Atrial septum thickness'] = 0.06 options['Atrial base wall thickness'] = 0.05 - options['Atrial base slope degrees'] = 30.0 - options['Base height'] = 0.12 + options['Atrial base slope degrees'] = 15.0 + options['Base height'] = 0.14 options['Base thickness'] = 0.06 options['Fibrous ring thickness'] = 0.01 - options['LV outlet front incline degrees'] = 30.0 + options['LV outlet front incline degrees'] = 15.0 options['LV outlet inner diameter'] = 0.3 options['LV outlet wall thickness'] = 0.025 - options['RV outlet left incline degrees'] = 30.0 + options['RV outlet left incline degrees'] = 25.0 options['RV outlet inner diameter'] = 0.27 options['RV outlet wall thickness'] = 0.025 options['Ventricles outlet element length'] = 0.1 - options['Ventricles outlet spacing'] = 0.04 + options['Ventricles outlet spacing y'] = 0.04 + options['Ventricles outlet spacing z'] = 0.14 + options['Ventricles rotation degrees'] = 20.0 + options['Ventricles translation x'] = -0.22 + options['Ventricles translation y'] = -0.2 return options @staticmethod @@ -84,7 +89,11 @@ def getOrderedOptionNames(): 'RV outlet inner diameter', 'RV outlet wall thickness', 'Ventricles outlet element length', - 'Ventricles outlet spacing'] + 'Ventricles outlet spacing y', + 'Ventricles outlet spacing z', + 'Ventricles rotation degrees', + 'Ventricles translation x', + 'Ventricles translation y'] # want refinement options last for optionName in [ 'Refine', @@ -125,7 +134,8 @@ def checkOptions(options): 'RV outlet inner diameter', 'RV outlet wall thickness', 'Ventricles outlet element length', - 'Ventricles outlet spacing']: + 'Ventricles outlet spacing y', + 'Ventricles outlet spacing z']: if options[key] < 0.0: options[key] = 0.0 if options['Atria major axis rotation degrees'] < -75.0: @@ -188,7 +198,11 @@ def generateBaseMesh(cls, region, options): rvOutletWallThickness = options['RV outlet wall thickness'] rvOutletOuterRadius = rvOutletInnerRadius + rvOutletWallThickness vOutletElementLength = options['Ventricles outlet element length'] - vOutletSpacing = options['Ventricles outlet spacing'] + vOutletSpacingy = options['Ventricles outlet spacing y'] + vOutletSpacingz = options['Ventricles outlet spacing z'] + vRotationRadians = math.radians(options['Ventricles rotation degrees']) + vTranslationx = options['Ventricles translation x'] + vTranslationy = options['Ventricles translation y'] useCrossDerivatives = False # generate heartventricles1 model to add base plane to @@ -226,6 +240,205 @@ def generateBaseMesh(cls, region, options): nodeIdentifier = startNodeIdentifier = getMaximumNodeIdentifier(nodes) + 1 + # move ventricles to fit atria centred around aorta + cosVRotationRadians = math.cos(-vRotationRadians) + sinVRotationRadians = math.sin(-vRotationRadians) + rotationMatrix = fm.createFieldConstant([ cosVRotationRadians, sinVRotationRadians, 0.0, -sinVRotationRadians, cosVRotationRadians, 0.0, 0.0, 0.0, 1.0 ]) + # offset makes top of fibrous ring (excepting cfb rotation) on z == 0 + ventriclesOffset = fm.createFieldConstant([ vTranslationx, vTranslationy, -(fibrousRingThickness + baseHeight + baseThickness)]) + newCoordinates = fm.createFieldAdd(fm.createFieldMatrixMultiply(3, rotationMatrix, coordinates), ventriclesOffset) + fieldassignment = coordinates.createFieldassignment(newCoordinates) + fieldassignment.assign() + fieldassignment = None + newCoordinates = None + ventriclesOffset = None + + # LV outlet points + elementsCountAroundOutlet = 6 + cosLvOutletFrontInclineRadians = math.cos(lvOutletFrontInclineRadians) + sinLvOutletFrontInclineRadians = math.sin(lvOutletFrontInclineRadians) + lvOutletCentre = [ 0.0, 0.0, -fibrousRingThickness ] + axis1 = [ 0.0, -cosLvOutletFrontInclineRadians, sinLvOutletFrontInclineRadians ] + axis2 = [ 1.0, 0.0, 0.0 ] + lvOutletInnerx, lvOutletInnerd1 = createCirclePoints(lvOutletCentre, + vector.setMagnitude(axis1, lvOutletInnerRadius), vector.setMagnitude(axis2, lvOutletInnerRadius), elementsCountAroundOutlet) + lvOutletOuterx, lvOutletOuterd1 = createCirclePoints(lvOutletCentre, + vector.setMagnitude(axis1, lvOutletOuterRadius), vector.setMagnitude(axis2, lvOutletOuterRadius), elementsCountAroundOutlet) + lvOutletd2 = [ 0.0, vOutletElementLength*sinLvOutletFrontInclineRadians, vOutletElementLength*cosLvOutletFrontInclineRadians ] + + # RV outlet points + cosRvOutletLeftInclineRadians = math.cos(rvOutletLeftInclineRadians) + sinRvOutletLeftInclineRadians = math.sin(rvOutletLeftInclineRadians) + dy = vOutletSpacingy + rvOutletOuterRadius + dz = vOutletSpacingz + axis1 = [ 0.0, -cosLvOutletFrontInclineRadians, sinLvOutletFrontInclineRadians ] + axis2 = [ cosRvOutletLeftInclineRadians, sinRvOutletLeftInclineRadians*sinLvOutletFrontInclineRadians, sinRvOutletLeftInclineRadians*cosLvOutletFrontInclineRadians ] + axis3 = vector.crossproduct3(axis1, axis2) + rvOutletCentre = [ (lvOutletOuterx[3][c] - dy*axis1[c] + dz*axis3[c]) for c in range(3) ] + rvOutletInnerx, rvOutletInnerd1 = createCirclePoints(rvOutletCentre, + vector.setMagnitude(axis1, rvOutletInnerRadius), vector.setMagnitude(axis2, rvOutletInnerRadius), elementsCountAroundOutlet) + rvOutletOuterx, rvOutletOuterd1 = createCirclePoints(rvOutletCentre, + vector.setMagnitude(axis1, rvOutletOuterRadius), vector.setMagnitude(axis2, rvOutletOuterRadius), elementsCountAroundOutlet) + rvOutletd2 = [ vOutletElementLength*axis3[c] for c in range(3) ] + + # Left av fibrous ring points + aBaseSlopeHeight = aBaseWallThickness*math.sin(aBaseSlopeRadians) + aBaseSlopeLength = aBaseWallThickness*math.cos(aBaseSlopeRadians) + aortaOuterRadius = lvOutletOuterRadius + aBaseFrontInclineRadians = lvOutletFrontInclineRadians + laCentre, laSeptumRadians, laBaseInnerx, laBaseInnerd1, laBaseInnerd2, laBaseOuterx, laBaseOuterd1, laBaseOuterd2 = \ + getLeftAtriumBasePoints(elementsCountAroundAtrialFreeWall, elementsCountAroundAtrialSeptum, + aBaseInnerMajorMag, aBaseInnerMinorMag, aMajorAxisRadians, + aBaseWallThickness, aBaseSlopeHeight, aBaseSlopeLength, aSeptumThickness, + aortaOuterRadius, aBaseFrontInclineRadians, aBaseSideInclineRadians = 0.0, aBaseBackInclineRadians = 0.0) + # get d3 from inner-outer difference + laBaseInnerd3 = [] + laBaseOuterd3 = [] + for n1 in range(elementsCountAroundAtria): + if laBaseOuterx[n1]: + innerd3 = outerd3 = [ (laBaseOuterx[n1][c] - laBaseInnerx[n1][c]) for c in range(3) ] + else: # septum + innerd3 = [ -2.0*laBaseInnerx[n1][0], 0.0, 0.0 ] + outerd3 = None + laBaseInnerd3.append(innerd3) + laBaseOuterd3.append(outerd3) + # fix cfb, crux centre derivative 3, code duplicated from atria1: + for n1 in [ 0, elementsCountAroundAtrialFreeWall ]: + laBaseOuterd3[n1] = [ 0.0, laBaseOuterx[n1][1] - laBaseInnerx[n1][1], laBaseOuterx[n1][2] - laBaseInnerx[n1][2] ] + + # displace to get bottom points + zero = [ 0.0, 0.0, 0.0 ] + lavInnerx = [ [], laBaseInnerx ] + for n1 in range(elementsCountAroundAtria): + lavInnerx[0].append([ laBaseInnerx[n1][0], laBaseInnerx[n1][1], laBaseInnerx[n1][2] - fibrousRingThickness ]) + lavInnerd1 = [ laBaseInnerd1, laBaseInnerd1 ] + lavInnerd2 = [ [ zero ]*elementsCountAroundAtria, [ zero ]*elementsCountAroundAtria ] + lavInnerd3 = [ laBaseInnerd3, laBaseInnerd3 ] + lavOuterx = [ [], laBaseOuterx ] + for n1 in range(elementsCountAroundAtria): + lavOuterx[0].append([ laBaseOuterx[n1][0], laBaseOuterx[n1][1], laBaseOuterx[n1][2] - fibrousRingThickness ] if (laBaseOuterx[n1]) else None) + lavOuterd1 = [ laBaseOuterd1, laBaseOuterd1 ] + lavOuterd2 = [ [ zero ]*elementsCountAroundAtria, [ zero ]*elementsCountAroundAtria ] + lavOuterd3 = [ laBaseOuterd3, laBaseOuterd3 ] + # Right av fibrous ring points = mirror + ravInnerx = [ [], [] ] + ravInnerd1 = [ [], [] ] + ravInnerd2 = [ [], [] ] + ravInnerd3 = [ [], [] ] + ravOuterx = [ [], [] ] + ravOuterd1 = [ [], [] ] + ravOuterd2 = [ [], [] ] + ravOuterd3 = [ [], [] ] + for n2 in range(2): + for n1 in range(elementsCountAroundAtria): + n1l = elementsCountAroundAtria - 1 - n1 + lavx = lavInnerx [n2][n1l] + lavd1 = lavInnerd1[n2][n1l] + lavd2 = lavInnerd2[n2][n1l] + lavd3 = lavInnerd3[n2][n1l] + ravInnerx [n2].append([ -lavx [0], lavx [1], lavx [2] ]) + ravInnerd1[n2].append([ lavd1[0], -lavd1[1], -lavd1[2] ]) + ravInnerd2[n2].append([ -lavd2[0], lavd2[1], lavd2[2] ]) + ravInnerd3[n2].append([ -lavd3[0], lavd3[1], lavd3[2] ]) + if lavOuterx[n2][n1l]: + lavx = lavOuterx [n2][n1l] + lavd1 = lavOuterd1[n2][n1l] + lavd2 = lavOuterd2[n2][n1l] + lavd3 = lavOuterd3[n2][n1l] + ravOuterx [n2].append([ -lavx [0], lavx [1], lavx [2] ]) + ravOuterd1[n2].append([ lavd1[0], -lavd1[1], -lavd1[2] ]) + ravOuterd2[n2].append([ -lavd2[0], lavd2[1], lavd2[2] ]) + ravOuterd3[n2].append([ -lavd3[0], lavd3[1], lavd3[2] ]) + else: + # no outer points around septum + ravOuterx [n2].append(None) + ravOuterd1[n2].append(None) + ravOuterd2[n2].append(None) + ravOuterd3[n2].append(None) + + # LV outlet nodes + lvOutletNodeId = [ [], [] ] + for n3 in range(2): + if n3 == 0: + lvOutletx = lvOutletInnerx + lvOutletd1 = lvOutletInnerd1 + else: + lvOutletx = lvOutletOuterx + lvOutletd1 = lvOutletOuterd1 + outletNodeId = [] + for n1 in range(elementsCountAroundOutlet): + node = nodes.createNode(nodeIdentifier, nodetemplateLinearS3) # if (n3 == 0) else nodetemplate) + lvOutletNodeId[n3].append(nodeIdentifier) + cache.setNode(node) + coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_VALUE, 1, lvOutletx[n1]) + coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS1, 1, lvOutletd1[n1]) + coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS2, 1, lvOutletd2) + nodeIdentifier += 1 + + # RV outlet nodes + rvOutletNodeId = [ [], [] ] + for n3 in range(2): + if n3 == 0: + rvOutletx = rvOutletInnerx + rvOutletd1 = rvOutletInnerd1 + else: + rvOutletx = rvOutletOuterx + rvOutletd1 = rvOutletOuterd1 + outletNodeId = [] + for n1 in range(elementsCountAroundOutlet): + node = nodes.createNode(nodeIdentifier, nodetemplateLinearS3) # if (n3 == 0) else nodetemplate) + rvOutletNodeId[n3].append(nodeIdentifier) + cache.setNode(node) + coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_VALUE, 1, rvOutletx[n1]) + coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS1, 1, rvOutletd1[n1]) + coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS2, 1, rvOutletd2) + nodeIdentifier += 1 + + # AV fibrous ring nodes + lavInnerNodeId = [ [], [] ] + lavOuterNodeId = [ [], [] ] + ravInnerNodeId = [ [], [] ] + ravOuterNodeId = [ [], [] ] + for n3 in range(2): + for n2 in range(2): + for i in range(2): + if n3 == 0: + if i == 0: + avx, avd1, avd2, avd3 = lavInnerx[n2], lavInnerd1[n2], lavInnerd2[n2], lavInnerd3[n2] + avNodeId = lavInnerNodeId[n2] + else: + avx, avd1, avd2, avd3 = ravInnerx[n2], ravInnerd1[n2], ravInnerd2[n2], ravInnerd3[n2] + avNodeId = ravInnerNodeId[n2] + else: + if i == 0: + avx, avd1, avd2, avd3 = lavOuterx[n2], lavOuterd1[n2], lavOuterd2[n2], lavOuterd3[n2] + avNodeId = lavOuterNodeId[n2] + else: + avx, avd1, avd2, avd3 = ravOuterx[n2], ravOuterd1[n2], ravOuterd2[n2], ravOuterd3[n2] + avNodeId = ravOuterNodeId[n2] + for n1 in range(elementsCountAroundAtria): + if avx[n1] is None: + avNodeId.append(None) + continue + if n3 == 1: + if (n2 == 0) and (((i == 0) and (n1 <= 1)) or ((i == 1) and (n1 >= (elementsCountAroundAtria - 2)))): + # substitute LV outlet node around cfb / fibrous trigones + #avNodeId.append(lvOutletNodeId[1][0] if (n1 == 0) else (lvOutletNodeId[1][-1] if (n1 == 1) else lvOutletNodeId[1][1])) + #continue + pass + if (i == 1) and ((n1 == (elementsCountAroundAtrialSeptum - 1)) or (n1 == (elementsCountAroundAtria - 1))): + # find common nodes on right at cfb and crux + avNodeId.append(lavOuterNodeId[n2][elementsCountAroundAtria - 1 - n1]) + continue + avNodeId.append(nodeIdentifier) + node = nodes.createNode(nodeIdentifier, nodetemplate) + cache.setNode(node) + coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_VALUE, 1, avx [n1]) + coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS1, 1, avd1[n1]) + coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS2, 1, avd2[n1]) + coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS3, 1, avd3[n1]) + nodeIdentifier += 1 + ################# # Create elements ################# From 63c3105c3f18a878ef7591de4e5a9db17a05386e Mon Sep 17 00:00:00 2001 From: Richard Christie Date: Mon, 29 Oct 2018 13:14:03 +1300 Subject: [PATCH 03/25] Add regular elements around base --- .../meshtype_3d_heartventriclesbase1.py | 181 +++++++++++++++++- 1 file changed, 178 insertions(+), 3 deletions(-) diff --git a/scaffoldmaker/meshtypes/meshtype_3d_heartventriclesbase1.py b/scaffoldmaker/meshtypes/meshtype_3d_heartventriclesbase1.py index 2852a3c6..74b23a40 100644 --- a/scaffoldmaker/meshtypes/meshtype_3d_heartventriclesbase1.py +++ b/scaffoldmaker/meshtypes/meshtype_3d_heartventriclesbase1.py @@ -38,14 +38,14 @@ def getDefaultOptions(): # only works with particular numbers of elements around options['Number of elements around LV free wall'] = 5 options['Number of elements around RV free wall'] = 7 - options['Number of elements around atrial free wall'] = 8 + options['Number of elements around atrial free wall'] = 6 options['Number of elements around atrial septum'] = 2 # works best with particular numbers of elements up options['Number of elements up LV apex'] = 1 options['Number of elements up RV'] = 4 # additional options options['Atria base inner major axis length'] = 0.55 - options['Atria base inner minor axis length'] = 0.42 + options['Atria base inner minor axis length'] = 0.45 options['Atria major axis rotation degrees'] = 40.0 options['Atrial septum thickness'] = 0.06 options['Atrial base wall thickness'] = 0.05 @@ -153,7 +153,9 @@ def generateBaseMesh(cls, region, options): """ elementsCountAroundLVFreeWall = options['Number of elements around LV free wall'] elementsCountAroundRVFreeWall = options['Number of elements around RV free wall'] + elementsCountAroundLV = elementsCountAroundLVFreeWall + elementsCountAroundRVFreeWall elementsCountAroundVSeptum = elementsCountAroundRVFreeWall + elementsCountAroundRV = 2*elementsCountAroundRVFreeWall elementsCountUpLVApex = options['Number of elements up LV apex'] elementsCountUpRV = options['Number of elements up RV'] elementsCountUpLV = elementsCountUpLVApex + elementsCountUpRV @@ -207,6 +209,8 @@ def generateBaseMesh(cls, region, options): # generate heartventricles1 model to add base plane to annotationGroups = MeshType_3d_heartventricles1.generateBaseMesh(region, options) + + # find/add annotation groups lvGroup = findAnnotationGroupByName(annotationGroups, 'left ventricle') rvGroup = findAnnotationGroupByName(annotationGroups, 'right ventricle') vSeptumGroup = findAnnotationGroupByName(annotationGroups, 'interventricular septum') @@ -253,6 +257,30 @@ def generateBaseMesh(cls, region, options): newCoordinates = None ventriclesOffset = None + # discover ventricles top LV inner, RV inner, V Outer nodes, coordinates and derivatives + startLVInnerNodeId = 2 + (elementsCountUpLV - 1)*elementsCountAroundLV + lvInnerNodeId = [ (startLVInnerNodeId + n1) for n1 in range(elementsCountAroundLV) ] + startRVInnerNodeId = startLVInnerNodeId + elementsCountAroundLV + elementsCountAroundRVFreeWall + 1 + elementsCountAroundRV*(elementsCountUpRV - 1) + rvInnerNodeId = [ (startRVInnerNodeId + n1) for n1 in range(elementsCountAroundRV) ] + startVOuterNodeId = startRVInnerNodeId + elementsCountAroundRV + 1 + (elementsCountUpLV - 1)*elementsCountAroundLV + vOuterNodeId = [ (startVOuterNodeId + n1) for n1 in range(elementsCountAroundLV) ] + for nodeId in [ lvInnerNodeId, rvInnerNodeId, vOuterNodeId ]: + vx = [] + vd2 = [] + for n1 in range(len(nodeId)): + node = nodes.findNodeByIdentifier(nodeId[n1]) + cache.setNode(node) + result, x = coordinates.getNodeParameters(cache, -1, Node.VALUE_LABEL_VALUE, 1, 3) + result, d2 = coordinates.getNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS2, 1, 3) + vx .append(x) + vd2.append(d2) + if nodeId is lvInnerNodeId: + lvInnerx, lvInnerd2 = vx, vd2 + elif nodeId is rvInnerNodeId: + rvInnerx, rvInnerd2 = vx, vd2 + else: + vOuterx, vOuterd2 = vx, vd2 + # LV outlet points elementsCountAroundOutlet = 6 cosLvOutletFrontInclineRadians = math.cos(lvOutletFrontInclineRadians) @@ -305,7 +333,6 @@ def generateBaseMesh(cls, region, options): # fix cfb, crux centre derivative 3, code duplicated from atria1: for n1 in [ 0, elementsCountAroundAtrialFreeWall ]: laBaseOuterd3[n1] = [ 0.0, laBaseOuterx[n1][1] - laBaseInnerx[n1][1], laBaseOuterx[n1][2] - laBaseInnerx[n1][2] ] - # displace to get bottom points zero = [ 0.0, 0.0, 0.0 ] lavInnerx = [ [], laBaseInnerx ] @@ -355,6 +382,26 @@ def generateBaseMesh(cls, region, options): ravOuterd1[n2].append(None) ravOuterd2[n2].append(None) ravOuterd3[n2].append(None) + # get av bottom derivative 2 from Hermite-Lagrange interpolation from top row of ventricles + elementsCountLVFreeWallRegular = elementsCountAroundAtrialFreeWall//2 + elementsCountRVFreeWallRegular = elementsCountAroundAtrialFreeWall//2 + for n1 in range(elementsCountLVFreeWallRegular + 1): + noa = elementsCountAroundAtrialFreeWall - elementsCountLVFreeWallRegular + n1 + nov = elementsCountAroundLVFreeWall - elementsCountLVFreeWallRegular + n1 + lavInnerd2[0][noa] = interpolateHermiteLagrangeDerivative(lvInnerx[nov], lvInnerd2[nov], lavInnerx[0][noa], 1.0) + lavOuterd2[0][noa] = interpolateHermiteLagrangeDerivative(vOuterx[nov], vOuterd2[nov], lavOuterx[0][noa], 1.0) + for n1 in range(1, elementsCountRVFreeWallRegular + 1): + noa = elementsCountAroundAtrialSeptum + n1 - 1 + nov = elementsCountAroundLVFreeWall + n1 + ravInnerd2[0][noa] = interpolateHermiteLagrangeDerivative(rvInnerx[n1 - 1], rvInnerd2[n1 - 1], ravInnerx[0][noa], 1.0) + ravOuterd2[0][noa] = interpolateHermiteLagrangeDerivative(vOuterx[nov], vOuterd2[nov], ravOuterx[0][noa], 1.0) + for n1 in range(elementsCountAroundAtrialSeptum + 1): + noa = (elementsCountAroundAtrialFreeWall + n1)%elementsCountAroundAtria + nov = elementsCountAroundLVFreeWall + n1 + lavInnerd2[0][noa] = interpolateHermiteLagrangeDerivative(lvInnerx[nov], lvInnerd2[nov], lavInnerx[0][noa], 1.0) + noa = (elementsCountAroundAtrialSeptum - 1 + elementsCountAroundAtria - n1)%elementsCountAroundAtria + nov = elementsCountAroundRV - n1 - 1 + ravInnerd2[0][noa] = interpolateHermiteLagrangeDerivative(rvInnerx[nov], rvInnerd2[nov], ravInnerx[0][noa], 1.0) # LV outlet nodes lvOutletNodeId = [ [], [] ] @@ -460,6 +507,134 @@ def generateBaseMesh(cls, region, options): elementtemplate1 = mesh.createElementtemplate() elementtemplate1.setElementShapeType(Element.SHAPE_TYPE_CUBE) + # LV base elements starting at anterior interventricular sulcus + for e in range(-1, elementsCountAroundLVFreeWall): + eft1 = eft + nids = None + scalefactors = None + meshGroups = [ lvMeshGroup ] + + if e < (elementsCountAroundLVFreeWall - elementsCountLVFreeWallRegular): + continue + else: # regular elements between LV free wall and left atrium + noa = elementsCountAroundAtrialFreeWall - elementsCountAroundLVFreeWall + e + nov = e + nids = [ lvInnerNodeId[nov], lvInnerNodeId[nov + 1], lavInnerNodeId[0][noa], lavInnerNodeId[0][noa + 1], + vOuterNodeId[nov], vOuterNodeId[nov + 1], lavOuterNodeId[0][noa], lavOuterNodeId[0][noa + 1] ] + if e == (elementsCountAroundLVFreeWall - 1): + # general linear map d3 adjacent to collapsed crux, transition to atria + eft1 = tricubichermite.createEftNoCrossDerivatives() + remapEftNodeValueLabel(eft1, [ 8 ], Node.VALUE_LABEL_D_DS3, [ ( Node.VALUE_LABEL_D_DS1, [] ), ( Node.VALUE_LABEL_D_DS3, []) ]) + + result = elementtemplate1.defineField(coordinates, -1, eft1) + element = mesh.createElement(elementIdentifier, elementtemplate1) + result2 = element.setNodesByIdentifier(eft1, nids) + if scalefactors: + result3 = element.setScaleFactors(eft1, scalefactors) + else: + result3 = 7 + #print('create element lv base', elementIdentifier, result, result2, result3, nids) + elementIdentifier += 1 + + for meshGroup in meshGroups: + meshGroup.addElement(element) + + # RV base elements, starting at crux / posterior interventricular sulcus + for e in range(-1, elementsCountAroundRVFreeWall): + eft1 = eft + nids = None + scalefactors = None + meshGroups = [ rvMeshGroup ] + + noa = elementsCountAroundAtrialSeptum - 1 + e + niv = e - 1 + nov = elementsCountAroundLVFreeWall + e + if e == -1: + # crux / posterior interventricular sulcus: collapsed to 6 element wedge + nids = [ lvInnerNodeId[elementsCountAroundLVFreeWall], rvInnerNodeId[niv + 1], lavInnerNodeId[0][elementsCountAroundAtrialFreeWall], ravInnerNodeId[0][noa + 1], + vOuterNodeId[nov + 1], ravOuterNodeId[0][noa + 1] ] + meshGroups += [ lvMeshGroup ] + eft1 = tricubichermite.createEftNoCrossDerivatives() + setEftScaleFactorIds(eft1, [1], []) + scalefactors = [ -1.0 ] + remapEftNodeValueLabel(eft1, [ 1, 3 ], Node.VALUE_LABEL_D_DS1, [ ( Node.VALUE_LABEL_D_DS1, [] ), ( Node.VALUE_LABEL_D_DS3, [] ) ]) + remapEftNodeValueLabel(eft1, [ 2 ], Node.VALUE_LABEL_D_DS3, [ ( Node.VALUE_LABEL_D_DS1, [] ), ( Node.VALUE_LABEL_D_DS3, [] ) ]) + remapEftNodeValueLabel(eft1, [ 2 ], Node.VALUE_LABEL_D_DS1, [ ( Node.VALUE_LABEL_D_DS3, [1] ) ]) # from ventricles + remapEftNodeValueLabel(eft1, [ 4 ], Node.VALUE_LABEL_D_DS1, [ ( Node.VALUE_LABEL_D_DS1, [] ), ( Node.VALUE_LABEL_D_DS3, [1] ) ]) + remapEftNodeValueLabel(eft1, [ 5, 6, 7, 8 ], Node.VALUE_LABEL_D_DS1, []) + remapEftNodeValueLabel(eft1, [ 6, 8 ], Node.VALUE_LABEL_D_DS3, [ ( Node.VALUE_LABEL_D_DS1, [1] ), ( Node.VALUE_LABEL_D_DS3, []) ]) + remapEftNodeValueLabel(eft1, [ 7 ], Node.VALUE_LABEL_D_DS3, [ ( Node.VALUE_LABEL_D_DS1, [] ), ( Node.VALUE_LABEL_D_DS3, []) ]) + ln_map = [ 1, 2, 3, 4, 5, 5, 6, 6 ] + remapEftLocalNodes(eft1, 6, ln_map) + elif e < elementsCountRVFreeWallRegular: + nids = [ rvInnerNodeId[niv], rvInnerNodeId[niv + 1], ravInnerNodeId[0][noa], ravInnerNodeId[0][noa + 1], + vOuterNodeId[nov], vOuterNodeId[nov + 1], ravOuterNodeId[0][noa], ravOuterNodeId[0][noa + 1] ] + if e == 0: + # general linear map d3 adjacent to collapsed crux, transition to atria + eft1 = tricubichermite.createEftNoCrossDerivatives() + setEftScaleFactorIds(eft1, [1], []) + scalefactors = [ -1.0 ] + remapEftNodeValueLabel(eft1, [ 1 ], Node.VALUE_LABEL_D_DS3, [ ( Node.VALUE_LABEL_D_DS1, [] ), ( Node.VALUE_LABEL_D_DS3, [] ) ]) + remapEftNodeValueLabel(eft1, [ 5, 7 ], Node.VALUE_LABEL_D_DS3, [ ( Node.VALUE_LABEL_D_DS1, [1] ), ( Node.VALUE_LABEL_D_DS3, []) ]) + else: + continue + + result = elementtemplate1.defineField(coordinates, -1, eft1) + element = mesh.createElement(elementIdentifier, elementtemplate1) + result2 = element.setNodesByIdentifier(eft1, nids) + if scalefactors: + result3 = element.setScaleFactors(eft1, scalefactors) + else: + result3 = 7 + #print('create element rv base', elementIdentifier, result, result2, result3, nids) + elementIdentifier += 1 + + for meshGroup in meshGroups: + meshGroup.addElement(element) + + # interventricular septum elements + for e in range(elementsCountAroundAtrialSeptum): + eft1 = eft + nids = None + scalefactors = None + meshGroups = [ lvMeshGroup, rvMeshGroup, vSeptumMeshGroup ] + + lv1 = elementsCountAroundLVFreeWall + e + lv2 = (lv1 + 1)%elementsCountAroundLV + la1 = elementsCountAroundAtrialFreeWall + e + la2 = (la1 + 1)%elementsCountAroundAtria + rv1 = elementsCountAroundRV - 1 - e + rv2 = rv1 - 1 + ra1 = elementsCountAroundAtrialSeptum - 1 - e + ra2 = ra1 - 1 + nids = [ lvInnerNodeId[lv1], lvInnerNodeId[lv2], lavInnerNodeId[0][la1], lavInnerNodeId[0][la2], + rvInnerNodeId[rv1], rvInnerNodeId[rv2], ravInnerNodeId[0][ra1], ravInnerNodeId[0][ra2] ] + + eft1 = tricubichermite.createEftNoCrossDerivatives() + setEftScaleFactorIds(eft1, [1], []) + scalefactors = [ -1.0 ] + if e == 0: + # general linear map d3 adjacent to collapsed posterior interventricular sulcus + scaleEftNodeValueLabels(eft1, [ 5, 6, 7, 8 ], [ Node.VALUE_LABEL_D_DS1 ], [ 1 ]) + scaleEftNodeValueLabels(eft1, [ 5, 6, 8 ], [ Node.VALUE_LABEL_D_DS3 ], [ 1 ]) + remapEftNodeValueLabel(eft1, [ 1, 3 ], Node.VALUE_LABEL_D_DS3, [ ( Node.VALUE_LABEL_D_DS1, [] ), ( Node.VALUE_LABEL_D_DS3, [] ) ]) + remapEftNodeValueLabel(eft1, [ 7 ], Node.VALUE_LABEL_D_DS3, [ ( Node.VALUE_LABEL_D_DS1, [] ), ( Node.VALUE_LABEL_D_DS3, [1] ) ]) + else: + scaleEftNodeValueLabels(eft1, [ 5, 6, 7, 8 ], [ Node.VALUE_LABEL_D_DS1, Node.VALUE_LABEL_D_DS3 ], [ 1 ]) + + result = elementtemplate1.defineField(coordinates, -1, eft1) + element = mesh.createElement(elementIdentifier, elementtemplate1) + result2 = element.setNodesByIdentifier(eft1, nids) + if scalefactors: + result3 = element.setScaleFactors(eft1, scalefactors) + else: + result3 = 7 + #print('create element sp base', elementIdentifier, result, result2, result3, nids) + elementIdentifier += 1 + + for meshGroup in meshGroups: + meshGroup.addElement(element) + fm.endChange() return annotationGroups From 2c50a289192969d44c6446a5959ad2a567681460 Mon Sep 17 00:00:00 2001 From: Richard Christie Date: Thu, 1 Nov 2018 16:24:42 +1300 Subject: [PATCH 04/25] Add cfb, more septum elements --- .../meshtype_3d_heartventriclesbase1.py | 130 ++++++++++++++---- 1 file changed, 103 insertions(+), 27 deletions(-) diff --git a/scaffoldmaker/meshtypes/meshtype_3d_heartventriclesbase1.py b/scaffoldmaker/meshtypes/meshtype_3d_heartventriclesbase1.py index 74b23a40..52a056b1 100644 --- a/scaffoldmaker/meshtypes/meshtype_3d_heartventriclesbase1.py +++ b/scaffoldmaker/meshtypes/meshtype_3d_heartventriclesbase1.py @@ -39,7 +39,7 @@ def getDefaultOptions(): options['Number of elements around LV free wall'] = 5 options['Number of elements around RV free wall'] = 7 options['Number of elements around atrial free wall'] = 6 - options['Number of elements around atrial septum'] = 2 + options['Number of elements around atrial septum'] = 3 # works best with particular numbers of elements up options['Number of elements up LV apex'] = 1 options['Number of elements up RV'] = 4 @@ -50,7 +50,7 @@ def getDefaultOptions(): options['Atrial septum thickness'] = 0.06 options['Atrial base wall thickness'] = 0.05 options['Atrial base slope degrees'] = 15.0 - options['Base height'] = 0.14 + options['Base height'] = 0.1 options['Base thickness'] = 0.06 options['Fibrous ring thickness'] = 0.01 options['LV outlet front incline degrees'] = 15.0 @@ -60,11 +60,11 @@ def getDefaultOptions(): options['RV outlet inner diameter'] = 0.27 options['RV outlet wall thickness'] = 0.025 options['Ventricles outlet element length'] = 0.1 - options['Ventricles outlet spacing y'] = 0.04 + options['Ventricles outlet spacing y'] = 0.02 options['Ventricles outlet spacing z'] = 0.14 - options['Ventricles rotation degrees'] = 20.0 + options['Ventricles rotation degrees'] = 16.0 options['Ventricles translation x'] = -0.22 - options['Ventricles translation y'] = -0.2 + options['Ventricles translation y'] = -0.22 return options @staticmethod @@ -293,6 +293,8 @@ def generateBaseMesh(cls, region, options): lvOutletOuterx, lvOutletOuterd1 = createCirclePoints(lvOutletCentre, vector.setMagnitude(axis1, lvOutletOuterRadius), vector.setMagnitude(axis2, lvOutletOuterRadius), elementsCountAroundOutlet) lvOutletd2 = [ 0.0, vOutletElementLength*sinLvOutletFrontInclineRadians, vOutletElementLength*cosLvOutletFrontInclineRadians ] + zero = [ 0.0, 0.0, 0.0 ] + lvOutletOuterd3 = [ vector.setMagnitude([ (lvOutletOuterx[n1][c] - lvOutletCentre[c]) for c in range(3) ], vSeptumThickness) for n1 in range(elementsCountAroundOutlet) ] # RV outlet points cosRvOutletLeftInclineRadians = math.cos(rvOutletLeftInclineRadians) @@ -308,6 +310,14 @@ def generateBaseMesh(cls, region, options): rvOutletOuterx, rvOutletOuterd1 = createCirclePoints(rvOutletCentre, vector.setMagnitude(axis1, rvOutletOuterRadius), vector.setMagnitude(axis2, rvOutletOuterRadius), elementsCountAroundOutlet) rvOutletd2 = [ vOutletElementLength*axis3[c] for c in range(3) ] + rvOutletOuterd3 = [ None ]*elementsCountAroundOutlet + + # fix derivative 3 between lv, rv outlets + lx = lvOutletOuterx[elementsCountAroundOutlet//2] + rx = rvOutletOuterx[0] + d3 = [ (rx[c] - lx[c]) for c in range(3) ] + lvOutletOuterd3[elementsCountAroundOutlet//2] = d3 + rvOutletOuterd3[0] = [ -d for d in d3 ] # Left av fibrous ring points aBaseSlopeHeight = aBaseWallThickness*math.sin(aBaseSlopeRadians) @@ -334,7 +344,6 @@ def generateBaseMesh(cls, region, options): for n1 in [ 0, elementsCountAroundAtrialFreeWall ]: laBaseOuterd3[n1] = [ 0.0, laBaseOuterx[n1][1] - laBaseInnerx[n1][1], laBaseOuterx[n1][2] - laBaseInnerx[n1][2] ] # displace to get bottom points - zero = [ 0.0, 0.0, 0.0 ] lavInnerx = [ [], laBaseInnerx ] for n1 in range(elementsCountAroundAtria): lavInnerx[0].append([ laBaseInnerx[n1][0], laBaseInnerx[n1][1], laBaseInnerx[n1][2] - fibrousRingThickness ]) @@ -403,23 +412,32 @@ def generateBaseMesh(cls, region, options): nov = elementsCountAroundRV - n1 - 1 ravInnerd2[0][noa] = interpolateHermiteLagrangeDerivative(rvInnerx[nov], rvInnerd2[nov], ravInnerx[0][noa], 1.0) + # copy derivative 3 from av points to LV outlet at centre, left and right cfb: + lvOutletOuterd3[0] = lavOuterd3[0][0] + lvOutletOuterd3[1] = [ -d for d in ravOuterd3[0][-2] ] + lvOutletOuterd3[-1] = [ -d for d in lavOuterd3[0][1] ] + # LV outlet nodes lvOutletNodeId = [ [], [] ] for n3 in range(2): if n3 == 0: lvOutletx = lvOutletInnerx lvOutletd1 = lvOutletInnerd1 + lvOutletd3 = None else: lvOutletx = lvOutletOuterx lvOutletd1 = lvOutletOuterd1 + lvOutletd3 = lvOutletOuterd3 outletNodeId = [] for n1 in range(elementsCountAroundOutlet): - node = nodes.createNode(nodeIdentifier, nodetemplateLinearS3) # if (n3 == 0) else nodetemplate) + node = nodes.createNode(nodeIdentifier, nodetemplate if lvOutletd3 else nodetemplateLinearS3) lvOutletNodeId[n3].append(nodeIdentifier) cache.setNode(node) coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_VALUE, 1, lvOutletx[n1]) coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS1, 1, lvOutletd1[n1]) coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS2, 1, lvOutletd2) + if lvOutletd3: + coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS3, 1, lvOutletd3[n1]) nodeIdentifier += 1 # RV outlet nodes @@ -428,17 +446,21 @@ def generateBaseMesh(cls, region, options): if n3 == 0: rvOutletx = rvOutletInnerx rvOutletd1 = rvOutletInnerd1 + rvOutletd3 = None else: rvOutletx = rvOutletOuterx rvOutletd1 = rvOutletOuterd1 + rvOutletd3 = rvOutletOuterd3 outletNodeId = [] for n1 in range(elementsCountAroundOutlet): - node = nodes.createNode(nodeIdentifier, nodetemplateLinearS3) # if (n3 == 0) else nodetemplate) + node = nodes.createNode(nodeIdentifier, nodetemplate if (rvOutletd3 and rvOutletd3[n1]) else nodetemplateLinearS3) rvOutletNodeId[n3].append(nodeIdentifier) cache.setNode(node) coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_VALUE, 1, rvOutletx[n1]) coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS1, 1, rvOutletd1[n1]) coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS2, 1, rvOutletd2) + if (rvOutletd3 and rvOutletd3[n1]): + coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS3, 1, rvOutletd3[n1]) nodeIdentifier += 1 # AV fibrous ring nodes @@ -447,7 +469,7 @@ def generateBaseMesh(cls, region, options): ravInnerNodeId = [ [], [] ] ravOuterNodeId = [ [], [] ] for n3 in range(2): - for n2 in range(2): + for n2 in range(1): # 2): for i in range(2): if n3 == 0: if i == 0: @@ -470,9 +492,8 @@ def generateBaseMesh(cls, region, options): if n3 == 1: if (n2 == 0) and (((i == 0) and (n1 <= 1)) or ((i == 1) and (n1 >= (elementsCountAroundAtria - 2)))): # substitute LV outlet node around cfb / fibrous trigones - #avNodeId.append(lvOutletNodeId[1][0] if (n1 == 0) else (lvOutletNodeId[1][-1] if (n1 == 1) else lvOutletNodeId[1][1])) - #continue - pass + avNodeId.append(lvOutletNodeId[1][0] if (n1 == 0) else (lvOutletNodeId[1][-1] if (n1 == 1) else lvOutletNodeId[1][1])) + continue if (i == 1) and ((n1 == (elementsCountAroundAtrialSeptum - 1)) or (n1 == (elementsCountAroundAtria - 1))): # find common nodes on right at cfb and crux avNodeId.append(lavOuterNodeId[n2][elementsCountAroundAtria - 1 - n1]) @@ -593,7 +614,7 @@ def generateBaseMesh(cls, region, options): meshGroup.addElement(element) # interventricular septum elements - for e in range(elementsCountAroundAtrialSeptum): + for e in range(elementsCountAroundVSeptum + 1): eft1 = eft nids = None scalefactors = None @@ -601,26 +622,81 @@ def generateBaseMesh(cls, region, options): lv1 = elementsCountAroundLVFreeWall + e lv2 = (lv1 + 1)%elementsCountAroundLV - la1 = elementsCountAroundAtrialFreeWall + e - la2 = (la1 + 1)%elementsCountAroundAtria rv1 = elementsCountAroundRV - 1 - e rv2 = rv1 - 1 - ra1 = elementsCountAroundAtrialSeptum - 1 - e - ra2 = ra1 - 1 - nids = [ lvInnerNodeId[lv1], lvInnerNodeId[lv2], lavInnerNodeId[0][la1], lavInnerNodeId[0][la2], - rvInnerNodeId[rv1], rvInnerNodeId[rv2], ravInnerNodeId[0][ra1], ravInnerNodeId[0][ra2] ] eft1 = tricubichermite.createEftNoCrossDerivatives() setEftScaleFactorIds(eft1, [1], []) scalefactors = [ -1.0 ] - if e == 0: - # general linear map d3 adjacent to collapsed posterior interventricular sulcus - scaleEftNodeValueLabels(eft1, [ 5, 6, 7, 8 ], [ Node.VALUE_LABEL_D_DS1 ], [ 1 ]) - scaleEftNodeValueLabels(eft1, [ 5, 6, 8 ], [ Node.VALUE_LABEL_D_DS3 ], [ 1 ]) - remapEftNodeValueLabel(eft1, [ 1, 3 ], Node.VALUE_LABEL_D_DS3, [ ( Node.VALUE_LABEL_D_DS1, [] ), ( Node.VALUE_LABEL_D_DS3, [] ) ]) - remapEftNodeValueLabel(eft1, [ 7 ], Node.VALUE_LABEL_D_DS3, [ ( Node.VALUE_LABEL_D_DS1, [] ), ( Node.VALUE_LABEL_D_DS3, [1] ) ]) + if e < elementsCountAroundAtrialSeptum: + la1 = (elementsCountAroundAtrialFreeWall + e)%elementsCountAroundAtria + la2 = (la1 + 1)%elementsCountAroundAtria + ra1 = elementsCountAroundAtrialSeptum - 1 - e + ra2 = ra1 - 1 + nids = [ lvInnerNodeId[lv1], lvInnerNodeId[lv2], lavInnerNodeId[0][la1], lavInnerNodeId[0][la2], + rvInnerNodeId[rv1], rvInnerNodeId[rv2], ravInnerNodeId[0][ra1], ravInnerNodeId[0][ra2] ] + if e == 0: + # general linear map d3 adjacent to collapsed posterior interventricular sulcus + scaleEftNodeValueLabels(eft1, [ 5, 6, 7, 8 ], [ Node.VALUE_LABEL_D_DS1 ], [ 1 ]) + scaleEftNodeValueLabels(eft1, [ 5, 6, 8 ], [ Node.VALUE_LABEL_D_DS3 ], [ 1 ]) + remapEftNodeValueLabel(eft1, [ 1, 3 ], Node.VALUE_LABEL_D_DS3, [ ( Node.VALUE_LABEL_D_DS1, [] ), ( Node.VALUE_LABEL_D_DS3, [] ) ]) + remapEftNodeValueLabel(eft1, [ 7 ], Node.VALUE_LABEL_D_DS3, [ ( Node.VALUE_LABEL_D_DS1, [] ), ( Node.VALUE_LABEL_D_DS3, [1] ) ]) + elif e == (elementsCountAroundAtrialSeptum - 1): + # general linear map d3 adjacent to cfb + scaleEftNodeValueLabels(eft1, [ 5, 6, 7, 8 ], [ Node.VALUE_LABEL_D_DS1 ], [ 1 ]) + scaleEftNodeValueLabels(eft1, [ 5, 6, 7 ], [ Node.VALUE_LABEL_D_DS3 ], [ 1 ]) + remapEftNodeValueLabel(eft1, [ 4 ], Node.VALUE_LABEL_D_DS3, [ ( Node.VALUE_LABEL_D_DS1, [1] ), ( Node.VALUE_LABEL_D_DS3, [] ) ]) + remapEftNodeValueLabel(eft1, [ 8 ], Node.VALUE_LABEL_D_DS3, [ ( Node.VALUE_LABEL_D_DS1, [1] ), ( Node.VALUE_LABEL_D_DS3, [1] ) ]) + else: + scaleEftNodeValueLabels(eft1, [ 5, 6, 7, 8 ], [ Node.VALUE_LABEL_D_DS1, Node.VALUE_LABEL_D_DS3 ], [ 1 ]) + elif e == elementsCountAroundAtrialSeptum: + # cfb: 6 node inclined wedge element + la1 = (elementsCountAroundAtrialFreeWall + e)%elementsCountAroundAtria + ra1 = elementsCountAroundAtrialSeptum - 1 - e + nids = [ lvInnerNodeId[lv1], lvOutletNodeId[0][0], lavInnerNodeId[0][la1], lvOutletNodeId[1][0], + rvInnerNodeId[rv1], ravInnerNodeId[0][ra1] ] + scaleEftNodeValueLabels(eft1, [ 5 ], [ Node.VALUE_LABEL_D_DS3 ], [ 1 ]) + remapEftNodeValueLabel(eft1, [ 1 ], Node.VALUE_LABEL_D_DS1, [ ( Node.VALUE_LABEL_D_DS1, [] ), ( Node.VALUE_LABEL_D_DS2, [] ) ]) + tricubichermite.setEftLinearDerivative(eft1, [ 2, 4 ], Node.VALUE_LABEL_D_DS2, 2, 4, 1) + remapEftNodeValueLabel(eft1, [ 2 ], Node.VALUE_LABEL_D_DS1, [ ( Node.VALUE_LABEL_D_DS2, []) ]) + remapEftNodeValueLabel(eft1, [ 4 ], Node.VALUE_LABEL_D_DS1, [ ( Node.VALUE_LABEL_D_DS1, [] ), ( Node.VALUE_LABEL_D_DS3, []) ]) + remapEftNodeValueLabel(eft1, [ 2, 4, 6, 8 ], Node.VALUE_LABEL_D_DS3, []) + #remapEftNodeValueLabel(eft1, [ 5 ], Node.VALUE_LABEL_D_DS1, [ ( Node.VALUE_LABEL_D_DS1, [1] ), ( Node.VALUE_LABEL_D_DS2, [] ) ]) + remapEftNodeValueLabel(eft1, [ 5 ], Node.VALUE_LABEL_D_DS1, [ ( Node.VALUE_LABEL_D_DS2, [] ), ( Node.VALUE_LABEL_D_DS3, [] ) ]) + remapEftNodeValueLabel(eft1, [ 3 ], Node.VALUE_LABEL_D_DS3, [ ( Node.VALUE_LABEL_D_DS1, [1] ), ( Node.VALUE_LABEL_D_DS3, [] ) ]) + remapEftNodeValueLabel(eft1, [ 3 ], Node.VALUE_LABEL_D_DS1, [ ( Node.VALUE_LABEL_D_DS3, [] ) ]) + remapEftNodeValueLabel(eft1, [ 7 ], Node.VALUE_LABEL_D_DS3, [ ( Node.VALUE_LABEL_D_DS1, [1] ), ( Node.VALUE_LABEL_D_DS3, [1] ) ]) + remapEftNodeValueLabel(eft1, [ 7 ], Node.VALUE_LABEL_D_DS1, [ ( Node.VALUE_LABEL_D_DS3, [] ) ]) + remapEftNodeValueLabel(eft1, [ 6 ], Node.VALUE_LABEL_D_DS1, [ ( Node.VALUE_LABEL_D_DS1, [1] ), ( Node.VALUE_LABEL_D_DS2, []) ]) + remapEftNodeValueLabel(eft1, [ 8 ], Node.VALUE_LABEL_D_DS1, [ ( Node.VALUE_LABEL_D_DS1, [1] ), ( Node.VALUE_LABEL_D_DS3, []) ]) + tricubichermite.setEftLinearDerivative(eft1, [ 6, 8 ], Node.VALUE_LABEL_D_DS2, 2, 4, 1) + ln_map = [ 1, 2, 3, 4, 5, 2, 6, 4 ] + remapEftLocalNodes(eft1, 6, ln_map) else: - scaleEftNodeValueLabels(eft1, [ 5, 6, 7, 8 ], [ Node.VALUE_LABEL_D_DS1, Node.VALUE_LABEL_D_DS3 ], [ 1 ]) + # wedge elements along remainder of interventricular septum + lv1 -= 1 + lv2 -= 1 + rv1 += 1 + rv2 += 1 + lo1 = e - elementsCountAroundAtrialSeptum - 1 + lo2 = lo1 + 1 + nids = [ lvInnerNodeId[lv1], lvInnerNodeId[lv2], lvOutletNodeId[0][lo1], lvOutletNodeId[0][lo2], + rvInnerNodeId[rv1], rvInnerNodeId[rv2] ] + scaleEftNodeValueLabels(eft1, [ 5, 6 ], [ Node.VALUE_LABEL_D_DS1, Node.VALUE_LABEL_D_DS3 ], [ 1 ]) + remapEftNodeValueLabel(eft1, [ 3, 4, 7, 8 ], Node.VALUE_LABEL_D_DS3, []) + if lo1 == 0: + remapEftNodeValueLabel(eft1, [ 1 ], Node.VALUE_LABEL_D_DS2, [ ( Node.VALUE_LABEL_D_DS1, [] ), ( Node.VALUE_LABEL_D_DS2, [] ) ]) + remapEftNodeValueLabel(eft1, [ 5 ], Node.VALUE_LABEL_D_DS2, [ ( Node.VALUE_LABEL_D_DS2, [] ), ( Node.VALUE_LABEL_D_DS3, [] ) ]) + remapEftNodeValueLabel(eft1, [ 7 ], Node.VALUE_LABEL_D_DS2, [ ( Node.VALUE_LABEL_D_DS1, [1] ), ( Node.VALUE_LABEL_D_DS2, []) ]) + #remapEftNodeValueLabel(eft1, [ 8 ], Node.VALUE_LABEL_D_DS2, [ ( Node.VALUE_LABEL_D_DS2, [] ), ( Node.VALUE_LABEL_D_DS3, [1]) ]) + elif lv2 == 0: + # general linear map d3 adjacent to collapsed anterior interventricular sulcus + remapEftNodeValueLabel(eft1, [ 2 ], Node.VALUE_LABEL_D_DS3, [ ( Node.VALUE_LABEL_D_DS1, [1] ), ( Node.VALUE_LABEL_D_DS3, [] ) ]) + else: + #remapEftNodeValueLabel(eft1, [ 7, 8 ], Node.VALUE_LABEL_D_DS2, [ ( Node.VALUE_LABEL_D_DS2, [] ), ( Node.VALUE_LABEL_D_DS3, [1]) ]) + pass + ln_map = [ 1, 2, 3, 4, 5, 6, 3, 4 ] + remapEftLocalNodes(eft1, 6, ln_map) result = elementtemplate1.defineField(coordinates, -1, eft1) element = mesh.createElement(elementIdentifier, elementtemplate1) @@ -629,7 +705,7 @@ def generateBaseMesh(cls, region, options): result3 = element.setScaleFactors(eft1, scalefactors) else: result3 = 7 - #print('create element sp base', elementIdentifier, result, result2, result3, nids) + print('create element sp base', elementIdentifier, result, result2, result3, nids) elementIdentifier += 1 for meshGroup in meshGroups: From 463840b05a79ba3d34a2ae8b2a6b90163b6f93de Mon Sep 17 00:00:00 2001 From: Richard Christie Date: Mon, 5 Nov 2018 13:10:53 +1300 Subject: [PATCH 05/25] Use wedge elements over v septum --- .../meshtypes/meshtype_3d_heartatria1.py | 2 +- .../meshtype_3d_heartventriclesbase1.py | 51 +++++++++++++++---- 2 files changed, 42 insertions(+), 11 deletions(-) diff --git a/scaffoldmaker/meshtypes/meshtype_3d_heartatria1.py b/scaffoldmaker/meshtypes/meshtype_3d_heartatria1.py index 98a36794..1f7b8f93 100644 --- a/scaffoldmaker/meshtypes/meshtype_3d_heartatria1.py +++ b/scaffoldmaker/meshtypes/meshtype_3d_heartatria1.py @@ -29,7 +29,7 @@ def getName(): def getDefaultOptions(): return { 'Number of elements around atrial free wall' : 8, - 'Number of elements around atrial septum' : 2, + 'Number of elements around atrial septum' : 3, 'Number of elements up atria' : 4, 'Number of elements inlet' : 2, 'Atria base inner major axis length' : 0.55, diff --git a/scaffoldmaker/meshtypes/meshtype_3d_heartventriclesbase1.py b/scaffoldmaker/meshtypes/meshtype_3d_heartventriclesbase1.py index 52a056b1..ee1da6af 100644 --- a/scaffoldmaker/meshtypes/meshtype_3d_heartventriclesbase1.py +++ b/scaffoldmaker/meshtypes/meshtype_3d_heartventriclesbase1.py @@ -50,7 +50,7 @@ def getDefaultOptions(): options['Atrial septum thickness'] = 0.06 options['Atrial base wall thickness'] = 0.05 options['Atrial base slope degrees'] = 15.0 - options['Base height'] = 0.1 + options['Base height'] = 0.12 options['Base thickness'] = 0.06 options['Fibrous ring thickness'] = 0.01 options['LV outlet front incline degrees'] = 15.0 @@ -319,6 +319,22 @@ def generateBaseMesh(cls, region, options): lvOutletOuterd3[elementsCountAroundOutlet//2] = d3 rvOutletOuterd3[0] = [ -d for d in d3 ] + # create point above anterior ventricular septum end + fd2 = [ -d for d in vOuterd2[0] ] + px, pd1 = sampleCubicHermiteCurves([ lvOutletOuterx[3], vOuterx[0] ], [ [ 2.0*d for d in lvOutletOuterd1[3] ], fd2 ], 2, arcLengthDerivatives=True)[0:2] + pd1 = smoothCubicHermiteDerivativesLine(px, [ lvOutletOuterd1[3], pd1[1], fd2 ], fixStartDerivative=True, fixEndDerivative=True) + pd3 = smoothCubicHermiteDerivativesLine([ lvOutletOuterx[4], px[1], rvOutletOuterx[-1] ], [ lvOutletOuterd3[4], zero, rvOutletd2 ], fixEndDerivative=True) + n1 = elementsCountAroundRVFreeWall - 1 + d3 = vector.crossproduct3(pd3[1], pd1[1]) + sx = [ 0.5*(lvInnerx[0][c] + rvInnerx[n1][c]) for c in range(3) ] + sd2 = [ 0.5*(lvInnerd2[0][c] + rvInnerd2[n1][c]) for c in range(3) ] + pd2 = smoothCubicHermiteDerivativesLine([ sx, px[1] ], [ sd2, d3 ], fixStartDerivative=True, fixEndDirection=True) + avsx = px[1] + avsd1 = pd1[1] + avsd2 = pd2[1] + avsd3 = pd3[1] + lvOutletOuterd3[4] = pd3[0] + # Left av fibrous ring points aBaseSlopeHeight = aBaseWallThickness*math.sin(aBaseSlopeRadians) aBaseSlopeLength = aBaseWallThickness*math.cos(aBaseSlopeRadians) @@ -463,6 +479,16 @@ def generateBaseMesh(cls, region, options): coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS3, 1, rvOutletd3[n1]) nodeIdentifier += 1 + # Node above above anterior ventricular septum end + avsNodeId = nodeIdentifier + node = nodes.createNode(nodeIdentifier, nodetemplate) + cache.setNode(node) + coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_VALUE, 1, avsx) + coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS1, 1, avsd1) + coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS2, 1, avsd2) + coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS3, 1, avsd3) + nodeIdentifier += 1 + # AV fibrous ring nodes lavInnerNodeId = [ [], [] ] lavOuterNodeId = [ [], [] ] @@ -650,13 +676,12 @@ def generateBaseMesh(cls, region, options): else: scaleEftNodeValueLabels(eft1, [ 5, 6, 7, 8 ], [ Node.VALUE_LABEL_D_DS1, Node.VALUE_LABEL_D_DS3 ], [ 1 ]) elif e == elementsCountAroundAtrialSeptum: - # cfb: 6 node inclined wedge element + # cfb: 5 node inclined pyramid element la1 = (elementsCountAroundAtrialFreeWall + e)%elementsCountAroundAtria ra1 = elementsCountAroundAtrialSeptum - 1 - e - nids = [ lvInnerNodeId[lv1], lvOutletNodeId[0][0], lavInnerNodeId[0][la1], lvOutletNodeId[1][0], - rvInnerNodeId[rv1], ravInnerNodeId[0][ra1] ] + nids = [ lvInnerNodeId[lv1], lvOutletNodeId[1][0], lavInnerNodeId[0][la1], rvInnerNodeId[rv1], ravInnerNodeId[0][ra1] ] scaleEftNodeValueLabels(eft1, [ 5 ], [ Node.VALUE_LABEL_D_DS3 ], [ 1 ]) - remapEftNodeValueLabel(eft1, [ 1 ], Node.VALUE_LABEL_D_DS1, [ ( Node.VALUE_LABEL_D_DS1, [] ), ( Node.VALUE_LABEL_D_DS2, [] ) ]) + remapEftNodeValueLabel(eft1, [ 1 ], Node.VALUE_LABEL_D_DS1, [ ( Node.VALUE_LABEL_D_DS1, [] ), ( Node.VALUE_LABEL_D_DS2, [] ), ( Node.VALUE_LABEL_D_DS3, [] ) ]) tricubichermite.setEftLinearDerivative(eft1, [ 2, 4 ], Node.VALUE_LABEL_D_DS2, 2, 4, 1) remapEftNodeValueLabel(eft1, [ 2 ], Node.VALUE_LABEL_D_DS1, [ ( Node.VALUE_LABEL_D_DS2, []) ]) remapEftNodeValueLabel(eft1, [ 4 ], Node.VALUE_LABEL_D_DS1, [ ( Node.VALUE_LABEL_D_DS1, [] ), ( Node.VALUE_LABEL_D_DS3, []) ]) @@ -670,8 +695,8 @@ def generateBaseMesh(cls, region, options): remapEftNodeValueLabel(eft1, [ 6 ], Node.VALUE_LABEL_D_DS1, [ ( Node.VALUE_LABEL_D_DS1, [1] ), ( Node.VALUE_LABEL_D_DS2, []) ]) remapEftNodeValueLabel(eft1, [ 8 ], Node.VALUE_LABEL_D_DS1, [ ( Node.VALUE_LABEL_D_DS1, [1] ), ( Node.VALUE_LABEL_D_DS3, []) ]) tricubichermite.setEftLinearDerivative(eft1, [ 6, 8 ], Node.VALUE_LABEL_D_DS2, 2, 4, 1) - ln_map = [ 1, 2, 3, 4, 5, 2, 6, 4 ] - remapEftLocalNodes(eft1, 6, ln_map) + ln_map = [ 1, 2, 3, 2, 4, 2, 5, 2 ] + remapEftLocalNodes(eft1, 5, ln_map) else: # wedge elements along remainder of interventricular septum lv1 -= 1 @@ -680,21 +705,27 @@ def generateBaseMesh(cls, region, options): rv2 += 1 lo1 = e - elementsCountAroundAtrialSeptum - 1 lo2 = lo1 + 1 - nids = [ lvInnerNodeId[lv1], lvInnerNodeId[lv2], lvOutletNodeId[0][lo1], lvOutletNodeId[0][lo2], + nids = [ lvInnerNodeId[lv1], lvInnerNodeId[lv2], lvOutletNodeId[1][lo1], lvOutletNodeId[1][lo2], rvInnerNodeId[rv1], rvInnerNodeId[rv2] ] scaleEftNodeValueLabels(eft1, [ 5, 6 ], [ Node.VALUE_LABEL_D_DS1, Node.VALUE_LABEL_D_DS3 ], [ 1 ]) remapEftNodeValueLabel(eft1, [ 3, 4, 7, 8 ], Node.VALUE_LABEL_D_DS3, []) if lo1 == 0: - remapEftNodeValueLabel(eft1, [ 1 ], Node.VALUE_LABEL_D_DS2, [ ( Node.VALUE_LABEL_D_DS1, [] ), ( Node.VALUE_LABEL_D_DS2, [] ) ]) + remapEftNodeValueLabel(eft1, [ 1 ], Node.VALUE_LABEL_D_DS2, [ ( Node.VALUE_LABEL_D_DS1, [] ), ( Node.VALUE_LABEL_D_DS2, [] ), ( Node.VALUE_LABEL_D_DS3, [] ) ]) + remapEftNodeValueLabel(eft1, [ 2, 6 ], Node.VALUE_LABEL_D_DS2, [ ( Node.VALUE_LABEL_D_DS2, [] ), ( Node.VALUE_LABEL_D_DS3, [] ) ]) remapEftNodeValueLabel(eft1, [ 5 ], Node.VALUE_LABEL_D_DS2, [ ( Node.VALUE_LABEL_D_DS2, [] ), ( Node.VALUE_LABEL_D_DS3, [] ) ]) remapEftNodeValueLabel(eft1, [ 7 ], Node.VALUE_LABEL_D_DS2, [ ( Node.VALUE_LABEL_D_DS1, [1] ), ( Node.VALUE_LABEL_D_DS2, []) ]) #remapEftNodeValueLabel(eft1, [ 8 ], Node.VALUE_LABEL_D_DS2, [ ( Node.VALUE_LABEL_D_DS2, [] ), ( Node.VALUE_LABEL_D_DS3, [1]) ]) elif lv2 == 0: + nids[3] = avsNodeId + remapEftNodeValueLabel(eft1, [ 1, 5, 6 ], Node.VALUE_LABEL_D_DS2, [ ( Node.VALUE_LABEL_D_DS2, [] ), ( Node.VALUE_LABEL_D_DS3, [] ) ]) + remapEftNodeValueLabel(eft1, [ 2 ], Node.VALUE_LABEL_D_DS2, [ ( Node.VALUE_LABEL_D_DS1, [1] ), ( Node.VALUE_LABEL_D_DS2, [] ), ( Node.VALUE_LABEL_D_DS3, [] ) ]) + #remapEftNodeValueLabel(eft1, [ 6 ], Node.VALUE_LABEL_D_DS2, [ ( Node.VALUE_LABEL_D_DS1, [] ), ( Node.VALUE_LABEL_D_DS2, [] ), ( Node.VALUE_LABEL_D_DS3, [] ) ]) # general linear map d3 adjacent to collapsed anterior interventricular sulcus remapEftNodeValueLabel(eft1, [ 2 ], Node.VALUE_LABEL_D_DS3, [ ( Node.VALUE_LABEL_D_DS1, [1] ), ( Node.VALUE_LABEL_D_DS3, [] ) ]) + remapEftNodeValueLabel(eft1, [ 4 ], Node.VALUE_LABEL_D_DS2, [ ( Node.VALUE_LABEL_D_DS2, [] ), ( Node.VALUE_LABEL_D_DS3, [] ) ]) else: + remapEftNodeValueLabel(eft1, [ 1, 2, 5, 6 ], Node.VALUE_LABEL_D_DS2, [ ( Node.VALUE_LABEL_D_DS2, [] ), ( Node.VALUE_LABEL_D_DS3, [] ) ]) #remapEftNodeValueLabel(eft1, [ 7, 8 ], Node.VALUE_LABEL_D_DS2, [ ( Node.VALUE_LABEL_D_DS2, [] ), ( Node.VALUE_LABEL_D_DS3, [1]) ]) - pass ln_map = [ 1, 2, 3, 4, 5, 6, 3, 4 ] remapEftLocalNodes(eft1, 6, ln_map) From cb239ec2797667ef7b0f2a3f69374f8bb074844d Mon Sep 17 00:00:00 2001 From: Richard Christie Date: Mon, 5 Nov 2018 13:11:35 +1300 Subject: [PATCH 06/25] Add heart1 scaffold --- scaffoldmaker/meshtypes/meshtype_3d_heart1.py | 116 ++++++++++++++++++ scaffoldmaker/scaffolds.py | 2 + 2 files changed, 118 insertions(+) create mode 100644 scaffoldmaker/meshtypes/meshtype_3d_heart1.py diff --git a/scaffoldmaker/meshtypes/meshtype_3d_heart1.py b/scaffoldmaker/meshtypes/meshtype_3d_heart1.py new file mode 100644 index 00000000..09a0de4e --- /dev/null +++ b/scaffoldmaker/meshtypes/meshtype_3d_heart1.py @@ -0,0 +1,116 @@ +""" +Generates a 3-D heart model including ventricles, base and atria. +""" + +from __future__ import division +import math +from scaffoldmaker.annotation.annotationgroup import AnnotationGroup, findAnnotationGroupByName +from scaffoldmaker.meshtypes.meshtype_3d_heartatria1 import MeshType_3d_heartatria1 +from scaffoldmaker.meshtypes.meshtype_3d_heartventriclesbase1 import MeshType_3d_heartventriclesbase1 +from scaffoldmaker.utils.zinc_utils import * +from scaffoldmaker.utils.meshrefinement import MeshRefinement + +class MeshType_3d_heart1(object): + ''' + Generates a 3-D heart model including ventricles, base and atria. + ''' + + @staticmethod + def getName(): + return '3D Heart 1' + + @staticmethod + def getDefaultOptions(): + options = MeshType_3d_heartatria1.getDefaultOptions() + # ventricles overrides some atria options, so update from it: + optionsVentricles = MeshType_3d_heartventriclesbase1.getDefaultOptions() + options.update(optionsVentricles) + return options + + @staticmethod + def getOrderedOptionNames(): + optionNames = MeshType_3d_heartventriclesbase1.getOrderedOptionNames() + optionNamesAtria = MeshType_3d_heartatria1.getOrderedOptionNames() + # insert numbers of elements in atria in initial group + for optionName in [ + 'Number of elements around atrial free wall', + 'Number of elements around atrial septum', + 'Number of elements up atria', + 'Number of elements inlet']: + optionNames.insert(6, optionName) + optionNamesAtria.remove(optionName) + # remove dependent or repeated options in atria1 + optionNamesAtria.remove('Aorta outer plus diameter') + for optionName in optionNames: + if optionName in optionNamesAtria: + optionNamesAtria.remove(optionName) + # add remaining atria options + optionNames += optionNamesAtria + # want refinement options last + for optionName in [ + 'Refine', + 'Refine number of elements surface', + 'Refine number of elements through LV wall', + 'Refine number of elements through wall']: + optionNames.remove(optionName) + optionNames.append(optionName) + return optionNames + + @staticmethod + def checkOptions(options): + MeshType_3d_heartventriclesbase1.checkOptions(options) + MeshType_3d_heartatria1.checkOptions(options) + # only works with particular numbers of elements around + #options['Number of elements around atrial septum'] = 2 + # set dependent outer diameter used in atria2 + options['Aorta outer plus diameter'] = options['LV outlet inner diameter'] + 2.0*options['LV outlet wall thickness'] + + @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: list of AnnotationGroup + """ + # set dependent outer diameter used in atria2 + options['Aorta outer plus diameter'] = options['LV outlet inner diameter'] + 2.0*options['LV outlet wall thickness'] + + fm = region.getFieldmodule() + fm.beginChange() + coordinates = getOrCreateCoordinateField(fm) + cache = fm.createFieldcache() + + # generate heartventriclesbase2 model and put atria2 on it + annotationGroups = MeshType_3d_heartventriclesbase1.generateBaseMesh(region, options) + annotationGroups += MeshType_3d_heartatria1.generateBaseMesh(region, options) + + fm.endChange() + return annotationGroups + + @classmethod + def refineMesh(cls, meshrefinement, options): + """ + Refine source mesh into separate region, with change of basis. + :param meshrefinement: MeshRefinement, which knows source and target region. + :param options: Dict containing options. See getDefaultOptions(). + """ + assert isinstance(meshrefinement, MeshRefinement) + MeshType_3d_heartventriclesbase1.refineMesh(meshrefinement, options) + MeshType_3d_heartatria1.refineMesh(meshrefinement, options) + + @classmethod + def generateMesh(cls, region, options): + """ + Generate base or refined mesh. + :param region: Zinc region to create mesh in. Must be empty. + :param options: Dict containing options. See getDefaultOptions(). + :return: list of AnnotationGroup for mesh. + """ + if not options['Refine']: + return cls.generateBaseMesh(region, options) + baseRegion = region.createRegion() + baseAnnotationGroups = cls.generateBaseMesh(baseRegion, options) + meshrefinement = MeshRefinement(baseRegion, region, baseAnnotationGroups) + cls.refineMesh(meshrefinement, options) + return meshrefinement.getAnnotationGroups() diff --git a/scaffoldmaker/scaffolds.py b/scaffoldmaker/scaffolds.py index d790cee7..63fe82bc 100644 --- a/scaffoldmaker/scaffolds.py +++ b/scaffoldmaker/scaffolds.py @@ -9,6 +9,7 @@ from scaffoldmaker.meshtypes.meshtype_2d_tube1 import MeshType_2d_tube1 from scaffoldmaker.meshtypes.meshtype_3d_box1 import MeshType_3d_box1 from scaffoldmaker.meshtypes.meshtype_3d_boxhole1 import MeshType_3d_boxhole1 +from scaffoldmaker.meshtypes.meshtype_3d_heart1 import MeshType_3d_heart1 from scaffoldmaker.meshtypes.meshtype_3d_heart2 import MeshType_3d_heart2 from scaffoldmaker.meshtypes.meshtype_3d_heartatria1 import MeshType_3d_heartatria1 from scaffoldmaker.meshtypes.meshtype_3d_heartatria2 import MeshType_3d_heartatria2 @@ -34,6 +35,7 @@ def __init__(self): MeshType_2d_tube1, MeshType_3d_box1, MeshType_3d_boxhole1, + MeshType_3d_heart1, MeshType_3d_heart2, MeshType_3d_heartatria1, MeshType_3d_heartatria2, From 02a9eda0c587d773a5f2d04bb8c4231b102cf732 Mon Sep 17 00:00:00 2001 From: Richard Christie Date: Mon, 5 Nov 2018 14:49:35 +1300 Subject: [PATCH 07/25] Renumber RV inner nodes Now consistent with LV, starting at posterior interventricular sulcus. Simplifies logic. --- .../meshtypes/meshtype_3d_heartventricles1.py | 48 +++++++++---------- .../meshtype_3d_heartventriclesbase1.py | 10 ++-- 2 files changed, 29 insertions(+), 29 deletions(-) diff --git a/scaffoldmaker/meshtypes/meshtype_3d_heartventricles1.py b/scaffoldmaker/meshtypes/meshtype_3d_heartventricles1.py index 07fe9e93..2bef91da 100644 --- a/scaffoldmaker/meshtypes/meshtype_3d_heartventricles1.py +++ b/scaffoldmaker/meshtypes/meshtype_3d_heartventricles1.py @@ -385,7 +385,7 @@ def generateBaseMesh(cls, region, options): lvInnerd3.append(layerInnerd3) vOuterd3.append(layerOuterd3) - # get points on inside of RV, anticlockwise starting on posterior free wall + # get points on inside of RV, anticlockwise starting at node opposite posterior interventricular sulcus septumOuterRadius = lvInnerRadius + vSeptumThickness rvInnerx = [] @@ -456,30 +456,32 @@ def generateBaseMesh(cls, region, options): layerInnerd2.append([ 0.0, 0.0, 0.0 ]) layerInnerd3.append(innerd3) if (n1 > 0) and (n1 < elementsCountAroundVSeptum): - lvInnerd3[n2][-n1] = [- d for d in innerd3 ] + lvInnerd3[n2][-n1] = [ -d for d in innerd3 ] + # swizzle lists to start at node opposite posterior interventricular sulcus + for li in [ layerInnerx, layerInnerd1, layerInnerd2, layerInnerd3 ]: + li.insert(0, li.pop()) rvInnerx.append(layerInnerx) rvInnerd1.append(layerInnerd1) rvInnerd2.append(layerInnerd2) rvInnerd3.append(layerInnerd3) - # calculate derivative 2 on inner RV septum n2Range = range(elementsCountUpLVApex, elementsCountUpLV) - for n1 in range(elementsCountAroundRVFreeWall - 1, elementsCountAroundRVFreeWall + elementsCountAroundVSeptum): + for n1 in range(-elementsCountAroundVSeptum, 1): nx = [ rvInnerx[n2][n1] for n2 in n2Range ] nd2 = [ rvInnerd2[n2][n1] for n2 in n2Range ] nd2 = smoothCubicHermiteDerivativesLine([ rvInnerx[n2][n1] for n2 in n2Range ], [ rvInnerd2[n2][n1] for n2 in n2Range ]) for n2 in n2Range: rvInnerd2[n2][n1] = nd2[n2 - elementsCountUpLVApex] - # get points on RV apex + # get points on RV apex curve, from posterior to anterior n2 = elementsCountUpLVApex dFactor = 2.0 sx = [] sd1 = [] sd2 = [] - for n1 in range(elementsCountAroundRVFreeWall - 1): - r1 = 2*elementsCountAroundRVFreeWall - n1 - 2 + for n1 in range(1, elementsCountAroundRVFreeWall): + r1 = 2*elementsCountAroundRVFreeWall - n1 ax = rvInnerx[n2][r1] ad1 = [ -d for d in rvInnerd1[n2][r1] ] ad2 = [ -dFactor*d for d in rvInnerd2[n2][r1] ] @@ -492,19 +494,18 @@ def generateBaseMesh(cls, region, options): sx .append(px[1]) sd1.append(interpolateSampleLinear([ ad1, bd1 ], pe[1:2], pxi[1:2])[0]) sd2.append(pd2[1]) - n1 = 2*elementsCountAroundRVFreeWall - 1 - ax = rvInnerx [n2][n1] - ad1 = rvInnerd1[n2][n1] - ad2 = [ -d for d in rvInnerd2[n2][n1] ] - n1 = elementsCountAroundRVFreeWall - 1 - bx = rvInnerx [n2][n1] - bd1 = [ -d for d in rvInnerd1[n2][n1] ] - bd2 = rvInnerd2[n2][n1] + ax = rvInnerx [n2][0] + ad1 = rvInnerd1[n2][0] + ad2 = [ -d for d in rvInnerd2[n2][0] ] + bx = rvInnerx [n2][elementsCountAroundRVFreeWall] + bd1 = [ -d for d in rvInnerd1[n2][elementsCountAroundRVFreeWall] ] + bd2 = rvInnerd2[n2][elementsCountAroundRVFreeWall] px, pd1, pe, pxi = sampleCubicHermiteCurves([ ax ] + sx + [ bx ], [ ad2 ] + sd1 + [ bd2 ], elementsCountAroundRVFreeWall + 2, addLengthStart = 0.5*vector.magnitude(ad2)/dFactor, lengthFractionStart = 0.5, addLengthEnd = 0.5*vector.magnitude(bd2)/dFactor, lengthFractionEnd = 0.5, arcLengthDerivatives = False)[0:4] pd2 = interpolateSampleLinear([ ad1 ] + sd2 + [ bd1 ], pe, pxi) n2 = elementsCountUpLVApex - 1 + # loop skips first and last in sample: for n1 in range(1, elementsCountAroundRVFreeWall + 2): rvInnerx [n2].append(px [n1]) rvInnerd1[n2].append(pd1[n1]) @@ -522,8 +523,6 @@ def generateBaseMesh(cls, region, options): od3 = [ (d1Factor*vOuterd1[n2][o1][c] + vOuterd2[n2][o1][c] - vOuterd3[n2][o1][c]) for c in range(3) ] id3 = interpolateHermiteLagrangeDerivative(vOuterx[n2][o1], od3, px[n1], 1.0) rvInnerd3[n2].append([ -d for d in id3 ]) - for li in [ rvInnerx[n2], rvInnerd1[n2], rvInnerd2[n2], rvInnerd3[n2] ]: - li.append(li.pop(0)) # create nodes on inner left ventricle @@ -575,9 +574,10 @@ def generateBaseMesh(cls, region, options): layerNodeId.append(nodeIdentifier) nodeIdentifier = nodeIdentifier + 1 rvInnerNodeId.append(layerNodeId) + # mirror RV apex so can index as for other rows n2 = elementsCountUpLVApex - 1 - for n1 in range(elementsCountAroundRVFreeWall - 1): - rvInnerNodeId[n2].insert(-1, rvInnerNodeId[n2][elementsCountAroundRVFreeWall - n1 - 2]) + for n1 in range(elementsCountAroundRVFreeWall - 1, 0, -1): + rvInnerNodeId[n2].append(rvInnerNodeId[n2][n1]) # create nodes on outer ventricles @@ -702,8 +702,8 @@ def generateBaseMesh(cls, region, options): vOuterNodeId [e2 - 1][va], vOuterNodeId [e2 - 1][vb], vOuterNodeId [e2][va], vOuterNodeId [e2][vb] ] if e1 == -1: # anterior interventricular sulcus: collapsed to 6 element wedge - nids[0] = rvInnerNodeId[e2 - 1][elementsCountAroundRVFreeWall - 1] - nids[2] = rvInnerNodeId[e2 ][elementsCountAroundRVFreeWall - 1] + nids[0] = rvInnerNodeId[e2 - 1][elementsCountAroundRVFreeWall] + nids[2] = rvInnerNodeId[e2 ][elementsCountAroundRVFreeWall] nids.pop(6) nids.pop(4) meshGroups += [ rvMeshGroup ] @@ -752,8 +752,8 @@ def generateBaseMesh(cls, region, options): eft1 = eft scalefactors = None meshGroups = [ rvMeshGroup ] - ua = (e1 - 1) % (2*elementsCountAroundRVFreeWall) - ub = e1 % (2*elementsCountAroundRVFreeWall) + ua = e1 + ub = e1 + 1 va = elementsCountAroundLVFreeWall + e1 vb = (va + 1)%elementsCountAroundLV e2m = max(e2 - 1, elementsCountUpLVApex - 1) @@ -878,7 +878,7 @@ def generateBaseMesh(cls, region, options): scalefactors = None meshGroups = [ lvMeshGroup, rvMeshGroup, vSeptumMeshGroup ] - ua = 2*elementsCountAroundRVFreeWall - 1 - e1 + ua = -e1 ub = ua - 1 va = elementsCountAroundLVFreeWall + e1 vb = (va + 1)%elementsCountAroundLV diff --git a/scaffoldmaker/meshtypes/meshtype_3d_heartventriclesbase1.py b/scaffoldmaker/meshtypes/meshtype_3d_heartventriclesbase1.py index ee1da6af..bd7b7040 100644 --- a/scaffoldmaker/meshtypes/meshtype_3d_heartventriclesbase1.py +++ b/scaffoldmaker/meshtypes/meshtype_3d_heartventriclesbase1.py @@ -324,7 +324,7 @@ def generateBaseMesh(cls, region, options): px, pd1 = sampleCubicHermiteCurves([ lvOutletOuterx[3], vOuterx[0] ], [ [ 2.0*d for d in lvOutletOuterd1[3] ], fd2 ], 2, arcLengthDerivatives=True)[0:2] pd1 = smoothCubicHermiteDerivativesLine(px, [ lvOutletOuterd1[3], pd1[1], fd2 ], fixStartDerivative=True, fixEndDerivative=True) pd3 = smoothCubicHermiteDerivativesLine([ lvOutletOuterx[4], px[1], rvOutletOuterx[-1] ], [ lvOutletOuterd3[4], zero, rvOutletd2 ], fixEndDerivative=True) - n1 = elementsCountAroundRVFreeWall - 1 + n1 = elementsCountAroundRVFreeWall d3 = vector.crossproduct3(pd3[1], pd1[1]) sx = [ 0.5*(lvInnerx[0][c] + rvInnerx[n1][c]) for c in range(3) ] sd2 = [ 0.5*(lvInnerd2[0][c] + rvInnerd2[n1][c]) for c in range(3) ] @@ -418,14 +418,14 @@ def generateBaseMesh(cls, region, options): for n1 in range(1, elementsCountRVFreeWallRegular + 1): noa = elementsCountAroundAtrialSeptum + n1 - 1 nov = elementsCountAroundLVFreeWall + n1 - ravInnerd2[0][noa] = interpolateHermiteLagrangeDerivative(rvInnerx[n1 - 1], rvInnerd2[n1 - 1], ravInnerx[0][noa], 1.0) + ravInnerd2[0][noa] = interpolateHermiteLagrangeDerivative(rvInnerx[n1], rvInnerd2[n1], ravInnerx[0][noa], 1.0) ravOuterd2[0][noa] = interpolateHermiteLagrangeDerivative(vOuterx[nov], vOuterd2[nov], ravOuterx[0][noa], 1.0) for n1 in range(elementsCountAroundAtrialSeptum + 1): noa = (elementsCountAroundAtrialFreeWall + n1)%elementsCountAroundAtria nov = elementsCountAroundLVFreeWall + n1 lavInnerd2[0][noa] = interpolateHermiteLagrangeDerivative(lvInnerx[nov], lvInnerd2[nov], lavInnerx[0][noa], 1.0) noa = (elementsCountAroundAtrialSeptum - 1 + elementsCountAroundAtria - n1)%elementsCountAroundAtria - nov = elementsCountAroundRV - n1 - 1 + nov = -n1 ravInnerd2[0][noa] = interpolateHermiteLagrangeDerivative(rvInnerx[nov], rvInnerd2[nov], ravInnerx[0][noa], 1.0) # copy derivative 3 from av points to LV outlet at centre, left and right cfb: @@ -594,7 +594,7 @@ def generateBaseMesh(cls, region, options): meshGroups = [ rvMeshGroup ] noa = elementsCountAroundAtrialSeptum - 1 + e - niv = e - 1 + niv = e nov = elementsCountAroundLVFreeWall + e if e == -1: # crux / posterior interventricular sulcus: collapsed to 6 element wedge @@ -648,7 +648,7 @@ def generateBaseMesh(cls, region, options): lv1 = elementsCountAroundLVFreeWall + e lv2 = (lv1 + 1)%elementsCountAroundLV - rv1 = elementsCountAroundRV - 1 - e + rv1 = -e rv2 = rv1 - 1 eft1 = tricubichermite.createEftNoCrossDerivatives() From 7450ea2a67f15b43a7911315140d1ad7db606d80 Mon Sep 17 00:00:00 2001 From: Richard Christie Date: Tue, 6 Nov 2018 14:14:27 +1300 Subject: [PATCH 08/25] Point RV inner septum end d3 toward sulci Make consistent with RV apex and atria. Improve refinement of ventricles base 1. --- .../meshtypes/meshtype_3d_heartventricles1.py | 39 +++++++----- .../meshtype_3d_heartventriclesbase1.py | 60 ++++++++++++++----- 2 files changed, 69 insertions(+), 30 deletions(-) diff --git a/scaffoldmaker/meshtypes/meshtype_3d_heartventricles1.py b/scaffoldmaker/meshtypes/meshtype_3d_heartventricles1.py index 2bef91da..83f6daf2 100644 --- a/scaffoldmaker/meshtypes/meshtype_3d_heartventricles1.py +++ b/scaffoldmaker/meshtypes/meshtype_3d_heartventricles1.py @@ -448,14 +448,16 @@ def generateBaseMesh(cls, region, options): addLengthEnd = 0.5*vector.magnitude(nd1[-1]), lengthFractionEnd = 0.5, arcLengthDerivatives = False)[0:2] for n1 in range(elementsCountAroundVSeptum + 1): - lvx = lvInnerx[n2][-n1] + # d3 at ends of septum is toward adjacent interventricular sulcus, inside septum is across septum to lvInner + insideSeptum = (n1 > 0) and (n1 < elementsCountAroundVSeptum) + lvx = lvInnerx[n2][-n1] if insideSeptum else vOuterx[n2][-n1] rvx = px[n1 + 1] innerd3 = [ (lvx[c] - rvx[c]) for c in range(3) ] layerInnerx .append(rvx) layerInnerd1.append(pd1[n1 + 1]) layerInnerd2.append([ 0.0, 0.0, 0.0 ]) layerInnerd3.append(innerd3) - if (n1 > 0) and (n1 < elementsCountAroundVSeptum): + if insideSeptum: lvInnerd3[n2][-n1] = [ -d for d in innerd3 ] # swizzle lists to start at node opposite posterior interventricular sulcus for li in [ layerInnerx, layerInnerd1, layerInnerd2, layerInnerd3 ]: @@ -716,15 +718,13 @@ def generateBaseMesh(cls, region, options): remapEftNodeValueLabel(eft1, [ 1 ], Node.VALUE_LABEL_D_DS1, [ ( Node.VALUE_LABEL_D_DS2, [1] ) ]) remapEftNodeValueLabel(eft1, [ 1 ], Node.VALUE_LABEL_D_DS2, [ ( Node.VALUE_LABEL_D_DS1, [] ) ]) remapEftNodeValueLabel(eft1, [ 2 ], Node.VALUE_LABEL_D_DS1, [ ( Node.VALUE_LABEL_D_DS1, [] ), ( Node.VALUE_LABEL_D_DS2, [1] ), ( Node.VALUE_LABEL_D_DS3, [1] ) ]) - remapEftNodeValueLabel(eft1, [ 3 ], Node.VALUE_LABEL_D_DS3, [ ( Node.VALUE_LABEL_D_DS1, [1] ), ( Node.VALUE_LABEL_D_DS3, [] ) ]) - remapEftNodeValueLabel(eft1, [ 3 ], Node.VALUE_LABEL_D_DS1, [ ( Node.VALUE_LABEL_D_DS3, [] ) ]) + remapEftNodeValueLabel(eft1, [ 3 ], Node.VALUE_LABEL_D_DS1, [ ( Node.VALUE_LABEL_D_DS1, [] ), ( Node.VALUE_LABEL_D_DS3, [] ) ]) remapEftNodeValueLabel(eft1, [ 4 ], Node.VALUE_LABEL_D_DS1, [ ( Node.VALUE_LABEL_D_DS1, [] ), ( Node.VALUE_LABEL_D_DS3, [1] ) ]) remapEftNodeValueLabel(eft1, [ 5 ], Node.VALUE_LABEL_D_DS3, [ ( Node.VALUE_LABEL_D_DS1, [] ), ( Node.VALUE_LABEL_D_DS2, [1] ), ( Node.VALUE_LABEL_D_DS3, []) ]) remapEftNodeValueLabel(eft1, [ 7 ], Node.VALUE_LABEL_D_DS3, [ ( Node.VALUE_LABEL_D_DS1, [] ), ( Node.VALUE_LABEL_D_DS3, []) ]) else: + remapEftNodeValueLabel(eft1, [ 1, 3 ], Node.VALUE_LABEL_D_DS1, [ ( Node.VALUE_LABEL_D_DS1, [] ), ( Node.VALUE_LABEL_D_DS3, [] ) ]) remapEftNodeValueLabel(eft1, [ 2, 4 ], Node.VALUE_LABEL_D_DS1, [ ( Node.VALUE_LABEL_D_DS1, [] ), ( Node.VALUE_LABEL_D_DS3, [1] ) ]) - remapEftNodeValueLabel(eft1, [ 1, 3 ], Node.VALUE_LABEL_D_DS3, [ ( Node.VALUE_LABEL_D_DS1, [1] ), ( Node.VALUE_LABEL_D_DS3, [] ) ]) - remapEftNodeValueLabel(eft1, [ 1, 3 ], Node.VALUE_LABEL_D_DS1, [ ( Node.VALUE_LABEL_D_DS3, [] ) ]) remapEftNodeValueLabel(eft1, [ 5, 7 ], Node.VALUE_LABEL_D_DS3, [ ( Node.VALUE_LABEL_D_DS1, [] ), ( Node.VALUE_LABEL_D_DS3, []) ]) ln_map = [ 1, 2, 3, 4, 5, 5, 6, 6 ] remapEftLocalNodes(eft1, 6, ln_map) @@ -807,14 +807,12 @@ def generateBaseMesh(cls, region, options): remapEftNodeValueLabel(eft1, [ 2 ], Node.VALUE_LABEL_D_DS2, [ ( Node.VALUE_LABEL_D_DS1, [1] ) ]) remapEftNodeValueLabel(eft1, [ 2 ], Node.VALUE_LABEL_D_DS1, [ ( Node.VALUE_LABEL_D_DS2, [] ) ]) remapEftNodeValueLabel(eft1, [ 3 ], Node.VALUE_LABEL_D_DS1, [ ( Node.VALUE_LABEL_D_DS1, [] ), ( Node.VALUE_LABEL_D_DS3, [] ) ]) - remapEftNodeValueLabel(eft1, [ 4 ], Node.VALUE_LABEL_D_DS3, [ ( Node.VALUE_LABEL_D_DS1, [] ), ( Node.VALUE_LABEL_D_DS3, [] ) ]) - remapEftNodeValueLabel(eft1, [ 4 ], Node.VALUE_LABEL_D_DS1, [ ( Node.VALUE_LABEL_D_DS3, [1] ) ]) + remapEftNodeValueLabel(eft1, [ 4 ], Node.VALUE_LABEL_D_DS1, [ ( Node.VALUE_LABEL_D_DS1, [] ), ( Node.VALUE_LABEL_D_DS3, [1] ) ]) remapEftNodeValueLabel(eft1, [ 6 ], Node.VALUE_LABEL_D_DS3, [ ( Node.VALUE_LABEL_D_DS1, [1] ), ( Node.VALUE_LABEL_D_DS2, [1] ), ( Node.VALUE_LABEL_D_DS3, []) ]) remapEftNodeValueLabel(eft1, [ 8 ], Node.VALUE_LABEL_D_DS3, [ ( Node.VALUE_LABEL_D_DS1, [1] ), ( Node.VALUE_LABEL_D_DS3, []) ]) else: remapEftNodeValueLabel(eft1, [ 1, 3 ], Node.VALUE_LABEL_D_DS1, [ ( Node.VALUE_LABEL_D_DS1, [] ), ( Node.VALUE_LABEL_D_DS3, [] ) ]) - remapEftNodeValueLabel(eft1, [ 2, 4 ], Node.VALUE_LABEL_D_DS3, [ ( Node.VALUE_LABEL_D_DS1, [] ), ( Node.VALUE_LABEL_D_DS3, [] ) ]) - remapEftNodeValueLabel(eft1, [ 2, 4 ], Node.VALUE_LABEL_D_DS1, [ ( Node.VALUE_LABEL_D_DS3, [1] ) ]) + remapEftNodeValueLabel(eft1, [ 2, 4 ], Node.VALUE_LABEL_D_DS1, [ ( Node.VALUE_LABEL_D_DS1, [] ), ( Node.VALUE_LABEL_D_DS3, [1] ) ]) remapEftNodeValueLabel(eft1, [ 6, 8 ], Node.VALUE_LABEL_D_DS3, [ ( Node.VALUE_LABEL_D_DS1, [1] ), ( Node.VALUE_LABEL_D_DS3, []) ]) ln_map = [ 1, 2, 3, 4, 5, 5, 6, 6 ] remapEftLocalNodes(eft1, 6, ln_map) @@ -826,12 +824,10 @@ def generateBaseMesh(cls, region, options): if e2 == elementsCountUpLVApex: # collapsed RV corner uses outside d/dxi2 = -d1 remapEftNodeValueLabel(eft1, [ 1 ], Node.VALUE_LABEL_D_DS2, [ ( Node.VALUE_LABEL_D_DS1, [1] ) ]) - remapEftNodeValueLabel(eft1, [ 3 ], Node.VALUE_LABEL_D_DS3, [ ( Node.VALUE_LABEL_D_DS1, [] ), ( Node.VALUE_LABEL_D_DS3, [] ) ]) remapEftNodeValueLabel(eft1, [ 5 ], Node.VALUE_LABEL_D_DS3, [ ( Node.VALUE_LABEL_D_DS1, [1] ), ( Node.VALUE_LABEL_D_DS2, [1] ), ( Node.VALUE_LABEL_D_DS3, []) ]) remapEftNodeValueLabel(eft1, [ 6 ], Node.VALUE_LABEL_D_DS3, [ ( Node.VALUE_LABEL_D_DS2, [1] ), ( Node.VALUE_LABEL_D_DS3, []) ]) remapEftNodeValueLabel(eft1, [ 7 ], Node.VALUE_LABEL_D_DS3, [ ( Node.VALUE_LABEL_D_DS1, [1] ), ( Node.VALUE_LABEL_D_DS3, []) ]) else: - remapEftNodeValueLabel(eft1, [ 1, 3 ], Node.VALUE_LABEL_D_DS3, [ ( Node.VALUE_LABEL_D_DS1, [] ), ( Node.VALUE_LABEL_D_DS3, [] ) ]) remapEftNodeValueLabel(eft1, [ 5, 7 ], Node.VALUE_LABEL_D_DS3, [ ( Node.VALUE_LABEL_D_DS1, [1] ), ( Node.VALUE_LABEL_D_DS3, []) ]) elif e1 == (elementsCountAroundRVFreeWall - 1): # general linear map d3 adjacent to collapsed anterior interventricular sulcus @@ -841,12 +837,10 @@ def generateBaseMesh(cls, region, options): if e2 == elementsCountUpLVApex: # collapsed RV corner uses outside d/dxi2 = d1 remapEftNodeValueLabel(eft1, [ 2 ], Node.VALUE_LABEL_D_DS2, [ ( Node.VALUE_LABEL_D_DS1, [] ) ]) - remapEftNodeValueLabel(eft1, [ 4 ], Node.VALUE_LABEL_D_DS3, [ ( Node.VALUE_LABEL_D_DS1, [1] ), ( Node.VALUE_LABEL_D_DS3, [] ) ]) remapEftNodeValueLabel(eft1, [ 5 ], Node.VALUE_LABEL_D_DS3, [ ( Node.VALUE_LABEL_D_DS2, [1] ), ( Node.VALUE_LABEL_D_DS3, []) ]) remapEftNodeValueLabel(eft1, [ 6 ], Node.VALUE_LABEL_D_DS3, [ ( Node.VALUE_LABEL_D_DS1, [] ), ( Node.VALUE_LABEL_D_DS2, [1] ), ( Node.VALUE_LABEL_D_DS3, []) ]) remapEftNodeValueLabel(eft1, [ 8 ], Node.VALUE_LABEL_D_DS3, [ ( Node.VALUE_LABEL_D_DS1, [] ), ( Node.VALUE_LABEL_D_DS3, []) ]) else: - remapEftNodeValueLabel(eft1, [ 2, 4 ], Node.VALUE_LABEL_D_DS3, [ ( Node.VALUE_LABEL_D_DS1, [1] ), ( Node.VALUE_LABEL_D_DS3, [] ) ]) remapEftNodeValueLabel(eft1, [ 6, 8 ], Node.VALUE_LABEL_D_DS3, [ ( Node.VALUE_LABEL_D_DS1, [] ), ( Node.VALUE_LABEL_D_DS3, []) ]) elif e2 == elementsCountUpLVApex: eft1 = tricubichermite.createEftNoCrossDerivatives() @@ -898,6 +892,9 @@ def generateBaseMesh(cls, region, options): remapEftNodeValueLabel(eft1, [ 5 ], Node.VALUE_LABEL_D_DS3, [ ( Node.VALUE_LABEL_D_DS2, [] ) ]) remapEftNodeValueLabel(eft1, [ 6 ], Node.VALUE_LABEL_D_DS2, [ ( Node.VALUE_LABEL_D_DS2, [1] ) ]) remapEftNodeValueLabel(eft1, [ 6 ], Node.VALUE_LABEL_D_DS3, [ ( Node.VALUE_LABEL_D_DS2, [] ), ( Node.VALUE_LABEL_D_DS3, [1] ) ]) + scaleEftNodeValueLabels(eft1, [ 7 ], [ Node.VALUE_LABEL_D_DS1 ], [ 1 ]) + remapEftNodeValueLabel(eft1, [ 7 ], Node.VALUE_LABEL_D_DS3, [ ( Node.VALUE_LABEL_D_DS1, [] ), ( Node.VALUE_LABEL_D_DS3, [1] ) ]) + scaleEftNodeValueLabels(eft1, [ 8 ], [ Node.VALUE_LABEL_D_DS1, Node.VALUE_LABEL_D_DS3 ], [ 1 ]) elif e1 == (elementsCountAroundVSeptum - 1): remapEftNodeValueLabel(eft1, [ 1 ], Node.VALUE_LABEL_D_DS3, [ ( Node.VALUE_LABEL_D_DS2, [] ), ( Node.VALUE_LABEL_D_DS3, [] ) ]) remapEftNodeValueLabel(eft1, [ 2 ], Node.VALUE_LABEL_D_DS3, [ ( Node.VALUE_LABEL_D_DS1, [1] ), ( Node.VALUE_LABEL_D_DS2, [] ), ( Node.VALUE_LABEL_D_DS3, [] ) ]) @@ -908,11 +905,14 @@ def generateBaseMesh(cls, region, options): remapEftNodeValueLabel(eft1, [ 6 ], Node.VALUE_LABEL_D_DS3, [ ( Node.VALUE_LABEL_D_DS2, [] ) ]) # general linear map d3 adjacent to collapsed anterior interventricular sulcus remapEftNodeValueLabel(eft1, [ 4 ], Node.VALUE_LABEL_D_DS3, [ ( Node.VALUE_LABEL_D_DS1, [1] ), ( Node.VALUE_LABEL_D_DS3, [] ) ]) + scaleEftNodeValueLabels(eft1, [ 7 ], [ Node.VALUE_LABEL_D_DS1, Node.VALUE_LABEL_D_DS3 ], [ 1 ]) + scaleEftNodeValueLabels(eft1, [ 8 ], [ Node.VALUE_LABEL_D_DS1 ], [ 1 ]) + remapEftNodeValueLabel(eft1, [ 8 ], Node.VALUE_LABEL_D_DS3, [ ( Node.VALUE_LABEL_D_DS1, [1] ), ( Node.VALUE_LABEL_D_DS3, [1] ) ]) else: remapEftNodeValueLabel(eft1, [ 1, 2 ], Node.VALUE_LABEL_D_DS3, [ ( Node.VALUE_LABEL_D_DS2, [] ), ( Node.VALUE_LABEL_D_DS3, [] ) ]) remapEftNodeValueLabel(eft1, [ 5, 6 ], Node.VALUE_LABEL_D_DS2, [ ( Node.VALUE_LABEL_D_DS2, [1] ) ]) remapEftNodeValueLabel(eft1, [ 5, 6 ], Node.VALUE_LABEL_D_DS3, [ ( Node.VALUE_LABEL_D_DS2, [] ), ( Node.VALUE_LABEL_D_DS3, [1] ) ]) - scaleEftNodeValueLabels(eft1, [ 7, 8 ], [ Node.VALUE_LABEL_D_DS1, Node.VALUE_LABEL_D_DS3 ], [ 1 ]) + scaleEftNodeValueLabels(eft1, [ 7, 8 ], [ Node.VALUE_LABEL_D_DS1, Node.VALUE_LABEL_D_DS3 ], [ 1 ]) else: eft1 = tricubichermite.createEftNoCrossDerivatives() setEftScaleFactorIds(eft1, [1], []) @@ -920,10 +920,17 @@ def generateBaseMesh(cls, region, options): if e1 == 0: # general linear map d3 adjacent to collapsed posterior interventricular sulcus remapEftNodeValueLabel(eft1, [ 1, 3 ], Node.VALUE_LABEL_D_DS3, [ ( Node.VALUE_LABEL_D_DS1, [] ), ( Node.VALUE_LABEL_D_DS3, [] ) ]) + scaleEftNodeValueLabels(eft1, [ 5, 6, 7, 8 ], [ Node.VALUE_LABEL_D_DS1 ], [ 1 ]) + remapEftNodeValueLabel(eft1, [ 5, 7 ], Node.VALUE_LABEL_D_DS3, [ ( Node.VALUE_LABEL_D_DS1, [] ), ( Node.VALUE_LABEL_D_DS3, [1] ) ]) + scaleEftNodeValueLabels(eft1, [ 6, 8 ], [ Node.VALUE_LABEL_D_DS3 ], [ 1 ]) elif e1 == (elementsCountAroundRVFreeWall - 1): # general linear map d3 adjacent to collapsed anterior interventricular sulcus remapEftNodeValueLabel(eft1, [ 2, 4 ], Node.VALUE_LABEL_D_DS3, [ ( Node.VALUE_LABEL_D_DS1, [1] ), ( Node.VALUE_LABEL_D_DS3, [] ) ]) - scaleEftNodeValueLabels(eft1, [ 5, 6, 7, 8 ], [ Node.VALUE_LABEL_D_DS1, Node.VALUE_LABEL_D_DS3 ], [ 1 ]) + scaleEftNodeValueLabels(eft1, [ 5, 6, 7, 8 ], [ Node.VALUE_LABEL_D_DS1 ], [ 1 ]) + scaleEftNodeValueLabels(eft1, [ 5, 7 ], [ Node.VALUE_LABEL_D_DS3 ], [ 1 ]) + remapEftNodeValueLabel(eft1, [ 6, 8 ], Node.VALUE_LABEL_D_DS3, [ ( Node.VALUE_LABEL_D_DS1, [1] ), ( Node.VALUE_LABEL_D_DS3, [1] ) ]) + else: + scaleEftNodeValueLabels(eft1, [ 5, 6, 7, 8 ], [ Node.VALUE_LABEL_D_DS1, Node.VALUE_LABEL_D_DS3 ], [ 1 ]) result1 = elementtemplate1.defineField(coordinates, -1, eft1) diff --git a/scaffoldmaker/meshtypes/meshtype_3d_heartventriclesbase1.py b/scaffoldmaker/meshtypes/meshtype_3d_heartventriclesbase1.py index bd7b7040..ba9f244a 100644 --- a/scaffoldmaker/meshtypes/meshtype_3d_heartventriclesbase1.py +++ b/scaffoldmaker/meshtypes/meshtype_3d_heartventriclesbase1.py @@ -605,8 +605,7 @@ def generateBaseMesh(cls, region, options): setEftScaleFactorIds(eft1, [1], []) scalefactors = [ -1.0 ] remapEftNodeValueLabel(eft1, [ 1, 3 ], Node.VALUE_LABEL_D_DS1, [ ( Node.VALUE_LABEL_D_DS1, [] ), ( Node.VALUE_LABEL_D_DS3, [] ) ]) - remapEftNodeValueLabel(eft1, [ 2 ], Node.VALUE_LABEL_D_DS3, [ ( Node.VALUE_LABEL_D_DS1, [] ), ( Node.VALUE_LABEL_D_DS3, [] ) ]) - remapEftNodeValueLabel(eft1, [ 2 ], Node.VALUE_LABEL_D_DS1, [ ( Node.VALUE_LABEL_D_DS3, [1] ) ]) # from ventricles + remapEftNodeValueLabel(eft1, [ 2 ], Node.VALUE_LABEL_D_DS1, [ ( Node.VALUE_LABEL_D_DS1, [] ), ( Node.VALUE_LABEL_D_DS3, [1] ) ]) remapEftNodeValueLabel(eft1, [ 4 ], Node.VALUE_LABEL_D_DS1, [ ( Node.VALUE_LABEL_D_DS1, [] ), ( Node.VALUE_LABEL_D_DS3, [1] ) ]) remapEftNodeValueLabel(eft1, [ 5, 6, 7, 8 ], Node.VALUE_LABEL_D_DS1, []) remapEftNodeValueLabel(eft1, [ 6, 8 ], Node.VALUE_LABEL_D_DS3, [ ( Node.VALUE_LABEL_D_DS1, [1] ), ( Node.VALUE_LABEL_D_DS3, []) ]) @@ -621,7 +620,6 @@ def generateBaseMesh(cls, region, options): eft1 = tricubichermite.createEftNoCrossDerivatives() setEftScaleFactorIds(eft1, [1], []) scalefactors = [ -1.0 ] - remapEftNodeValueLabel(eft1, [ 1 ], Node.VALUE_LABEL_D_DS3, [ ( Node.VALUE_LABEL_D_DS1, [] ), ( Node.VALUE_LABEL_D_DS3, [] ) ]) remapEftNodeValueLabel(eft1, [ 5, 7 ], Node.VALUE_LABEL_D_DS3, [ ( Node.VALUE_LABEL_D_DS1, [1] ), ( Node.VALUE_LABEL_D_DS3, []) ]) else: continue @@ -664,9 +662,9 @@ def generateBaseMesh(cls, region, options): if e == 0: # general linear map d3 adjacent to collapsed posterior interventricular sulcus scaleEftNodeValueLabels(eft1, [ 5, 6, 7, 8 ], [ Node.VALUE_LABEL_D_DS1 ], [ 1 ]) - scaleEftNodeValueLabels(eft1, [ 5, 6, 8 ], [ Node.VALUE_LABEL_D_DS3 ], [ 1 ]) + scaleEftNodeValueLabels(eft1, [ 6, 8 ], [ Node.VALUE_LABEL_D_DS3 ], [ 1 ]) remapEftNodeValueLabel(eft1, [ 1, 3 ], Node.VALUE_LABEL_D_DS3, [ ( Node.VALUE_LABEL_D_DS1, [] ), ( Node.VALUE_LABEL_D_DS3, [] ) ]) - remapEftNodeValueLabel(eft1, [ 7 ], Node.VALUE_LABEL_D_DS3, [ ( Node.VALUE_LABEL_D_DS1, [] ), ( Node.VALUE_LABEL_D_DS3, [1] ) ]) + remapEftNodeValueLabel(eft1, [ 5, 7 ], Node.VALUE_LABEL_D_DS3, [ ( Node.VALUE_LABEL_D_DS1, [] ), ( Node.VALUE_LABEL_D_DS3, [1] ) ]) elif e == (elementsCountAroundAtrialSeptum - 1): # general linear map d3 adjacent to cfb scaleEftNodeValueLabels(eft1, [ 5, 6, 7, 8 ], [ Node.VALUE_LABEL_D_DS1 ], [ 1 ]) @@ -686,7 +684,6 @@ def generateBaseMesh(cls, region, options): remapEftNodeValueLabel(eft1, [ 2 ], Node.VALUE_LABEL_D_DS1, [ ( Node.VALUE_LABEL_D_DS2, []) ]) remapEftNodeValueLabel(eft1, [ 4 ], Node.VALUE_LABEL_D_DS1, [ ( Node.VALUE_LABEL_D_DS1, [] ), ( Node.VALUE_LABEL_D_DS3, []) ]) remapEftNodeValueLabel(eft1, [ 2, 4, 6, 8 ], Node.VALUE_LABEL_D_DS3, []) - #remapEftNodeValueLabel(eft1, [ 5 ], Node.VALUE_LABEL_D_DS1, [ ( Node.VALUE_LABEL_D_DS1, [1] ), ( Node.VALUE_LABEL_D_DS2, [] ) ]) remapEftNodeValueLabel(eft1, [ 5 ], Node.VALUE_LABEL_D_DS1, [ ( Node.VALUE_LABEL_D_DS2, [] ), ( Node.VALUE_LABEL_D_DS3, [] ) ]) remapEftNodeValueLabel(eft1, [ 3 ], Node.VALUE_LABEL_D_DS3, [ ( Node.VALUE_LABEL_D_DS1, [1] ), ( Node.VALUE_LABEL_D_DS3, [] ) ]) remapEftNodeValueLabel(eft1, [ 3 ], Node.VALUE_LABEL_D_DS1, [ ( Node.VALUE_LABEL_D_DS3, [] ) ]) @@ -707,25 +704,27 @@ def generateBaseMesh(cls, region, options): lo2 = lo1 + 1 nids = [ lvInnerNodeId[lv1], lvInnerNodeId[lv2], lvOutletNodeId[1][lo1], lvOutletNodeId[1][lo2], rvInnerNodeId[rv1], rvInnerNodeId[rv2] ] - scaleEftNodeValueLabels(eft1, [ 5, 6 ], [ Node.VALUE_LABEL_D_DS1, Node.VALUE_LABEL_D_DS3 ], [ 1 ]) + scaleEftNodeValueLabels(eft1, [ 5, 6 ], [ Node.VALUE_LABEL_D_DS1 ], [ 1 ]) remapEftNodeValueLabel(eft1, [ 3, 4, 7, 8 ], Node.VALUE_LABEL_D_DS3, []) if lo1 == 0: + scaleEftNodeValueLabels(eft1, [ 5, 6 ], [ Node.VALUE_LABEL_D_DS3 ], [ 1 ]) remapEftNodeValueLabel(eft1, [ 1 ], Node.VALUE_LABEL_D_DS2, [ ( Node.VALUE_LABEL_D_DS1, [] ), ( Node.VALUE_LABEL_D_DS2, [] ), ( Node.VALUE_LABEL_D_DS3, [] ) ]) remapEftNodeValueLabel(eft1, [ 2, 6 ], Node.VALUE_LABEL_D_DS2, [ ( Node.VALUE_LABEL_D_DS2, [] ), ( Node.VALUE_LABEL_D_DS3, [] ) ]) remapEftNodeValueLabel(eft1, [ 5 ], Node.VALUE_LABEL_D_DS2, [ ( Node.VALUE_LABEL_D_DS2, [] ), ( Node.VALUE_LABEL_D_DS3, [] ) ]) remapEftNodeValueLabel(eft1, [ 7 ], Node.VALUE_LABEL_D_DS2, [ ( Node.VALUE_LABEL_D_DS1, [1] ), ( Node.VALUE_LABEL_D_DS2, []) ]) - #remapEftNodeValueLabel(eft1, [ 8 ], Node.VALUE_LABEL_D_DS2, [ ( Node.VALUE_LABEL_D_DS2, [] ), ( Node.VALUE_LABEL_D_DS3, [1]) ]) - elif lv2 == 0: + elif lv2 == 0: # final element on v septum nids[3] = avsNodeId - remapEftNodeValueLabel(eft1, [ 1, 5, 6 ], Node.VALUE_LABEL_D_DS2, [ ( Node.VALUE_LABEL_D_DS2, [] ), ( Node.VALUE_LABEL_D_DS3, [] ) ]) + scaleEftNodeValueLabels(eft1, [ 5 ], [ Node.VALUE_LABEL_D_DS3 ], [ 1 ]) + remapEftNodeValueLabel(eft1, [ 1, 5 ], Node.VALUE_LABEL_D_DS2, [ ( Node.VALUE_LABEL_D_DS2, [] ), ( Node.VALUE_LABEL_D_DS3, [] ) ]) remapEftNodeValueLabel(eft1, [ 2 ], Node.VALUE_LABEL_D_DS2, [ ( Node.VALUE_LABEL_D_DS1, [1] ), ( Node.VALUE_LABEL_D_DS2, [] ), ( Node.VALUE_LABEL_D_DS3, [] ) ]) - #remapEftNodeValueLabel(eft1, [ 6 ], Node.VALUE_LABEL_D_DS2, [ ( Node.VALUE_LABEL_D_DS1, [] ), ( Node.VALUE_LABEL_D_DS2, [] ), ( Node.VALUE_LABEL_D_DS3, [] ) ]) - # general linear map d3 adjacent to collapsed anterior interventricular sulcus remapEftNodeValueLabel(eft1, [ 2 ], Node.VALUE_LABEL_D_DS3, [ ( Node.VALUE_LABEL_D_DS1, [1] ), ( Node.VALUE_LABEL_D_DS3, [] ) ]) + # general linear map d3 adjacent to collapsed anterior interventricular sulcus remapEftNodeValueLabel(eft1, [ 4 ], Node.VALUE_LABEL_D_DS2, [ ( Node.VALUE_LABEL_D_DS2, [] ), ( Node.VALUE_LABEL_D_DS3, [] ) ]) + remapEftNodeValueLabel(eft1, [ 6 ], Node.VALUE_LABEL_D_DS2, [ ( Node.VALUE_LABEL_D_DS1, [] ), ( Node.VALUE_LABEL_D_DS2, [] ), ( Node.VALUE_LABEL_D_DS3, [] ) ]) + remapEftNodeValueLabel(eft1, [ 6 ], Node.VALUE_LABEL_D_DS3, [ ( Node.VALUE_LABEL_D_DS1, [1] ), ( Node.VALUE_LABEL_D_DS3, [1] ) ]) else: + scaleEftNodeValueLabels(eft1, [ 5, 6 ], [ Node.VALUE_LABEL_D_DS3 ], [ 1 ]) remapEftNodeValueLabel(eft1, [ 1, 2, 5, 6 ], Node.VALUE_LABEL_D_DS2, [ ( Node.VALUE_LABEL_D_DS2, [] ), ( Node.VALUE_LABEL_D_DS3, [] ) ]) - #remapEftNodeValueLabel(eft1, [ 7, 8 ], Node.VALUE_LABEL_D_DS2, [ ( Node.VALUE_LABEL_D_DS2, [] ), ( Node.VALUE_LABEL_D_DS3, [1]) ]) ln_map = [ 1, 2, 3, 4, 5, 6, 3, 4 ] remapEftLocalNodes(eft1, 6, ln_map) @@ -753,12 +752,45 @@ def refineMesh(cls, meshrefinement, options): :param options: Dict containing options. See getDefaultOptions(). """ assert isinstance(meshrefinement, MeshRefinement) + elementsCountAroundLVFreeWall = options['Number of elements around LV free wall'] + elementsCountAroundRVFreeWall = options['Number of elements around RV free wall'] + elementsCountAroundLV = elementsCountAroundLVFreeWall + elementsCountAroundRVFreeWall + elementsCountAroundVSeptum = elementsCountAroundRVFreeWall + elementsCountAroundRV = 2*elementsCountAroundRVFreeWall + elementsCountUpLVApex = options['Number of elements up LV apex'] + elementsCountUpRV = options['Number of elements up RV'] + elementsCountUpLV = elementsCountUpLVApex + elementsCountUpRV + elementsCountAroundAtrialFreeWall = options['Number of elements around atrial free wall'] + elementsCountAroundAtrialSeptum = options['Number of elements around atrial septum'] + elementsCountAroundAtria = elementsCountAroundAtrialFreeWall + elementsCountAroundAtrialSeptum + elementsCountLVFreeWallRegular = elementsCountAroundAtrialFreeWall//2 + elementsCountRVFreeWallRegular = elementsCountAroundAtrialFreeWall//2 refineElementsCountSurface = options['Refine number of elements surface'] refineElementsCountThroughLVWall = options['Refine number of elements through LV wall'] refineElementsCountThroughWall = options['Refine number of elements through wall'] MeshType_3d_heartventricles1.refineMesh(meshrefinement, options) element = meshrefinement._sourceElementiterator.next() - startBaseElementIdentifier = element.getIdentifier() + startBaseLvElementIdentifier = element.getIdentifier() + startBaseRvElementIdentifier = startBaseLvElementIdentifier + elementsCountLVFreeWallRegular + startBaseSeptumElementIdentifier = startBaseRvElementIdentifier + 1 + elementsCountRVFreeWallRegular + limitBaseElementIdentifier = startBaseSeptumElementIdentifier + elementsCountAroundVSeptum + 1 + #print(startBaseLvElementIdentifier, startBaseRvElementIdentifier, startBaseSeptumElementIdentifier, limitBaseElementIdentifier) + while element.isValid(): + numberInXi1 = refineElementsCountSurface + numberInXi2 = refineElementsCountSurface + numberInXi3 = refineElementsCountThroughLVWall + elementId = element.getIdentifier() + if elementId < startBaseRvElementIdentifier: + pass + elif elementId == startBaseRvElementIdentifier: + # collapsed elements on posterior or anterior interventricular sulcus: + numberInXi1 = refineElementsCountThroughLVWall + elif elementId < startBaseSeptumElementIdentifier: + numberInXi3 = refineElementsCountThroughWall + meshrefinement.refineElementCubeStandard3d(element, numberInXi1, numberInXi2, numberInXi3) + if elementId == (limitBaseElementIdentifier - 1): + return # finish on last so can continue in full heart mesh + element = meshrefinement._sourceElementiterator.next() @classmethod def generateMesh(cls, region, options): From ed06b38e8d424bd474d585e26af7642bfe965150 Mon Sep 17 00:00:00 2001 From: Richard Christie Date: Tue, 6 Nov 2018 14:19:26 +1300 Subject: [PATCH 09/25] Make RV inside ends rounder --- scaffoldmaker/meshtypes/meshtype_3d_heartventricles1.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/scaffoldmaker/meshtypes/meshtype_3d_heartventricles1.py b/scaffoldmaker/meshtypes/meshtype_3d_heartventricles1.py index 83f6daf2..336b7bf6 100644 --- a/scaffoldmaker/meshtypes/meshtype_3d_heartventricles1.py +++ b/scaffoldmaker/meshtypes/meshtype_3d_heartventricles1.py @@ -438,14 +438,17 @@ def generateBaseMesh(cls, region, options): xiUp = max(0.0, (radiansUp - radialDisplacementStartRadiansUp)/(0.5*math.pi - radialDisplacementStartRadiansUp)) midSeptumDisplacement = interpolateCubicHermite([0.0], [0.0], [vSeptumBaseRadialDisplacement], [0.0], xiUp)[0] - numberAround = 8 + numberAround = 10 nx, nd1 = getLeftVentricleInnerPoints(lvRadius, midSeptumDisplacement, rvArcAroundZRadians, z, numberAround, numberAround) # extract septum points, reversing order and derivative nx = [ layerInnerx [-1] ] + nx [-1:-numberAround:-1] + [ layerInnerx [1 - elementsCountAroundRVFreeWall] ] nd1 = [ layerInnerd1[-1] ] + [ ([-d for d in nd1[n1] ]) for n1 in range(-1, -numberAround, -1) ] + [ layerInnerd1[1 - elementsCountAroundRVFreeWall] ] + scale = 2.0 + for n1 in [ -1, 0 ]: + nd1[n1] = [ scale*d for d in nd1[n1] ] px, pd1 = sampleCubicHermiteCurves(nx, nd1, elementsCountAroundVSeptum + 2, - addLengthStart = 0.5*vector.magnitude(nd1[ 0]), lengthFractionStart = 0.5, - addLengthEnd = 0.5*vector.magnitude(nd1[-1]), lengthFractionEnd = 0.5, + addLengthStart = (0.5/scale)*vector.magnitude(nd1[ 0]), lengthFractionStart = 0.5, + addLengthEnd = (0.5/scale)*vector.magnitude(nd1[-1]), lengthFractionEnd = 0.5, arcLengthDerivatives = False)[0:2] for n1 in range(elementsCountAroundVSeptum + 1): # d3 at ends of septum is toward adjacent interventricular sulcus, inside septum is across septum to lvInner From d9a1de1899063e7d41610a9495776e4a7cdd8193 Mon Sep 17 00:00:00 2001 From: Richard Christie Date: Wed, 7 Nov 2018 12:48:35 +1300 Subject: [PATCH 10/25] Tweak base septum elements --- .../meshtypes/meshtype_3d_heartventriclesbase1.py | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/scaffoldmaker/meshtypes/meshtype_3d_heartventriclesbase1.py b/scaffoldmaker/meshtypes/meshtype_3d_heartventriclesbase1.py index ba9f244a..b5cb49f3 100644 --- a/scaffoldmaker/meshtypes/meshtype_3d_heartventriclesbase1.py +++ b/scaffoldmaker/meshtypes/meshtype_3d_heartventriclesbase1.py @@ -64,7 +64,7 @@ def getDefaultOptions(): options['Ventricles outlet spacing z'] = 0.14 options['Ventricles rotation degrees'] = 16.0 options['Ventricles translation x'] = -0.22 - options['Ventricles translation y'] = -0.22 + options['Ventricles translation y'] = -0.2 return options @staticmethod @@ -320,10 +320,12 @@ def generateBaseMesh(cls, region, options): rvOutletOuterd3[0] = [ -d for d in d3 ] # create point above anterior ventricular septum end + sd2 = lvOutletOuterd1[3] fd2 = [ -d for d in vOuterd2[0] ] - px, pd1 = sampleCubicHermiteCurves([ lvOutletOuterx[3], vOuterx[0] ], [ [ 2.0*d for d in lvOutletOuterd1[3] ], fd2 ], 2, arcLengthDerivatives=True)[0:2] - pd1 = smoothCubicHermiteDerivativesLine(px, [ lvOutletOuterd1[3], pd1[1], fd2 ], fixStartDerivative=True, fixEndDerivative=True) - pd3 = smoothCubicHermiteDerivativesLine([ lvOutletOuterx[4], px[1], rvOutletOuterx[-1] ], [ lvOutletOuterd3[4], zero, rvOutletd2 ], fixEndDerivative=True) + px, pd1 = sampleCubicHermiteCurves([ lvOutletOuterx[3], vOuterx[0] ], [ [ 2.0*d for d in sd2 ], fd2 ], 2, arcLengthDerivatives=True)[0:2] + pd1 = smoothCubicHermiteDerivativesLine(px, [ sd2, pd1[1], fd2 ], fixStartDerivative=True, fixEndDerivative=True) + pd3 = smoothCubicHermiteDerivativesLine([ lvOutletOuterx[4], px[1], rvOutletOuterx[-1] ], [ lvOutletOuterd3[4], zero, rvOutletd2 ], + fixEndDerivative=True, magnitudeScalingMode = DerivativeScalingMode.HARMONIC_MEAN) n1 = elementsCountAroundRVFreeWall d3 = vector.crossproduct3(pd3[1], pd1[1]) sx = [ 0.5*(lvInnerx[0][c] + rvInnerx[n1][c]) for c in range(3) ] @@ -684,7 +686,7 @@ def generateBaseMesh(cls, region, options): remapEftNodeValueLabel(eft1, [ 2 ], Node.VALUE_LABEL_D_DS1, [ ( Node.VALUE_LABEL_D_DS2, []) ]) remapEftNodeValueLabel(eft1, [ 4 ], Node.VALUE_LABEL_D_DS1, [ ( Node.VALUE_LABEL_D_DS1, [] ), ( Node.VALUE_LABEL_D_DS3, []) ]) remapEftNodeValueLabel(eft1, [ 2, 4, 6, 8 ], Node.VALUE_LABEL_D_DS3, []) - remapEftNodeValueLabel(eft1, [ 5 ], Node.VALUE_LABEL_D_DS1, [ ( Node.VALUE_LABEL_D_DS2, [] ), ( Node.VALUE_LABEL_D_DS3, [] ) ]) + remapEftNodeValueLabel(eft1, [ 5 ], Node.VALUE_LABEL_D_DS1, [ ( Node.VALUE_LABEL_D_DS1, [1] ), ( Node.VALUE_LABEL_D_DS2, [] ), ( Node.VALUE_LABEL_D_DS3, [] ) ]) remapEftNodeValueLabel(eft1, [ 3 ], Node.VALUE_LABEL_D_DS3, [ ( Node.VALUE_LABEL_D_DS1, [1] ), ( Node.VALUE_LABEL_D_DS3, [] ) ]) remapEftNodeValueLabel(eft1, [ 3 ], Node.VALUE_LABEL_D_DS1, [ ( Node.VALUE_LABEL_D_DS3, [] ) ]) remapEftNodeValueLabel(eft1, [ 7 ], Node.VALUE_LABEL_D_DS3, [ ( Node.VALUE_LABEL_D_DS1, [1] ), ( Node.VALUE_LABEL_D_DS3, [1] ) ]) @@ -710,7 +712,7 @@ def generateBaseMesh(cls, region, options): scaleEftNodeValueLabels(eft1, [ 5, 6 ], [ Node.VALUE_LABEL_D_DS3 ], [ 1 ]) remapEftNodeValueLabel(eft1, [ 1 ], Node.VALUE_LABEL_D_DS2, [ ( Node.VALUE_LABEL_D_DS1, [] ), ( Node.VALUE_LABEL_D_DS2, [] ), ( Node.VALUE_LABEL_D_DS3, [] ) ]) remapEftNodeValueLabel(eft1, [ 2, 6 ], Node.VALUE_LABEL_D_DS2, [ ( Node.VALUE_LABEL_D_DS2, [] ), ( Node.VALUE_LABEL_D_DS3, [] ) ]) - remapEftNodeValueLabel(eft1, [ 5 ], Node.VALUE_LABEL_D_DS2, [ ( Node.VALUE_LABEL_D_DS2, [] ), ( Node.VALUE_LABEL_D_DS3, [] ) ]) + remapEftNodeValueLabel(eft1, [ 5 ], Node.VALUE_LABEL_D_DS2, [ ( Node.VALUE_LABEL_D_DS1, [1] ), ( Node.VALUE_LABEL_D_DS2, [] ), ( Node.VALUE_LABEL_D_DS3, [] ) ]) remapEftNodeValueLabel(eft1, [ 7 ], Node.VALUE_LABEL_D_DS2, [ ( Node.VALUE_LABEL_D_DS1, [1] ), ( Node.VALUE_LABEL_D_DS2, []) ]) elif lv2 == 0: # final element on v septum nids[3] = avsNodeId From be09ac5d8babe6886267262a11e2b3e0b83aec67 Mon Sep 17 00:00:00 2001 From: Richard Christie Date: Thu, 8 Nov 2018 14:03:46 +1300 Subject: [PATCH 11/25] Add extra points on supraventricular crest --- .../meshtypes/meshtype_3d_heartatria1.py | 2 +- .../meshtype_3d_heartventriclesbase1.py | 60 ++++++++++++++++++- 2 files changed, 60 insertions(+), 2 deletions(-) diff --git a/scaffoldmaker/meshtypes/meshtype_3d_heartatria1.py b/scaffoldmaker/meshtypes/meshtype_3d_heartatria1.py index 1f7b8f93..e9d1e573 100644 --- a/scaffoldmaker/meshtypes/meshtype_3d_heartatria1.py +++ b/scaffoldmaker/meshtypes/meshtype_3d_heartatria1.py @@ -39,7 +39,7 @@ def getDefaultOptions(): 'Atrial septum thickness' : 0.08, 'Atrial free wall thickness' : 0.02, 'Atrial base wall thickness' : 0.05, - 'Atrial base slope degrees' : 15.0, + 'Atrial base slope degrees' : 30.0, 'Aorta outer plus diameter' : 0.35, 'Atrial base front incline degrees' : 15.0, 'Atrial base back incline degrees' : 30.0, diff --git a/scaffoldmaker/meshtypes/meshtype_3d_heartventriclesbase1.py b/scaffoldmaker/meshtypes/meshtype_3d_heartventriclesbase1.py index b5cb49f3..4652566b 100644 --- a/scaffoldmaker/meshtypes/meshtype_3d_heartventriclesbase1.py +++ b/scaffoldmaker/meshtypes/meshtype_3d_heartventriclesbase1.py @@ -49,7 +49,7 @@ def getDefaultOptions(): options['Atria major axis rotation degrees'] = 40.0 options['Atrial septum thickness'] = 0.06 options['Atrial base wall thickness'] = 0.05 - options['Atrial base slope degrees'] = 15.0 + options['Atrial base slope degrees'] = 30.0 options['Base height'] = 0.12 options['Base thickness'] = 0.06 options['Fibrous ring thickness'] = 0.01 @@ -429,12 +429,52 @@ def generateBaseMesh(cls, region, options): noa = (elementsCountAroundAtrialSeptum - 1 + elementsCountAroundAtria - n1)%elementsCountAroundAtria nov = -n1 ravInnerd2[0][noa] = interpolateHermiteLagrangeDerivative(rvInnerx[nov], rvInnerd2[nov], ravInnerx[0][noa], 1.0) + # special fix for right cfb + noa = elementsCountAroundAtria - 2 + nov = -elementsCountAroundAtrialSeptum + ravInnerd2[0][noa] = interpolateHermiteLagrangeDerivative(rvInnerx[nov], rvInnerd2[nov], ravInnerx[0][noa], 1.0) + + # set d2 at ra node mid supraventricular crest to be normal to surface; smooth to get final magnitude later + ravsvcn1 = elementsCountAroundAtria - 3 + mag = baseHeight + baseThickness + ravInnerd2[0][ravsvcn1] = vector.setMagnitude(vector.crossproduct3(ravInnerd3[0][ravsvcn1], ravInnerd1[0][ravsvcn1]), mag) + ravOuterd2[0][ravsvcn1] = vector.setMagnitude(vector.crossproduct3(ravOuterd3[0][ravsvcn1], ravOuterd1[0][ravsvcn1]), mag) # copy derivative 3 from av points to LV outlet at centre, left and right cfb: lvOutletOuterd3[0] = lavOuterd3[0][0] lvOutletOuterd3[1] = [ -d for d in ravOuterd3[0][-2] ] lvOutletOuterd3[-1] = [ -d for d in lavOuterd3[0][1] ] + # create points on bottom and top of RV supraventricular crest + ns = (elementsCountAroundRVFreeWall + 1)//2 + nf = elementsCountAroundRVFreeWall + 2 + xis = 0.55 + xif = 1.0 - xis + mx = [ xis*rvInnerx[ns][0] + xif*rvInnerx[nf][0], xis*rvInnerx[ns][1] + xif*rvInnerx[nf][1], -(fibrousRingThickness + baseThickness) ] + md2 = [ (rvInnerx[nf][c] - rvInnerx[ns][c]) for c in range(3) ] + pd1 = smoothCubicHermiteDerivativesLine([ ravInnerx[0][ravsvcn1], mx, rvOutletInnerx[1] ], [ [ -d for d in ravInnerd2[0][ravsvcn1] ], zero, rvOutletd2 ], + fixStartDirection=True, fixEndDerivative = True) + pd2 = smoothCubicHermiteDerivativesLine([ rvInnerx[ns], mx, rvInnerx[nf] ], [ rvInnerd2[ns], md2, [ -d for d in rvInnerd2[nf] ] ], + fixStartDerivative = True, fixEndDerivative = True) + svcix = [ mx[0], mx[1], mx[2] ] # list components to avoid reference bug + svcid1 = pd1[1] + svcid2 = pd2[1] + svcid3 = vector.setMagnitude(vector.crossproduct3(svcid1, svcid2), baseThickness) + ravInnerd2[0][ravsvcn1] = [ -d for d in pd1[0] ] + + mx = [ (mx[c] + svcid3[c]) for c in range(3) ] + md2 = svcid2 + nf = 2 + pd1 = smoothCubicHermiteDerivativesLine([ ravOuterx[0][ravsvcn1], mx, rvOutletOuterx[1] ], [ [ -d for d in ravOuterd2[0][ravsvcn1] ], zero, rvOutletd2 ], + fixStartDirection=True, fixEndDerivative = True) + pd2 = smoothCubicHermiteDerivativesLine([ mx, lvOutletOuterx[nf] ], [ md2, [ -d for d in lvOutletOuterd3[nf] ] ], fixStartDirection=True) + svcox = [ mx[0], mx[1], mx[2] ] # list components to avoid reference bug + svcod1 = pd1[1] + svcod2 = pd2[0] + svcod3 = svcid3 + ravOuterd2[0][ravsvcn1] = [ -d for d in pd1[0] ] + lvOutletOuterd3[nf] = [ -d for d in pd2[1] ] + # LV outlet nodes lvOutletNodeId = [ [], [] ] for n3 in range(2): @@ -535,6 +575,24 @@ def generateBaseMesh(cls, region, options): coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS3, 1, avd3[n1]) nodeIdentifier += 1 + # nodes on bottom and top of RV supraventricular crest + svciNodeId = nodeIdentifier + node = nodes.createNode(nodeIdentifier, nodetemplate) + cache.setNode(node) + coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_VALUE, 1, svcix) + coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS1, 1, svcid1) + coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS2, 1, svcid2) + coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS3, 1, svcid3) + nodeIdentifier += 1 + svcoNodeId = nodeIdentifier + node = nodes.createNode(nodeIdentifier, nodetemplate) + cache.setNode(node) + coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_VALUE, 1, svcox) + coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS1, 1, svcod1) + coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS2, 1, svcod2) + coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS3, 1, svcod3) + nodeIdentifier += 1 + ################# # Create elements ################# From e970ccee8fd660a5c7ba681e9968d5d629728a0a Mon Sep 17 00:00:00 2001 From: Richard Christie Date: Thu, 8 Nov 2018 16:12:58 +1300 Subject: [PATCH 12/25] Add more RV base elements --- .../meshtypes/meshtype_3d_heartventricles1.py | 2 +- .../meshtype_3d_heartventriclesbase1.py | 84 +++++++++++++++++-- 2 files changed, 76 insertions(+), 10 deletions(-) diff --git a/scaffoldmaker/meshtypes/meshtype_3d_heartventricles1.py b/scaffoldmaker/meshtypes/meshtype_3d_heartventricles1.py index 336b7bf6..b8e3d01e 100644 --- a/scaffoldmaker/meshtypes/meshtype_3d_heartventricles1.py +++ b/scaffoldmaker/meshtypes/meshtype_3d_heartventricles1.py @@ -48,7 +48,7 @@ def getDefaultOptions(): 'RV side extension' : 0.12, 'RV side extension growth factor' : 0.5, 'Ventricular septum thickness' : 0.1, - 'Ventricular septum base radial displacement' : 0.1, + 'Ventricular septum base radial displacement' : 0.05, 'Use cross derivatives' : False, 'Refine' : False, 'Refine number of elements surface' : 4, diff --git a/scaffoldmaker/meshtypes/meshtype_3d_heartventriclesbase1.py b/scaffoldmaker/meshtypes/meshtype_3d_heartventriclesbase1.py index 4652566b..dcb17f37 100644 --- a/scaffoldmaker/meshtypes/meshtype_3d_heartventriclesbase1.py +++ b/scaffoldmaker/meshtypes/meshtype_3d_heartventriclesbase1.py @@ -448,7 +448,7 @@ def generateBaseMesh(cls, region, options): # create points on bottom and top of RV supraventricular crest ns = (elementsCountAroundRVFreeWall + 1)//2 nf = elementsCountAroundRVFreeWall + 2 - xis = 0.55 + xis = 0.6 xif = 1.0 - xis mx = [ xis*rvInnerx[ns][0] + xif*rvInnerx[nf][0], xis*rvInnerx[ns][1] + xif*rvInnerx[nf][1], -(fibrousRingThickness + baseThickness) ] md2 = [ (rvInnerx[nf][c] - rvInnerx[ns][c]) for c in range(3) ] @@ -647,7 +647,9 @@ def generateBaseMesh(cls, region, options): meshGroup.addElement(element) # RV base elements, starting at crux / posterior interventricular sulcus - for e in range(-1, elementsCountAroundRVFreeWall): + scalefactors5hanging = [ -1.0, 0.5, 0.25, 0.125, 0.75 ] + elementsCountRVHanging = 1 + for e in range(-1, elementsCountAroundRVFreeWall + elementsCountRVHanging): eft1 = eft nids = None scalefactors = None @@ -655,11 +657,15 @@ def generateBaseMesh(cls, region, options): noa = elementsCountAroundAtrialSeptum - 1 + e niv = e - nov = elementsCountAroundLVFreeWall + e + if e > (elementsCountAroundRVFreeWall + elementsCountRVHanging - 3): + niv -= 1 + nivp = niv + 1 + nov = elementsCountAroundLVFreeWall + niv + novp = (nov + 1)%elementsCountAroundLV if e == -1: # crux / posterior interventricular sulcus: collapsed to 6 element wedge - nids = [ lvInnerNodeId[elementsCountAroundLVFreeWall], rvInnerNodeId[niv + 1], lavInnerNodeId[0][elementsCountAroundAtrialFreeWall], ravInnerNodeId[0][noa + 1], - vOuterNodeId[nov + 1], ravOuterNodeId[0][noa + 1] ] + nids = [ lvInnerNodeId[elementsCountAroundLVFreeWall], rvInnerNodeId[nivp], lavInnerNodeId[0][elementsCountAroundAtrialFreeWall], ravInnerNodeId[0][noa + 1], + vOuterNodeId[novp], ravOuterNodeId[0][noa + 1] ] meshGroups += [ lvMeshGroup ] eft1 = tricubichermite.createEftNoCrossDerivatives() setEftScaleFactorIds(eft1, [1], []) @@ -673,14 +679,73 @@ def generateBaseMesh(cls, region, options): ln_map = [ 1, 2, 3, 4, 5, 5, 6, 6 ] remapEftLocalNodes(eft1, 6, ln_map) elif e < elementsCountRVFreeWallRegular: - nids = [ rvInnerNodeId[niv], rvInnerNodeId[niv + 1], ravInnerNodeId[0][noa], ravInnerNodeId[0][noa + 1], - vOuterNodeId[nov], vOuterNodeId[nov + 1], ravOuterNodeId[0][noa], ravOuterNodeId[0][noa + 1] ] + nids = [ rvInnerNodeId[niv], rvInnerNodeId[nivp], ravInnerNodeId[0][noa], ravInnerNodeId[0][noa + 1], + vOuterNodeId[nov], vOuterNodeId[novp], ravOuterNodeId[0][noa], ravOuterNodeId[0][noa + 1] ] if e == 0: # general linear map d3 adjacent to collapsed crux, transition to atria eft1 = tricubichermite.createEftNoCrossDerivatives() setEftScaleFactorIds(eft1, [1], []) scalefactors = [ -1.0 ] remapEftNodeValueLabel(eft1, [ 5, 7 ], Node.VALUE_LABEL_D_DS3, [ ( Node.VALUE_LABEL_D_DS1, [1] ), ( Node.VALUE_LABEL_D_DS3, []) ]) + elif e == elementsCountRVFreeWallRegular: + # supraventricular crest outer 1 + nids = [ rvInnerNodeId[niv], rvInnerNodeId[nivp], ravInnerNodeId[0][noa], svciNodeId, + vOuterNodeId[nov], vOuterNodeId[novp], ravOuterNodeId[0][noa], svcoNodeId ] + eft1 = tricubichermite.createEftNoCrossDerivatives() + setEftScaleFactorIds(eft1, [1], []) + scalefactors = [ -1.0 ] + remapEftNodeValueLabel(eft1, [ 3, 7 ], Node.VALUE_LABEL_D_DS1, [ ( Node.VALUE_LABEL_D_DS1, [] ), ( Node.VALUE_LABEL_D_DS2, [1] ) ]) + elif e == (elementsCountRVFreeWallRegular + 1): + # supraventricular crest outer 2, outer infundibulum 1 + nids = [ rvInnerNodeId[niv], rvInnerNodeId[nivp], svciNodeId, rvOutletNodeId[0][2], + vOuterNodeId[nov], vOuterNodeId[novp], svcoNodeId, rvOutletNodeId[1][2] ] + eft1 = tricubichermite.createEftNoCrossDerivatives() + setEftScaleFactorIds(eft1, [1], []) + scalefactors = [ -1.0 ] + tricubichermite.setEftLinearDerivative(eft1, [ 4, 8 ], Node.VALUE_LABEL_D_DS3, 4, 8, 1) + remapEftNodeValueLabel(eft1, [ 4, 8 ], Node.VALUE_LABEL_D_DS1, [ ( Node.VALUE_LABEL_D_DS1, [] ), ( Node.VALUE_LABEL_D_DS2, [] ) ]) + meshGroups += [ conusArteriosusMeshGroup ] + elif e == (elementsCountAroundRVFreeWall + elementsCountRVHanging - 3): + # supraventricular crest outer 3, outer infundibulum 2 + # 1st of pair of elements with hanging nodes at xi1=0.5 on xi2 == 0 plane + nids = [ rvInnerNodeId[niv], rvInnerNodeId[nivp], rvOutletNodeId[0][2], rvOutletNodeId[0][3], + vOuterNodeId[nov], vOuterNodeId[novp], rvOutletNodeId[1][2], rvOutletNodeId[1][3] ] + eft1 = tricubichermite.createEftNoCrossDerivatives() + setEftScaleFactorIds(eft1, [1, 102, 104, 108, 304], []) + scalefactors = scalefactors5hanging + tricubichermite.setEftMidsideXi1HangingNode(eft1, 2, 1, 1, 2, [1, 2, 3, 4, 5]) + tricubichermite.setEftMidsideXi1HangingNode(eft1, 6, 5, 5, 6, [1, 2, 3, 4, 5]) + tricubichermite.setEftLinearDerivative(eft1, [ 3, 7 ], Node.VALUE_LABEL_D_DS3, 3, 7, 1) + tricubichermite.setEftLinearDerivative(eft1, [ 4, 8 ], Node.VALUE_LABEL_D_DS3, 4, 8, 1) + #remapEftNodeValueLabel(eft1, [ 3, 7 ], Node.VALUE_LABEL_D_DS2, [ ( Node.VALUE_LABEL_D_DS1, [1] ), ( Node.VALUE_LABEL_D_DS2, [] ) ]) + #tricubichermite.setEftLinearDerivative(eft1, [ 4, 8 ], Node.VALUE_LABEL_D_DS3, 4, 8, 1) + #remapEftNodeValueLabel(eft1, [ 4, 8 ], Node.VALUE_LABEL_D_DS2, [ ( Node.VALUE_LABEL_D_DS1, [1] ), ( Node.VALUE_LABEL_D_DS2, [] ) ]) # must do before following + #remapEftNodeValueLabel(eft1, [ 4, 8 ], Node.VALUE_LABEL_D_DS1, [ ( Node.VALUE_LABEL_D_DS2, [] ) ]) + meshGroups += [ conusArteriosusMeshGroup ] + elif e == (elementsCountAroundRVFreeWall + elementsCountRVHanging - 2): + # outer infundibulum 3 + # 2nd of pair of elements with hanging nodes at xi1=0.5 on xi2 == 0 plane + nids = [ rvInnerNodeId[niv], rvInnerNodeId[nivp], rvOutletNodeId[0][3], rvOutletNodeId[0][4], + vOuterNodeId[nov], vOuterNodeId[novp], rvOutletNodeId[1][3], rvOutletNodeId[1][4] ] + eft1 = tricubichermite.createEftNoCrossDerivatives() + setEftScaleFactorIds(eft1, [1, 102, 104, 108, 304], []) + scalefactors = scalefactors5hanging + tricubichermite.setEftMidsideXi1HangingNode(eft1, 1, 2, 1, 2, [1, 2, 3, 4, 5]) + tricubichermite.setEftMidsideXi1HangingNode(eft1, 5, 6, 5, 6, [1, 2, 3, 4, 5]) + tricubichermite.setEftLinearDerivative(eft1, [ 3, 7 ], Node.VALUE_LABEL_D_DS3, 3, 7, 1) + tricubichermite.setEftLinearDerivative(eft1, [ 4, 8 ], Node.VALUE_LABEL_D_DS3, 4, 8, 1) + meshGroups += [ conusArteriosusMeshGroup ] + elif e == (elementsCountAroundRVFreeWall + elementsCountRVHanging - 1): + # outer infundibulum 4 + nids = [ rvInnerNodeId[niv], rvInnerNodeId[nivp], rvOutletNodeId[0][4], rvOutletNodeId[0][5], + vOuterNodeId[nov], vOuterNodeId[novp], rvOutletNodeId[1][4], rvOutletNodeId[1][5] ] + eft1 = tricubichermite.createEftNoCrossDerivatives() + setEftScaleFactorIds(eft1, [1], []) + scalefactors = [ 1.0 ] + tricubichermite.setEftLinearDerivative(eft1, [ 3, 7 ], Node.VALUE_LABEL_D_DS3, 3, 7, 1) + tricubichermite.setEftLinearDerivative(eft1, [ 4, 8 ], Node.VALUE_LABEL_D_DS3, 4, 8, 1) + remapEftNodeValueLabel(eft1, [ 6 ], Node.VALUE_LABEL_D_DS3, [ ( Node.VALUE_LABEL_D_DS1, [] ), ( Node.VALUE_LABEL_D_DS3, []) ]) + meshGroups += [ conusArteriosusMeshGroup ] else: continue @@ -795,7 +860,7 @@ def generateBaseMesh(cls, region, options): result3 = element.setScaleFactors(eft1, scalefactors) else: result3 = 7 - print('create element sp base', elementIdentifier, result, result2, result3, nids) + #print('create element sp base', elementIdentifier, result, result2, result3, nids) elementIdentifier += 1 for meshGroup in meshGroups: @@ -832,7 +897,8 @@ def refineMesh(cls, meshrefinement, options): element = meshrefinement._sourceElementiterator.next() startBaseLvElementIdentifier = element.getIdentifier() startBaseRvElementIdentifier = startBaseLvElementIdentifier + elementsCountLVFreeWallRegular - startBaseSeptumElementIdentifier = startBaseRvElementIdentifier + 1 + elementsCountRVFreeWallRegular + elementsCountRVHanging = 1 + startBaseSeptumElementIdentifier = startBaseRvElementIdentifier + elementsCountAroundRVFreeWall + elementsCountRVHanging + 1 limitBaseElementIdentifier = startBaseSeptumElementIdentifier + elementsCountAroundVSeptum + 1 #print(startBaseLvElementIdentifier, startBaseRvElementIdentifier, startBaseSeptumElementIdentifier, limitBaseElementIdentifier) while element.isValid(): From 1070aad0f42b2608afe8d7e5c4326a7364089d83 Mon Sep 17 00:00:00 2001 From: Richard Christie Date: Fri, 9 Nov 2018 11:45:43 +1300 Subject: [PATCH 13/25] Close rv supraventricular crest --- .../meshtype_3d_heartventriclesbase1.py | 182 ++++++++++++++++-- 1 file changed, 162 insertions(+), 20 deletions(-) diff --git a/scaffoldmaker/meshtypes/meshtype_3d_heartventriclesbase1.py b/scaffoldmaker/meshtypes/meshtype_3d_heartventriclesbase1.py index dcb17f37..556e79b5 100644 --- a/scaffoldmaker/meshtypes/meshtype_3d_heartventriclesbase1.py +++ b/scaffoldmaker/meshtypes/meshtype_3d_heartventriclesbase1.py @@ -410,8 +410,8 @@ def generateBaseMesh(cls, region, options): ravOuterd2[n2].append(None) ravOuterd3[n2].append(None) # get av bottom derivative 2 from Hermite-Lagrange interpolation from top row of ventricles - elementsCountLVFreeWallRegular = elementsCountAroundAtrialFreeWall//2 - elementsCountRVFreeWallRegular = elementsCountAroundAtrialFreeWall//2 + elementsCountLVFreeWallRegular = elementsCountAroundLVFreeWall - 1 + elementsCountRVFreeWallRegular = 3 for n1 in range(elementsCountLVFreeWallRegular + 1): noa = elementsCountAroundAtrialFreeWall - elementsCountLVFreeWallRegular + n1 nov = elementsCountAroundLVFreeWall - elementsCountLVFreeWallRegular + n1 @@ -431,8 +431,11 @@ def generateBaseMesh(cls, region, options): ravInnerd2[0][noa] = interpolateHermiteLagrangeDerivative(rvInnerx[nov], rvInnerd2[nov], ravInnerx[0][noa], 1.0) # special fix for right cfb noa = elementsCountAroundAtria - 2 - nov = -elementsCountAroundAtrialSeptum - ravInnerd2[0][noa] = interpolateHermiteLagrangeDerivative(rvInnerx[nov], rvInnerd2[nov], ravInnerx[0][noa], 1.0) + nov = -elementsCountAroundAtrialSeptum - 1 + mag = baseHeight + baseThickness + d2 = vector.setMagnitude(vector.crossproduct3(ravInnerd3[0][noa], ravInnerd1[0][noa]), mag) + pd2 = smoothCubicHermiteDerivativesLine([ rvInnerx[nov], ravInnerx[0][noa]], [ rvInnerd2[nov], d2 ], fixStartDerivative=True, fixEndDirection=True) + ravInnerd2[0][noa] = pd2[1] # set d2 at ra node mid supraventricular crest to be normal to surface; smooth to get final magnitude later ravsvcn1 = elementsCountAroundAtria - 3 @@ -446,7 +449,7 @@ def generateBaseMesh(cls, region, options): lvOutletOuterd3[-1] = [ -d for d in lavOuterd3[0][1] ] # create points on bottom and top of RV supraventricular crest - ns = (elementsCountAroundRVFreeWall + 1)//2 + ns = elementsCountAroundRVFreeWall//2 + 1 nf = elementsCountAroundRVFreeWall + 2 xis = 0.6 xif = 1.0 - xis @@ -614,7 +617,9 @@ def generateBaseMesh(cls, region, options): elementtemplate1 = mesh.createElementtemplate() elementtemplate1.setElementShapeType(Element.SHAPE_TYPE_CUBE) - # LV base elements starting at anterior interventricular sulcus + scalefactors5hanging = [ -1.0, 0.5, 0.25, 0.125, 0.75 ] + + # LV base elements row 1, starting at anterior interventricular sulcus for e in range(-1, elementsCountAroundLVFreeWall): eft1 = eft nids = None @@ -640,14 +645,13 @@ def generateBaseMesh(cls, region, options): result3 = element.setScaleFactors(eft1, scalefactors) else: result3 = 7 - #print('create element lv base', elementIdentifier, result, result2, result3, nids) + #print('create element lv base r1', elementIdentifier, result, result2, result3, nids) elementIdentifier += 1 for meshGroup in meshGroups: meshGroup.addElement(element) - # RV base elements, starting at crux / posterior interventricular sulcus - scalefactors5hanging = [ -1.0, 0.5, 0.25, 0.125, 0.75 ] + # RV base elements row 1, starting at crux / posterior interventricular sulcus elementsCountRVHanging = 1 for e in range(-1, elementsCountAroundRVFreeWall + elementsCountRVHanging): eft1 = eft @@ -717,10 +721,6 @@ def generateBaseMesh(cls, region, options): tricubichermite.setEftMidsideXi1HangingNode(eft1, 6, 5, 5, 6, [1, 2, 3, 4, 5]) tricubichermite.setEftLinearDerivative(eft1, [ 3, 7 ], Node.VALUE_LABEL_D_DS3, 3, 7, 1) tricubichermite.setEftLinearDerivative(eft1, [ 4, 8 ], Node.VALUE_LABEL_D_DS3, 4, 8, 1) - #remapEftNodeValueLabel(eft1, [ 3, 7 ], Node.VALUE_LABEL_D_DS2, [ ( Node.VALUE_LABEL_D_DS1, [1] ), ( Node.VALUE_LABEL_D_DS2, [] ) ]) - #tricubichermite.setEftLinearDerivative(eft1, [ 4, 8 ], Node.VALUE_LABEL_D_DS3, 4, 8, 1) - #remapEftNodeValueLabel(eft1, [ 4, 8 ], Node.VALUE_LABEL_D_DS2, [ ( Node.VALUE_LABEL_D_DS1, [1] ), ( Node.VALUE_LABEL_D_DS2, [] ) ]) # must do before following - #remapEftNodeValueLabel(eft1, [ 4, 8 ], Node.VALUE_LABEL_D_DS1, [ ( Node.VALUE_LABEL_D_DS2, [] ) ]) meshGroups += [ conusArteriosusMeshGroup ] elif e == (elementsCountAroundRVFreeWall + elementsCountRVHanging - 2): # outer infundibulum 3 @@ -756,13 +756,13 @@ def generateBaseMesh(cls, region, options): result3 = element.setScaleFactors(eft1, scalefactors) else: result3 = 7 - #print('create element rv base', elementIdentifier, result, result2, result3, nids) + #print('create element rv base r1', elementIdentifier, result, result2, result3, nids) elementIdentifier += 1 for meshGroup in meshGroups: meshGroup.addElement(element) - # interventricular septum elements + # interventricular septum elements, row 1 for e in range(elementsCountAroundVSeptum + 1): eft1 = eft nids = None @@ -866,6 +866,142 @@ def generateBaseMesh(cls, region, options): for meshGroup in meshGroups: meshGroup.addElement(element) + # RV base elements remainder + elementsCountRVHanging = 1 + for e in range(5): + eft1 = eft + nids = None + scalefactors = None + meshGroups = [ rvMeshGroup ] + + noa = elementsCountAroundAtrialSeptum - 1 + e + niv = e + if e > (elementsCountAroundRVFreeWall + elementsCountRVHanging - 3): + niv -= 1 + nivp = niv + 1 + nov = elementsCountAroundLVFreeWall + niv + novp = (nov + 1)%elementsCountAroundLV + if e == 0: + # 6 node collapsed vs-ra shim element + rvin1 = -elementsCountAroundAtrialSeptum + nids = [ lvOutletNodeId[1][0], lvOutletNodeId[1][1], rvInnerNodeId[rvin1], rvInnerNodeId[rvin1 - 1], ravInnerNodeId[0][-1], ravInnerNodeId[0][-2] ] + eft1 = tricubichermite.createEftNoCrossDerivatives() + scalefactors = [ -1.0 ] + setEftScaleFactorIds(eft1, [1], []) + remapEftNodeValueLabel(eft1, [ 1, 2, 3, 4 ], Node.VALUE_LABEL_D_DS2, []) + remapEftNodeValueLabel(eft1, [ 1 ], Node.VALUE_LABEL_D_DS3, [ ( Node.VALUE_LABEL_D_DS1, [] ), ( Node.VALUE_LABEL_D_DS2, [1] ) ]) + remapEftNodeValueLabel(eft1, [ 2 ], Node.VALUE_LABEL_D_DS3, [ ( Node.VALUE_LABEL_D_DS2, [1] ) ]) + remapEftNodeValueLabel(eft1, [ 3 ], Node.VALUE_LABEL_D_DS3, [ ( Node.VALUE_LABEL_D_DS1, [] ), ( Node.VALUE_LABEL_D_DS3, [1] ) ]) + scaleEftNodeValueLabels(eft1, [ 5, 6 ], [ Node.VALUE_LABEL_D_DS1 ], [ 1 ]) + remapEftNodeValueLabel(eft1, [ 5 ], Node.VALUE_LABEL_D_DS3, [ ( Node.VALUE_LABEL_D_DS1, [] ), ( Node.VALUE_LABEL_D_DS2, [1] ), ( Node.VALUE_LABEL_D_DS3, [1] ) ]) + remapEftNodeValueLabel(eft1, [ 6 ], Node.VALUE_LABEL_D_DS3, [ ( Node.VALUE_LABEL_D_DS2, [1] ), ( Node.VALUE_LABEL_D_DS3, [1] ) ]) + scaleEftNodeValueLabels(eft1, [ 7, 8 ], [ Node.VALUE_LABEL_D_DS1, Node.VALUE_LABEL_D_DS3 ], [ 1 ]) + ln_map = [ 1, 2, 1, 2, 3, 4, 5, 6 ] + remapEftLocalNodes(eft1, 6, ln_map) + elif e == 1: + # 7-node collapsed rv crest inner 1, by RA-LV outlet junction + rvin1 = -elementsCountAroundAtrialSeptum - 1 + nids = [ ravInnerNodeId[0][-3], rvInnerNodeId[rvin1 - 1], ravInnerNodeId[0][-2], rvInnerNodeId[rvin1], ravOuterNodeId[0][-3], lvOutletNodeId[1][2], lvOutletNodeId[1][1] ] + eft1 = tricubichermite.createEftNoCrossDerivatives() + setEftScaleFactorIds(eft1, [1], []) + scalefactors = [ -1.0 ] + remapEftNodeValueLabel(eft1, [ 1, 5 ], Node.VALUE_LABEL_D_DS1, [ ( 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 ], Node.VALUE_LABEL_D_DS1, [ ( Node.VALUE_LABEL_D_DS1, [1] ), ( Node.VALUE_LABEL_D_DS2, [1] ) ]) + remapEftNodeValueLabel(eft1, [ 2, 4 ], Node.VALUE_LABEL_D_DS3, [ ( Node.VALUE_LABEL_D_DS2, [] ), ( Node.VALUE_LABEL_D_DS3, [] ) ]) + remapEftNodeValueLabel(eft1, [ 3, 4 ], Node.VALUE_LABEL_D_DS1, [ ( Node.VALUE_LABEL_D_DS2, [1] ) ]) + remapEftNodeValueLabel(eft1, [ 2, 4 ], Node.VALUE_LABEL_D_DS2, [ ( Node.VALUE_LABEL_D_DS1, [] ) ]) + remapEftNodeValueLabel(eft1, [ 3 ], Node.VALUE_LABEL_D_DS2, [ ( Node.VALUE_LABEL_D_DS1, [] ) ]) + remapEftNodeValueLabel(eft1, [ 6 ], Node.VALUE_LABEL_D_DS1, [ ( Node.VALUE_LABEL_D_DS1, [] ), ( Node.VALUE_LABEL_D_DS3, [1] ) ]) + remapEftNodeValueLabel(eft1, [ 6 ], Node.VALUE_LABEL_D_DS2, [ ( Node.VALUE_LABEL_D_DS1, [1] ) ]) + remapEftNodeValueLabel(eft1, [ 6 ], Node.VALUE_LABEL_D_DS3, [ ( Node.VALUE_LABEL_D_DS2, [] ) ]) + remapEftNodeValueLabel(eft1, [ 7, 8 ], Node.VALUE_LABEL_D_DS1, []) + remapEftNodeValueLabel(eft1, [ 7 ], Node.VALUE_LABEL_D_DS2, [ ( Node.VALUE_LABEL_D_DS1, [1] ), ( Node.VALUE_LABEL_D_DS3, [1] ) ]) + remapEftNodeValueLabel(eft1, [ 7 ], Node.VALUE_LABEL_D_DS3, [ ( Node.VALUE_LABEL_D_DS3, [1] ) ]) + remapEftNodeValueLabel(eft1, [ 8 ], Node.VALUE_LABEL_D_DS2, [ ( Node.VALUE_LABEL_D_DS1, [1] ) ]) + remapEftNodeValueLabel(eft1, [ 8 ], Node.VALUE_LABEL_D_DS3, [ ( Node.VALUE_LABEL_D_DS2, [] ) ]) + ln_map = [ 1, 2, 3, 4, 5, 6, 7, 7 ] + remapEftLocalNodes(eft1, 7, ln_map) + elif e == 2: + # 8-node rv crest row 2 element 1 + rvin1 = -elementsCountAroundAtrialSeptum - 2 + nids = [ ravInnerNodeId[0][-4], svciNodeId, ravInnerNodeId[0][-3], rvInnerNodeId[rvin1], ravOuterNodeId[0][-4], svcoNodeId, ravOuterNodeId[0][-3], lvOutletNodeId[1][2] ] + eft1 = tricubichermite.createEftNoCrossDerivatives() + setEftScaleFactorIds(eft1, [1], []) + scalefactors = [ -1.0 ] + remapEftNodeValueLabel(eft1, [ 1, 3, 5, 7 ], Node.VALUE_LABEL_D_DS1, [ ( Node.VALUE_LABEL_D_DS1, [] ), ( Node.VALUE_LABEL_D_DS2, [1] ) ]) # must do before following + remapEftNodeValueLabel(eft1, [ 1, 3, 5, 7 ], Node.VALUE_LABEL_D_DS2, [ ( Node.VALUE_LABEL_D_DS1, [] )]) + remapEftNodeValueLabel(eft1, [ 4 ], Node.VALUE_LABEL_D_DS1, [ ( Node.VALUE_LABEL_D_DS1, [1] ), ( Node.VALUE_LABEL_D_DS2, [1] ) ]) + remapEftNodeValueLabel(eft1, [ 4 ], Node.VALUE_LABEL_D_DS2, [ ( Node.VALUE_LABEL_D_DS2, [1] ) ]) + remapEftNodeValueLabel(eft1, [ 4 ], Node.VALUE_LABEL_D_DS3, [ ( Node.VALUE_LABEL_D_DS2, [] ), ( Node.VALUE_LABEL_D_DS3, [] ) ]) + remapEftNodeValueLabel(eft1, [ 8 ], Node.VALUE_LABEL_D_DS1, [ ( Node.VALUE_LABEL_D_DS1, [] ), ( Node.VALUE_LABEL_D_DS3, [1] ) ]) + remapEftNodeValueLabel(eft1, [ 8 ], Node.VALUE_LABEL_D_DS2, [ ( Node.VALUE_LABEL_D_DS3, [1] ) ]) + remapEftNodeValueLabel(eft1, [ 8 ], Node.VALUE_LABEL_D_DS3, [ ( Node.VALUE_LABEL_D_DS2, [] ) ]) + elif e == 3: + # 8-node wedge rv crest row 2 element 2 + rvin1 = -elementsCountAroundAtrialSeptum - 2 + nids = [ svciNodeId, rvOutletNodeId[0][2], rvInnerNodeId[rvin1], rvOutletNodeId[0][1], svcoNodeId, rvOutletNodeId[1][2], lvOutletNodeId[1][2], rvOutletNodeId[1][1] ] + eft1 = tricubichermite.createEftNoCrossDerivatives() + setEftScaleFactorIds(eft1, [1], []) + scalefactors = [ -1.0 ] + tricubichermite.setEftLinearDerivative(eft1, [ 2, 6 ], Node.VALUE_LABEL_D_DS3, 2, 6, 1) + tricubichermite.setEftLinearDerivative(eft1, [ 4, 8 ], Node.VALUE_LABEL_D_DS3, 4, 8, 1) + remapEftNodeValueLabel(eft1, [ 2, 6 ], Node.VALUE_LABEL_D_DS1, [ ( Node.VALUE_LABEL_D_DS1, [] ), ( Node.VALUE_LABEL_D_DS2, [] ) ]) + remapEftNodeValueLabel(eft1, [ 2, 4, 6, 8 ], Node.VALUE_LABEL_D_DS2, [ ( Node.VALUE_LABEL_D_DS1, [1] ) ]) # must do before following + remapEftNodeValueLabel(eft1, [ 2, 4, 6, 8 ], Node.VALUE_LABEL_D_DS1, [ ( Node.VALUE_LABEL_D_DS2, [] ) ]) + remapEftNodeValueLabel(eft1, [ 3 ], Node.VALUE_LABEL_D_DS1, [ ( Node.VALUE_LABEL_D_DS1, [1] ), ( Node.VALUE_LABEL_D_DS2, [] ) ]) + remapEftNodeValueLabel(eft1, [ 3 ], Node.VALUE_LABEL_D_DS2, [ ( Node.VALUE_LABEL_D_DS2, [1] ) ]) + remapEftNodeValueLabel(eft1, [ 3 ], Node.VALUE_LABEL_D_DS3, [ ( Node.VALUE_LABEL_D_DS2, [] ), ( Node.VALUE_LABEL_D_DS3, [] ) ]) + remapEftNodeValueLabel(eft1, [ 7 ], Node.VALUE_LABEL_D_DS1, [ ( Node.VALUE_LABEL_D_DS1, [] ), ( Node.VALUE_LABEL_D_DS3, [] ) ]) + remapEftNodeValueLabel(eft1, [ 7 ], Node.VALUE_LABEL_D_DS2, [ ( Node.VALUE_LABEL_D_DS3, [1] ) ]) + remapEftNodeValueLabel(eft1, [ 7 ], Node.VALUE_LABEL_D_DS3, [ ( Node.VALUE_LABEL_D_DS2, [] ) ]) + meshGroups += [ conusArteriosusMeshGroup ] + elif e == 4: + # 8-node rv crest inner 4 by rv outlet + rvin1 = -elementsCountAroundAtrialSeptum - 2 + nids = [ rvInnerNodeId[rvin1], rvOutletNodeId[0][1], rvInnerNodeId[rvin1 - 1], rvOutletNodeId[0][0], lvOutletNodeId[1][2], rvOutletNodeId[1][1], lvOutletNodeId[1][3], rvOutletNodeId[1][0] ] + eft1 = tricubichermite.createEftNoCrossDerivatives() + setEftScaleFactorIds(eft1, [1], []) + scalefactors = [ -1.0 ] + tricubichermite.setEftLinearDerivative(eft1, [ 2, 6 ], Node.VALUE_LABEL_D_DS3, 2, 6, 1) + tricubichermite.setEftLinearDerivative(eft1, [ 4, 8 ], Node.VALUE_LABEL_D_DS3, 4, 8, 1) + remapEftNodeValueLabel(eft1, [ 1 ], Node.VALUE_LABEL_D_DS1, [ ( Node.VALUE_LABEL_D_DS1, [1] ), ( Node.VALUE_LABEL_D_DS2, [] ) ]) + remapEftNodeValueLabel(eft1, [ 1 ], Node.VALUE_LABEL_D_DS2, [ ( Node.VALUE_LABEL_D_DS1, [1] ) ]) + remapEftNodeValueLabel(eft1, [ 1 ], Node.VALUE_LABEL_D_DS3, [ ( Node.VALUE_LABEL_D_DS2, [] ), ( Node.VALUE_LABEL_D_DS3, [] ) ]) + remapEftNodeValueLabel(eft1, [ 2, 4 ], Node.VALUE_LABEL_D_DS2, [ ( Node.VALUE_LABEL_D_DS1, [1] ) ]) # must do before following + remapEftNodeValueLabel(eft1, [ 2, 4 ], Node.VALUE_LABEL_D_DS1, [ ( Node.VALUE_LABEL_D_DS2, [] ) ]) + remapEftNodeValueLabel(eft1, [ 3 ], Node.VALUE_LABEL_D_DS2, [ ( Node.VALUE_LABEL_D_DS1, [1] ) ]) + remapEftNodeValueLabel(eft1, [ 3 ], Node.VALUE_LABEL_D_DS3, [ ( Node.VALUE_LABEL_D_DS2, [] ), ( Node.VALUE_LABEL_D_DS3, [] ) ]) + remapEftNodeValueLabel(eft1, [ 3 ], Node.VALUE_LABEL_D_DS1, [ ( Node.VALUE_LABEL_D_DS2, [] ) ]) + remapEftNodeValueLabel(eft1, [ 5 ], Node.VALUE_LABEL_D_DS1, [ ( Node.VALUE_LABEL_D_DS1, [] ), ( Node.VALUE_LABEL_D_DS3, [] ) ]) + remapEftNodeValueLabel(eft1, [ 5 ], Node.VALUE_LABEL_D_DS2, [ ( Node.VALUE_LABEL_D_DS1, [] ) ]) + remapEftNodeValueLabel(eft1, [ 5 ], Node.VALUE_LABEL_D_DS3, [ ( Node.VALUE_LABEL_D_DS2, [] ) ]) + remapEftNodeValueLabel(eft1, [ 6 ], Node.VALUE_LABEL_D_DS2, [ ( Node.VALUE_LABEL_D_DS1, [1] ) ]) + remapEftNodeValueLabel(eft1, [ 6 ], Node.VALUE_LABEL_D_DS1, [ ( Node.VALUE_LABEL_D_DS2, [] ) ]) + remapEftNodeValueLabel(eft1, [ 7 ], Node.VALUE_LABEL_D_DS3, [ ( Node.VALUE_LABEL_D2_DS1DS2, [] ) ]) # temporary, to swap with D_DS2 + remapEftNodeValueLabel(eft1, [ 7 ], Node.VALUE_LABEL_D_DS1, [ ( Node.VALUE_LABEL_D_DS3, [] ) ]) + remapEftNodeValueLabel(eft1, [ 7 ], Node.VALUE_LABEL_D_DS2, [ ( Node.VALUE_LABEL_D_DS1, [] ) ]) + remapEftNodeValueLabel(eft1, [ 7 ], Node.VALUE_LABEL_D2_DS1DS2, [ ( Node.VALUE_LABEL_D_DS2, [] ) ]) + remapEftNodeValueLabel(eft1, [ 8 ], Node.VALUE_LABEL_D_DS1, [ ( Node.VALUE_LABEL_D_DS3, [1] ) ]) + remapEftNodeValueLabel(eft1, [ 8 ], Node.VALUE_LABEL_D_DS2, [ ( Node.VALUE_LABEL_D_DS1, [1] ) ]) + meshGroups += [ conusArteriosusMeshGroup ] + else: + continue + + result = elementtemplate1.defineField(coordinates, -1, eft1) + element = mesh.createElement(elementIdentifier, elementtemplate1) + result2 = element.setNodesByIdentifier(eft1, nids) + if scalefactors: + result3 = element.setScaleFactors(eft1, scalefactors) + else: + result3 = 7 + print('create element rv base r2', elementIdentifier, result, result2, result3, nids) + elementIdentifier += 1 + + for meshGroup in meshGroups: + meshGroup.addElement(element) + + fm.endChange() return annotationGroups @@ -888,8 +1024,8 @@ def refineMesh(cls, meshrefinement, options): elementsCountAroundAtrialFreeWall = options['Number of elements around atrial free wall'] elementsCountAroundAtrialSeptum = options['Number of elements around atrial septum'] elementsCountAroundAtria = elementsCountAroundAtrialFreeWall + elementsCountAroundAtrialSeptum - elementsCountLVFreeWallRegular = elementsCountAroundAtrialFreeWall//2 - elementsCountRVFreeWallRegular = elementsCountAroundAtrialFreeWall//2 + elementsCountLVFreeWallRegular = elementsCountAroundLVFreeWall - 1 + elementsCountRVFreeWallRegular = 3 refineElementsCountSurface = options['Refine number of elements surface'] refineElementsCountThroughLVWall = options['Refine number of elements through LV wall'] refineElementsCountThroughWall = options['Refine number of elements through wall'] @@ -899,20 +1035,26 @@ def refineMesh(cls, meshrefinement, options): startBaseRvElementIdentifier = startBaseLvElementIdentifier + elementsCountLVFreeWallRegular elementsCountRVHanging = 1 startBaseSeptumElementIdentifier = startBaseRvElementIdentifier + elementsCountAroundRVFreeWall + elementsCountRVHanging + 1 - limitBaseElementIdentifier = startBaseSeptumElementIdentifier + elementsCountAroundVSeptum + 1 + startBaseRv2ElementIdentifier = startBaseSeptumElementIdentifier + elementsCountAroundVSeptum + 1 + limitBaseElementIdentifier = startBaseRv2ElementIdentifier + 5 #print(startBaseLvElementIdentifier, startBaseRvElementIdentifier, startBaseSeptumElementIdentifier, limitBaseElementIdentifier) while element.isValid(): numberInXi1 = refineElementsCountSurface numberInXi2 = refineElementsCountSurface - numberInXi3 = refineElementsCountThroughLVWall + numberInXi3 = None elementId = element.getIdentifier() if elementId < startBaseRvElementIdentifier: - pass + numberInXi3 = refineElementsCountThroughLVWall elif elementId == startBaseRvElementIdentifier: # collapsed elements on posterior or anterior interventricular sulcus: numberInXi1 = refineElementsCountThroughLVWall + numberInXi3 = refineElementsCountThroughLVWall elif elementId < startBaseSeptumElementIdentifier: numberInXi3 = refineElementsCountThroughWall + elif elementId < startBaseRv2ElementIdentifier: + numberInXi3 = refineElementsCountThroughLVWall + else: + numberInXi3 = refineElementsCountThroughWall meshrefinement.refineElementCubeStandard3d(element, numberInXi1, numberInXi2, numberInXi3) if elementId == (limitBaseElementIdentifier - 1): return # finish on last so can continue in full heart mesh From 4faff61ec0c5dda531fab08e757330196f5c5571 Mon Sep 17 00:00:00 2001 From: Richard Christie Date: Fri, 9 Nov 2018 13:15:47 +1300 Subject: [PATCH 14/25] Remove hanging node elements, close infundibulum --- .../meshtype_3d_heartventriclesbase1.py | 69 +++++++++++-------- 1 file changed, 42 insertions(+), 27 deletions(-) diff --git a/scaffoldmaker/meshtypes/meshtype_3d_heartventriclesbase1.py b/scaffoldmaker/meshtypes/meshtype_3d_heartventriclesbase1.py index 556e79b5..36e4b2f5 100644 --- a/scaffoldmaker/meshtypes/meshtype_3d_heartventriclesbase1.py +++ b/scaffoldmaker/meshtypes/meshtype_3d_heartventriclesbase1.py @@ -653,7 +653,7 @@ def generateBaseMesh(cls, region, options): # RV base elements row 1, starting at crux / posterior interventricular sulcus elementsCountRVHanging = 1 - for e in range(-1, elementsCountAroundRVFreeWall + elementsCountRVHanging): + for e in range(-1, elementsCountAroundRVFreeWall + elementsCountRVHanging + 1): eft1 = eft nids = None scalefactors = None @@ -661,8 +661,8 @@ def generateBaseMesh(cls, region, options): noa = elementsCountAroundAtrialSeptum - 1 + e niv = e - if e > (elementsCountAroundRVFreeWall + elementsCountRVHanging - 3): - niv -= 1 + #if e > (elementsCountAroundRVFreeWall + elementsCountRVHanging - 2): + # niv -= 1 nivp = niv + 1 nov = elementsCountAroundLVFreeWall + niv novp = (nov + 1)%elementsCountAroundLV @@ -711,40 +711,63 @@ def generateBaseMesh(cls, region, options): meshGroups += [ conusArteriosusMeshGroup ] elif e == (elementsCountAroundRVFreeWall + elementsCountRVHanging - 3): # supraventricular crest outer 3, outer infundibulum 2 - # 1st of pair of elements with hanging nodes at xi1=0.5 on xi2 == 0 plane nids = [ rvInnerNodeId[niv], rvInnerNodeId[nivp], rvOutletNodeId[0][2], rvOutletNodeId[0][3], vOuterNodeId[nov], vOuterNodeId[novp], rvOutletNodeId[1][2], rvOutletNodeId[1][3] ] eft1 = tricubichermite.createEftNoCrossDerivatives() - setEftScaleFactorIds(eft1, [1, 102, 104, 108, 304], []) - scalefactors = scalefactors5hanging - tricubichermite.setEftMidsideXi1HangingNode(eft1, 2, 1, 1, 2, [1, 2, 3, 4, 5]) - tricubichermite.setEftMidsideXi1HangingNode(eft1, 6, 5, 5, 6, [1, 2, 3, 4, 5]) + setEftScaleFactorIds(eft1, [1], []) + scalefactors = [ 1.0 ] tricubichermite.setEftLinearDerivative(eft1, [ 3, 7 ], Node.VALUE_LABEL_D_DS3, 3, 7, 1) tricubichermite.setEftLinearDerivative(eft1, [ 4, 8 ], Node.VALUE_LABEL_D_DS3, 4, 8, 1) meshGroups += [ conusArteriosusMeshGroup ] elif e == (elementsCountAroundRVFreeWall + elementsCountRVHanging - 2): # outer infundibulum 3 - # 2nd of pair of elements with hanging nodes at xi1=0.5 on xi2 == 0 plane nids = [ rvInnerNodeId[niv], rvInnerNodeId[nivp], rvOutletNodeId[0][3], rvOutletNodeId[0][4], vOuterNodeId[nov], vOuterNodeId[novp], rvOutletNodeId[1][3], rvOutletNodeId[1][4] ] eft1 = tricubichermite.createEftNoCrossDerivatives() - setEftScaleFactorIds(eft1, [1, 102, 104, 108, 304], []) - scalefactors = scalefactors5hanging - tricubichermite.setEftMidsideXi1HangingNode(eft1, 1, 2, 1, 2, [1, 2, 3, 4, 5]) - tricubichermite.setEftMidsideXi1HangingNode(eft1, 5, 6, 5, 6, [1, 2, 3, 4, 5]) + setEftScaleFactorIds(eft1, [1], []) + scalefactors = [ 1.0 ] tricubichermite.setEftLinearDerivative(eft1, [ 3, 7 ], Node.VALUE_LABEL_D_DS3, 3, 7, 1) tricubichermite.setEftLinearDerivative(eft1, [ 4, 8 ], Node.VALUE_LABEL_D_DS3, 4, 8, 1) + remapEftNodeValueLabel(eft1, [ 2, 6 ], Node.VALUE_LABEL_D_DS2, [ ( Node.VALUE_LABEL_D_DS1, [1] ), ( Node.VALUE_LABEL_D_DS2, []) ]) + remapEftNodeValueLabel(eft1, [ 6 ], Node.VALUE_LABEL_D_DS3, [ ( Node.VALUE_LABEL_D_DS1, [] ), ( Node.VALUE_LABEL_D_DS3, []) ]) meshGroups += [ conusArteriosusMeshGroup ] elif e == (elementsCountAroundRVFreeWall + elementsCountRVHanging - 1): - # outer infundibulum 4 - nids = [ rvInnerNodeId[niv], rvInnerNodeId[nivp], rvOutletNodeId[0][4], rvOutletNodeId[0][5], - vOuterNodeId[nov], vOuterNodeId[novp], rvOutletNodeId[1][4], rvOutletNodeId[1][5] ] + # outer infundibulum 4, above septum end + # 7 node collapsed element above tetrahedral septum end + nids = [ rvInnerNodeId[niv], rvOutletNodeId[0][4], rvOutletNodeId[0][5], + vOuterNodeId[0], avsNodeId, rvOutletNodeId[1][4], rvOutletNodeId[1][5] ] eft1 = tricubichermite.createEftNoCrossDerivatives() setEftScaleFactorIds(eft1, [1], []) scalefactors = [ 1.0 ] + remapEftNodeValueLabel(eft1, [ 1, 2 ], Node.VALUE_LABEL_D_DS1, []) + remapEftNodeValueLabel(eft1, [ 1, 5 ], Node.VALUE_LABEL_D_DS2, [ ( Node.VALUE_LABEL_D_DS1, [1] ), ( Node.VALUE_LABEL_D_DS2, [] ) ]) + remapEftNodeValueLabel(eft1, [ 2 ], Node.VALUE_LABEL_D_DS3, [ ( Node.VALUE_LABEL_D_DS1, [] ), ( Node.VALUE_LABEL_D_DS2, [] ), ( Node.VALUE_LABEL_D_DS3, [] ) ]) tricubichermite.setEftLinearDerivative(eft1, [ 3, 7 ], Node.VALUE_LABEL_D_DS3, 3, 7, 1) tricubichermite.setEftLinearDerivative(eft1, [ 4, 8 ], Node.VALUE_LABEL_D_DS3, 4, 8, 1) - remapEftNodeValueLabel(eft1, [ 6 ], Node.VALUE_LABEL_D_DS3, [ ( Node.VALUE_LABEL_D_DS1, [] ), ( Node.VALUE_LABEL_D_DS3, []) ]) + remapEftNodeValueLabel(eft1, [ 5 ], Node.VALUE_LABEL_D_DS1, [ ( Node.VALUE_LABEL_D_DS2, [] ) ]) + remapEftNodeValueLabel(eft1, [ 5 ], Node.VALUE_LABEL_D_DS3, [ ( Node.VALUE_LABEL_D_DS1, [] ), ( Node.VALUE_LABEL_D_DS3, []) ]) + remapEftNodeValueLabel(eft1, [ 6 ], Node.VALUE_LABEL_D_DS1, [ ( Node.VALUE_LABEL_D_DS1, [1]) ]) + remapEftNodeValueLabel(eft1, [ 6 ], Node.VALUE_LABEL_D_DS2, [ ( Node.VALUE_LABEL_D2_DS1DS2, []) ]) # swap begin + remapEftNodeValueLabel(eft1, [ 6 ], Node.VALUE_LABEL_D_DS3, [ ( Node.VALUE_LABEL_D_DS2, []) ]) + remapEftNodeValueLabel(eft1, [ 6 ], Node.VALUE_LABEL_D2_DS1DS2, [ ( Node.VALUE_LABEL_D_DS3, []) ]) # swap end + ln_map = [ 1, 1, 2, 3, 4, 5, 6, 7 ] + remapEftLocalNodes(eft1, 7, ln_map) + meshGroups += [ conusArteriosusMeshGroup ] + elif e == (elementsCountAroundRVFreeWall + elementsCountRVHanging): + # outer infundibulum 5, above septum + nids = [ rvInnerNodeId[niv - 1], rvInnerNodeId[niv], rvOutletNodeId[0][5], rvOutletNodeId[0][0], + avsNodeId, lvOutletNodeId[1][3], rvOutletNodeId[1][5], rvOutletNodeId[1][0] ] + eft1 = tricubichermite.createEftNoCrossDerivatives() + setEftScaleFactorIds(eft1, [1], []) + scalefactors = [ 1.0 ] + remapEftNodeValueLabel(eft1, [ 1 ], Node.VALUE_LABEL_D_DS3, [ ( Node.VALUE_LABEL_D_DS1, [] ), ( Node.VALUE_LABEL_D_DS2, [] ), ( Node.VALUE_LABEL_D_DS3, [] ) ]) + remapEftNodeValueLabel(eft1, [ 2 ], Node.VALUE_LABEL_D_DS3, [ ( Node.VALUE_LABEL_D_DS2, [] ), ( Node.VALUE_LABEL_D_DS3, [] ) ]) + tricubichermite.setEftLinearDerivative(eft1, [ 3, 7 ], Node.VALUE_LABEL_D_DS3, 3, 7, 1) + tricubichermite.setEftLinearDerivative(eft1, [ 4, 8 ], Node.VALUE_LABEL_D_DS3, 4, 8, 1) + remapEftNodeValueLabel(eft1, [ 5, 6 ], Node.VALUE_LABEL_D_DS1, [ ( Node.VALUE_LABEL_D_DS1, [1]) ]) + remapEftNodeValueLabel(eft1, [ 5, 6 ], Node.VALUE_LABEL_D_DS2, [ ( Node.VALUE_LABEL_D2_DS1DS2, []) ]) # swap begin + remapEftNodeValueLabel(eft1, [ 5, 6 ], Node.VALUE_LABEL_D_DS3, [ ( Node.VALUE_LABEL_D_DS2, []) ]) + remapEftNodeValueLabel(eft1, [ 5, 6 ], Node.VALUE_LABEL_D2_DS1DS2, [ ( Node.VALUE_LABEL_D_DS3, []) ]) # swap end meshGroups += [ conusArteriosusMeshGroup ] else: continue @@ -867,20 +890,12 @@ def generateBaseMesh(cls, region, options): meshGroup.addElement(element) # RV base elements remainder - elementsCountRVHanging = 1 for e in range(5): eft1 = eft nids = None scalefactors = None meshGroups = [ rvMeshGroup ] - noa = elementsCountAroundAtrialSeptum - 1 + e - niv = e - if e > (elementsCountAroundRVFreeWall + elementsCountRVHanging - 3): - niv -= 1 - nivp = niv + 1 - nov = elementsCountAroundLVFreeWall + niv - novp = (nov + 1)%elementsCountAroundLV if e == 0: # 6 node collapsed vs-ra shim element rvin1 = -elementsCountAroundAtrialSeptum @@ -995,7 +1010,7 @@ def generateBaseMesh(cls, region, options): result3 = element.setScaleFactors(eft1, scalefactors) else: result3 = 7 - print('create element rv base r2', elementIdentifier, result, result2, result3, nids) + #print('create element rv base r2', elementIdentifier, result, result2, result3, nids) elementIdentifier += 1 for meshGroup in meshGroups: @@ -1034,7 +1049,7 @@ def refineMesh(cls, meshrefinement, options): startBaseLvElementIdentifier = element.getIdentifier() startBaseRvElementIdentifier = startBaseLvElementIdentifier + elementsCountLVFreeWallRegular elementsCountRVHanging = 1 - startBaseSeptumElementIdentifier = startBaseRvElementIdentifier + elementsCountAroundRVFreeWall + elementsCountRVHanging + 1 + startBaseSeptumElementIdentifier = startBaseRvElementIdentifier + elementsCountAroundRVFreeWall + elementsCountRVHanging + 2 startBaseRv2ElementIdentifier = startBaseSeptumElementIdentifier + elementsCountAroundVSeptum + 1 limitBaseElementIdentifier = startBaseRv2ElementIdentifier + 5 #print(startBaseLvElementIdentifier, startBaseRvElementIdentifier, startBaseSeptumElementIdentifier, limitBaseElementIdentifier) From 6f7635add3d36ed2b94409dc21ff835ebf953561 Mon Sep 17 00:00:00 2001 From: Richard Christie Date: Fri, 9 Nov 2018 14:14:38 +1300 Subject: [PATCH 15/25] Fix/remove derivative 3 between lv, rv outlets --- .../meshtype_3d_heartventriclesbase1.py | 18 +++++------------- 1 file changed, 5 insertions(+), 13 deletions(-) diff --git a/scaffoldmaker/meshtypes/meshtype_3d_heartventriclesbase1.py b/scaffoldmaker/meshtypes/meshtype_3d_heartventriclesbase1.py index 36e4b2f5..e58d6c5b 100644 --- a/scaffoldmaker/meshtypes/meshtype_3d_heartventriclesbase1.py +++ b/scaffoldmaker/meshtypes/meshtype_3d_heartventriclesbase1.py @@ -310,14 +310,10 @@ def generateBaseMesh(cls, region, options): rvOutletOuterx, rvOutletOuterd1 = createCirclePoints(rvOutletCentre, vector.setMagnitude(axis1, rvOutletOuterRadius), vector.setMagnitude(axis2, rvOutletOuterRadius), elementsCountAroundOutlet) rvOutletd2 = [ vOutletElementLength*axis3[c] for c in range(3) ] - rvOutletOuterd3 = [ None ]*elementsCountAroundOutlet - # fix derivative 3 between lv, rv outlets - lx = lvOutletOuterx[elementsCountAroundOutlet//2] - rx = rvOutletOuterx[0] - d3 = [ (rx[c] - lx[c]) for c in range(3) ] - lvOutletOuterd3[elementsCountAroundOutlet//2] = d3 - rvOutletOuterd3[0] = [ -d for d in d3 ] + # fix derivative 3 on lv outlet adjacent to rv outlet + n1 = elementsCountAroundOutlet//2 + lvOutletOuterd3[n1] = interpolateLagrangeHermiteDerivative(lvOutletOuterx[n1], rvOutletOuterx[0], rvOutletd2, 0.0) # create point above anterior ventricular septum end sd2 = lvOutletOuterd1[3] @@ -507,21 +503,17 @@ def generateBaseMesh(cls, region, options): if n3 == 0: rvOutletx = rvOutletInnerx rvOutletd1 = rvOutletInnerd1 - rvOutletd3 = None else: rvOutletx = rvOutletOuterx rvOutletd1 = rvOutletOuterd1 - rvOutletd3 = rvOutletOuterd3 outletNodeId = [] for n1 in range(elementsCountAroundOutlet): - node = nodes.createNode(nodeIdentifier, nodetemplate if (rvOutletd3 and rvOutletd3[n1]) else nodetemplateLinearS3) + node = nodes.createNode(nodeIdentifier, nodetemplateLinearS3) rvOutletNodeId[n3].append(nodeIdentifier) cache.setNode(node) coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_VALUE, 1, rvOutletx[n1]) coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS1, 1, rvOutletd1[n1]) coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS2, 1, rvOutletd2) - if (rvOutletd3 and rvOutletd3[n1]): - coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS3, 1, rvOutletd3[n1]) nodeIdentifier += 1 # Node above above anterior ventricular septum end @@ -997,8 +989,8 @@ def generateBaseMesh(cls, region, options): remapEftNodeValueLabel(eft1, [ 7 ], Node.VALUE_LABEL_D_DS1, [ ( Node.VALUE_LABEL_D_DS3, [] ) ]) remapEftNodeValueLabel(eft1, [ 7 ], Node.VALUE_LABEL_D_DS2, [ ( Node.VALUE_LABEL_D_DS1, [] ) ]) remapEftNodeValueLabel(eft1, [ 7 ], Node.VALUE_LABEL_D2_DS1DS2, [ ( Node.VALUE_LABEL_D_DS2, [] ) ]) - remapEftNodeValueLabel(eft1, [ 8 ], Node.VALUE_LABEL_D_DS1, [ ( Node.VALUE_LABEL_D_DS3, [1] ) ]) remapEftNodeValueLabel(eft1, [ 8 ], Node.VALUE_LABEL_D_DS2, [ ( Node.VALUE_LABEL_D_DS1, [1] ) ]) + remapEftNodeValueLabel(eft1, [ 8 ], Node.VALUE_LABEL_D_DS1, [ ( Node.VALUE_LABEL_D_DS2, [] ) ]) meshGroups += [ conusArteriosusMeshGroup ] else: continue From edddc7b0f82c8bdd8f45b0e555b9c0aeb2e71d19 Mon Sep 17 00:00:00 2001 From: Richard Christie Date: Sun, 11 Nov 2018 22:25:53 +1300 Subject: [PATCH 16/25] Complete LV freewall row 1 elements --- .../meshtype_3d_heartventriclesbase1.py | 95 +++++++++++++------ 1 file changed, 65 insertions(+), 30 deletions(-) diff --git a/scaffoldmaker/meshtypes/meshtype_3d_heartventriclesbase1.py b/scaffoldmaker/meshtypes/meshtype_3d_heartventriclesbase1.py index e58d6c5b..63f471c3 100644 --- a/scaffoldmaker/meshtypes/meshtype_3d_heartventriclesbase1.py +++ b/scaffoldmaker/meshtypes/meshtype_3d_heartventriclesbase1.py @@ -50,20 +50,20 @@ def getDefaultOptions(): options['Atrial septum thickness'] = 0.06 options['Atrial base wall thickness'] = 0.05 options['Atrial base slope degrees'] = 30.0 - options['Base height'] = 0.12 + options['Base height'] = 0.15 options['Base thickness'] = 0.06 options['Fibrous ring thickness'] = 0.01 options['LV outlet front incline degrees'] = 15.0 options['LV outlet inner diameter'] = 0.3 options['LV outlet wall thickness'] = 0.025 - options['RV outlet left incline degrees'] = 25.0 + options['RV outlet left incline degrees'] = 30.0 options['RV outlet inner diameter'] = 0.27 options['RV outlet wall thickness'] = 0.025 options['Ventricles outlet element length'] = 0.1 options['Ventricles outlet spacing y'] = 0.02 - options['Ventricles outlet spacing z'] = 0.14 + options['Ventricles outlet spacing z'] = 0.12 options['Ventricles rotation degrees'] = 16.0 - options['Ventricles translation x'] = -0.22 + options['Ventricles translation x'] = -0.19 options['Ventricles translation y'] = -0.2 return options @@ -267,15 +267,18 @@ def generateBaseMesh(cls, region, options): for nodeId in [ lvInnerNodeId, rvInnerNodeId, vOuterNodeId ]: vx = [] vd2 = [] + vd3 = [] for n1 in range(len(nodeId)): node = nodes.findNodeByIdentifier(nodeId[n1]) cache.setNode(node) result, x = coordinates.getNodeParameters(cache, -1, Node.VALUE_LABEL_VALUE, 1, 3) result, d2 = coordinates.getNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS2, 1, 3) + result, d3 = coordinates.getNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS3, 1, 3) vx .append(x) vd2.append(d2) + vd3.append(d3) if nodeId is lvInnerNodeId: - lvInnerx, lvInnerd2 = vx, vd2 + lvInnerx, lvInnerd2, lvInnerd3 = vx, vd2, vd3 elif nodeId is rvInnerNodeId: rvInnerx, rvInnerd2 = vx, vd2 else: @@ -315,24 +318,6 @@ def generateBaseMesh(cls, region, options): n1 = elementsCountAroundOutlet//2 lvOutletOuterd3[n1] = interpolateLagrangeHermiteDerivative(lvOutletOuterx[n1], rvOutletOuterx[0], rvOutletd2, 0.0) - # create point above anterior ventricular septum end - sd2 = lvOutletOuterd1[3] - fd2 = [ -d for d in vOuterd2[0] ] - px, pd1 = sampleCubicHermiteCurves([ lvOutletOuterx[3], vOuterx[0] ], [ [ 2.0*d for d in sd2 ], fd2 ], 2, arcLengthDerivatives=True)[0:2] - pd1 = smoothCubicHermiteDerivativesLine(px, [ sd2, pd1[1], fd2 ], fixStartDerivative=True, fixEndDerivative=True) - pd3 = smoothCubicHermiteDerivativesLine([ lvOutletOuterx[4], px[1], rvOutletOuterx[-1] ], [ lvOutletOuterd3[4], zero, rvOutletd2 ], - fixEndDerivative=True, magnitudeScalingMode = DerivativeScalingMode.HARMONIC_MEAN) - n1 = elementsCountAroundRVFreeWall - d3 = vector.crossproduct3(pd3[1], pd1[1]) - sx = [ 0.5*(lvInnerx[0][c] + rvInnerx[n1][c]) for c in range(3) ] - sd2 = [ 0.5*(lvInnerd2[0][c] + rvInnerd2[n1][c]) for c in range(3) ] - pd2 = smoothCubicHermiteDerivativesLine([ sx, px[1] ], [ sd2, d3 ], fixStartDerivative=True, fixEndDirection=True) - avsx = px[1] - avsd1 = pd1[1] - avsd2 = pd2[1] - avsd3 = pd3[1] - lvOutletOuterd3[4] = pd3[0] - # Left av fibrous ring points aBaseSlopeHeight = aBaseWallThickness*math.sin(aBaseSlopeRadians) aBaseSlopeLength = aBaseWallThickness*math.cos(aBaseSlopeRadians) @@ -444,6 +429,24 @@ def generateBaseMesh(cls, region, options): lvOutletOuterd3[1] = [ -d for d in ravOuterd3[0][-2] ] lvOutletOuterd3[-1] = [ -d for d in lavOuterd3[0][1] ] + # create point above anterior ventricular septum end + xi = 0.6 + fd2 = [ (lvOutletOuterx[4][c] - vOuterx[1][c]) for c in range(3) ] + mag = 1.0*vector.magnitude(fd2) + fd2 = vector.setMagnitude([ fd2[0], fd2[1], 0.0 ], mag) + x = interpolateCubicHermite(vOuterx[0], vOuterd2[0], lvOutletOuterx[4], fd2, xi) + d2 = interpolateCubicHermiteDerivative(vOuterx[0], vOuterd2[0], lvOutletOuterx[4], fd2, xi) + pd2 = smoothCubicHermiteDerivativesLine([ vOuterx[0], x, lvOutletOuterx[4] ], [ vOuterd2[0], d2, lvOutletd2 ], fixAllDirections=True, fixStartDerivative=True, fixEndDerivative=True) + fd1 = [ (lavOuterd1[0][2][c] + lavOuterd2[0][2][c]) for c in range(3) ] + pd1 = smoothCubicHermiteDerivativesLine([ rvOutletOuterx[-1], x, lavOuterx[0][2] ], [ [ -d for d in rvOutletd2 ], zero, fd1 ], fixStartDerivative=True, fixEndDerivative=True, magnitudeScalingMode=DerivativeScalingMode.HARMONIC_MEAN) + avsx = x + avsd1 = pd1[1] + avsd2 = pd2[1] + sf = math.cos(math.pi*0.25) + avsd3 = interpolateHermiteLagrangeDerivative(lvInnerx[0], lvInnerd2[0], avsx, 1.0) + # GRC temp: + avsd1, avsd2, avsd3 = [ -d for d in avsd2 ], avsd3, [ -d for d in avsd1 ] + # create points on bottom and top of RV supraventricular crest ns = elementsCountAroundRVFreeWall//2 + 1 nf = elementsCountAroundRVFreeWall + 2 @@ -618,14 +621,47 @@ def generateBaseMesh(cls, region, options): scalefactors = None meshGroups = [ lvMeshGroup ] - if e < (elementsCountAroundLVFreeWall - elementsCountLVFreeWallRegular): - continue + if e == -1: + # 4 node collapsed tetrahedral element on anterior interventricular sulcus + nids = [ rvInnerNodeId[elementsCountAroundRVFreeWall], lvInnerNodeId[0], vOuterNodeId[0], avsNodeId ] + eft1 = tricubichermite.createEftNoCrossDerivatives() + setEftScaleFactorIds(eft1, [1], []) + scalefactors = [ -1.0 ] + remapEftNodeValueLabel(eft1, [ 1, 2, 3, 4 ], Node.VALUE_LABEL_D_DS2, []) + remapEftNodeValueLabel(eft1, [ 5, 6, 7, 8 ], Node.VALUE_LABEL_D_DS1, []) + remapEftNodeValueLabel(eft1, [ 1, 3 ], Node.VALUE_LABEL_D_DS1, [ ( Node.VALUE_LABEL_D_DS1, [] ), ( Node.VALUE_LABEL_D_DS3, []) ]) + remapEftNodeValueLabel(eft1, [ 2, 4 ], Node.VALUE_LABEL_D_DS1, [ ( Node.VALUE_LABEL_D_DS1, [] ), ( Node.VALUE_LABEL_D_DS3, [1]) ]) + remapEftNodeValueLabel(eft1, [ 3 ], Node.VALUE_LABEL_D_DS3, [ ( Node.VALUE_LABEL_D_DS1, [] ), ( Node.VALUE_LABEL_D_DS2, [] ), ( Node.VALUE_LABEL_D_DS3, []) ]) + remapEftNodeValueLabel(eft1, [ 4 ], Node.VALUE_LABEL_D_DS3, [ ( Node.VALUE_LABEL_D_DS2, [] ), ( Node.VALUE_LABEL_D_DS3, []) ]) + remapEftNodeValueLabel(eft1, [ 5 ], Node.VALUE_LABEL_D_DS3, [ ( Node.VALUE_LABEL_D_DS1, [] ), ( Node.VALUE_LABEL_D_DS3, []) ]) + remapEftNodeValueLabel(eft1, [ 7, 8 ], Node.VALUE_LABEL_D_DS2, [ ( Node.VALUE_LABEL_D_DS1, [1]) ]) + remapEftNodeValueLabel(eft1, [ 7 ], Node.VALUE_LABEL_D_DS3, [ ( Node.VALUE_LABEL_D_DS2, []) ]) + remapEftNodeValueLabel(eft1, [ 8 ], Node.VALUE_LABEL_D_DS3, [ ( Node.VALUE_LABEL_D_DS2, []), ( Node.VALUE_LABEL_D_DS3, []) ]) + ln_map = [ 1, 2, 1, 2, 3, 3, 4, 4 ] + remapEftLocalNodes(eft1, 4, ln_map) + meshGroups += [ rvMeshGroup ] else: # regular elements between LV free wall and left atrium noa = elementsCountAroundAtrialFreeWall - elementsCountAroundLVFreeWall + e nov = e nids = [ lvInnerNodeId[nov], lvInnerNodeId[nov + 1], lavInnerNodeId[0][noa], lavInnerNodeId[0][noa + 1], vOuterNodeId[nov], vOuterNodeId[nov + 1], lavOuterNodeId[0][noa], lavOuterNodeId[0][noa + 1] ] - if e == (elementsCountAroundLVFreeWall - 1): + if e == 0: + # 7 node collapsed hex-wedge "hedge" element + nids.pop(2) + nids[5] = avsNodeId + eft1 = tricubichermite.createEftNoCrossDerivatives() + setEftScaleFactorIds(eft1, [1], []) + scalefactors = [ -1.0 ] + remapEftNodeValueLabel(eft1, [ 1, 3 ], Node.VALUE_LABEL_D_DS2, []) + remapEftNodeValueLabel(eft1, [ 3 ], Node.VALUE_LABEL_D_DS1, [ ( Node.VALUE_LABEL_D_DS1, [] ), ( Node.VALUE_LABEL_D_DS2, []) ]) + remapEftNodeValueLabel(eft1, [ 3 ], Node.VALUE_LABEL_D_DS3, [ ( Node.VALUE_LABEL_D_DS2, [] ), ( Node.VALUE_LABEL_D_DS3, []) ]) + remapEftNodeValueLabel(eft1, [ 7 ], Node.VALUE_LABEL_D_DS1, [ ( Node.VALUE_LABEL_D_DS3, [1]) ]) + remapEftNodeValueLabel(eft1, [ 7 ], Node.VALUE_LABEL_D_DS2, [ ( Node.VALUE_LABEL_D_DS1, [1] ) ]) + remapEftNodeValueLabel(eft1, [ 7 ], Node.VALUE_LABEL_D_DS3, [ ( Node.VALUE_LABEL_D_DS2, [] ), ( Node.VALUE_LABEL_D_DS3, []) ]) + remapEftNodeValueLabel(eft1, [ 4, 8 ], Node.VALUE_LABEL_D_DS1, [ ( Node.VALUE_LABEL_D_DS1, [] ), ( Node.VALUE_LABEL_D_DS2, []) ]) + ln_map = [ 1, 2, 1, 3, 4, 5, 6, 7 ] + remapEftLocalNodes(eft1, 7, ln_map) + elif e == (elementsCountAroundLVFreeWall - 1): # general linear map d3 adjacent to collapsed crux, transition to atria eft1 = tricubichermite.createEftNoCrossDerivatives() remapEftNodeValueLabel(eft1, [ 8 ], Node.VALUE_LABEL_D_DS3, [ ( Node.VALUE_LABEL_D_DS1, [] ), ( Node.VALUE_LABEL_D_DS3, []) ]) @@ -662,7 +698,6 @@ def generateBaseMesh(cls, region, options): # crux / posterior interventricular sulcus: collapsed to 6 element wedge nids = [ lvInnerNodeId[elementsCountAroundLVFreeWall], rvInnerNodeId[nivp], lavInnerNodeId[0][elementsCountAroundAtrialFreeWall], ravInnerNodeId[0][noa + 1], vOuterNodeId[novp], ravOuterNodeId[0][noa + 1] ] - meshGroups += [ lvMeshGroup ] eft1 = tricubichermite.createEftNoCrossDerivatives() setEftScaleFactorIds(eft1, [1], []) scalefactors = [ -1.0 ] @@ -674,6 +709,7 @@ def generateBaseMesh(cls, region, options): remapEftNodeValueLabel(eft1, [ 7 ], Node.VALUE_LABEL_D_DS3, [ ( Node.VALUE_LABEL_D_DS1, [] ), ( Node.VALUE_LABEL_D_DS3, []) ]) ln_map = [ 1, 2, 3, 4, 5, 5, 6, 6 ] remapEftLocalNodes(eft1, 6, ln_map) + meshGroups += [ lvMeshGroup ] elif e < elementsCountRVFreeWallRegular: nids = [ rvInnerNodeId[niv], rvInnerNodeId[nivp], ravInnerNodeId[0][noa], ravInnerNodeId[0][noa + 1], vOuterNodeId[nov], vOuterNodeId[novp], ravOuterNodeId[0][noa], ravOuterNodeId[0][noa + 1] ] @@ -856,7 +892,7 @@ def generateBaseMesh(cls, region, options): nids[3] = avsNodeId scaleEftNodeValueLabels(eft1, [ 5 ], [ Node.VALUE_LABEL_D_DS3 ], [ 1 ]) remapEftNodeValueLabel(eft1, [ 1, 5 ], Node.VALUE_LABEL_D_DS2, [ ( Node.VALUE_LABEL_D_DS2, [] ), ( Node.VALUE_LABEL_D_DS3, [] ) ]) - remapEftNodeValueLabel(eft1, [ 2 ], Node.VALUE_LABEL_D_DS2, [ ( Node.VALUE_LABEL_D_DS1, [1] ), ( Node.VALUE_LABEL_D_DS2, [] ), ( Node.VALUE_LABEL_D_DS3, [] ) ]) + remapEftNodeValueLabel(eft1, [ 2 ], Node.VALUE_LABEL_D_DS2, [ ( Node.VALUE_LABEL_D_DS2, [] ), ( Node.VALUE_LABEL_D_DS3, [] ) ]) remapEftNodeValueLabel(eft1, [ 2 ], Node.VALUE_LABEL_D_DS3, [ ( Node.VALUE_LABEL_D_DS1, [1] ), ( Node.VALUE_LABEL_D_DS3, [] ) ]) # general linear map d3 adjacent to collapsed anterior interventricular sulcus remapEftNodeValueLabel(eft1, [ 4 ], Node.VALUE_LABEL_D_DS2, [ ( Node.VALUE_LABEL_D_DS2, [] ), ( Node.VALUE_LABEL_D_DS3, [] ) ]) @@ -1031,7 +1067,6 @@ def refineMesh(cls, meshrefinement, options): elementsCountAroundAtrialFreeWall = options['Number of elements around atrial free wall'] elementsCountAroundAtrialSeptum = options['Number of elements around atrial septum'] elementsCountAroundAtria = elementsCountAroundAtrialFreeWall + elementsCountAroundAtrialSeptum - elementsCountLVFreeWallRegular = elementsCountAroundLVFreeWall - 1 elementsCountRVFreeWallRegular = 3 refineElementsCountSurface = options['Refine number of elements surface'] refineElementsCountThroughLVWall = options['Refine number of elements through LV wall'] @@ -1039,7 +1074,7 @@ def refineMesh(cls, meshrefinement, options): MeshType_3d_heartventricles1.refineMesh(meshrefinement, options) element = meshrefinement._sourceElementiterator.next() startBaseLvElementIdentifier = element.getIdentifier() - startBaseRvElementIdentifier = startBaseLvElementIdentifier + elementsCountLVFreeWallRegular + startBaseRvElementIdentifier = startBaseLvElementIdentifier + elementsCountAroundLVFreeWall + 1 elementsCountRVHanging = 1 startBaseSeptumElementIdentifier = startBaseRvElementIdentifier + elementsCountAroundRVFreeWall + elementsCountRVHanging + 2 startBaseRv2ElementIdentifier = startBaseSeptumElementIdentifier + elementsCountAroundVSeptum + 1 From a06419296951fd196b12fb4fb4a204b3483c15eb Mon Sep 17 00:00:00 2001 From: Richard Christie Date: Sun, 11 Nov 2018 23:42:20 +1300 Subject: [PATCH 17/25] Reorder and remap derivatives on anterior sulcus point Remove d3 from LV outlet outer node 4. --- .../meshtype_3d_heartventriclesbase1.py | 49 +++++++++---------- 1 file changed, 24 insertions(+), 25 deletions(-) diff --git a/scaffoldmaker/meshtypes/meshtype_3d_heartventriclesbase1.py b/scaffoldmaker/meshtypes/meshtype_3d_heartventriclesbase1.py index 63f471c3..87afea28 100644 --- a/scaffoldmaker/meshtypes/meshtype_3d_heartventriclesbase1.py +++ b/scaffoldmaker/meshtypes/meshtype_3d_heartventriclesbase1.py @@ -297,7 +297,7 @@ def generateBaseMesh(cls, region, options): vector.setMagnitude(axis1, lvOutletOuterRadius), vector.setMagnitude(axis2, lvOutletOuterRadius), elementsCountAroundOutlet) lvOutletd2 = [ 0.0, vOutletElementLength*sinLvOutletFrontInclineRadians, vOutletElementLength*cosLvOutletFrontInclineRadians ] zero = [ 0.0, 0.0, 0.0 ] - lvOutletOuterd3 = [ vector.setMagnitude([ (lvOutletOuterx[n1][c] - lvOutletCentre[c]) for c in range(3) ], vSeptumThickness) for n1 in range(elementsCountAroundOutlet) ] + lvOutletOuterd3 = [ None ]*elementsCountAroundOutlet # RV outlet points cosRvOutletLeftInclineRadians = math.cos(rvOutletLeftInclineRadians) @@ -444,8 +444,6 @@ def generateBaseMesh(cls, region, options): avsd2 = pd2[1] sf = math.cos(math.pi*0.25) avsd3 = interpolateHermiteLagrangeDerivative(lvInnerx[0], lvInnerd2[0], avsx, 1.0) - # GRC temp: - avsd1, avsd2, avsd3 = [ -d for d in avsd2 ], avsd3, [ -d for d in avsd1 ] # create points on bottom and top of RV supraventricular crest ns = elementsCountAroundRVFreeWall//2 + 1 @@ -469,7 +467,7 @@ def generateBaseMesh(cls, region, options): nf = 2 pd1 = smoothCubicHermiteDerivativesLine([ ravOuterx[0][ravsvcn1], mx, rvOutletOuterx[1] ], [ [ -d for d in ravOuterd2[0][ravsvcn1] ], zero, rvOutletd2 ], fixStartDirection=True, fixEndDerivative = True) - pd2 = smoothCubicHermiteDerivativesLine([ mx, lvOutletOuterx[nf] ], [ md2, [ -d for d in lvOutletOuterd3[nf] ] ], fixStartDirection=True) + pd2 = smoothCubicHermiteDerivativesLine([ mx, lvOutletOuterx[nf] ], [ md2, svcid2 ], fixStartDirection=True) svcox = [ mx[0], mx[1], mx[2] ] # list components to avoid reference bug svcod1 = pd1[1] svcod2 = pd2[0] @@ -490,13 +488,13 @@ def generateBaseMesh(cls, region, options): lvOutletd3 = lvOutletOuterd3 outletNodeId = [] for n1 in range(elementsCountAroundOutlet): - node = nodes.createNode(nodeIdentifier, nodetemplate if lvOutletd3 else nodetemplateLinearS3) + node = nodes.createNode(nodeIdentifier, nodetemplate if (lvOutletd3 and lvOutletd3[n1]) else nodetemplateLinearS3) lvOutletNodeId[n3].append(nodeIdentifier) cache.setNode(node) coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_VALUE, 1, lvOutletx[n1]) coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS1, 1, lvOutletd1[n1]) coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS2, 1, lvOutletd2) - if lvOutletd3: + if (lvOutletd3 and lvOutletd3[n1]): coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS3, 1, lvOutletd3[n1]) nodeIdentifier += 1 @@ -634,9 +632,7 @@ def generateBaseMesh(cls, region, options): remapEftNodeValueLabel(eft1, [ 3 ], Node.VALUE_LABEL_D_DS3, [ ( Node.VALUE_LABEL_D_DS1, [] ), ( Node.VALUE_LABEL_D_DS2, [] ), ( Node.VALUE_LABEL_D_DS3, []) ]) remapEftNodeValueLabel(eft1, [ 4 ], Node.VALUE_LABEL_D_DS3, [ ( Node.VALUE_LABEL_D_DS2, [] ), ( Node.VALUE_LABEL_D_DS3, []) ]) remapEftNodeValueLabel(eft1, [ 5 ], Node.VALUE_LABEL_D_DS3, [ ( Node.VALUE_LABEL_D_DS1, [] ), ( Node.VALUE_LABEL_D_DS3, []) ]) - remapEftNodeValueLabel(eft1, [ 7, 8 ], Node.VALUE_LABEL_D_DS2, [ ( Node.VALUE_LABEL_D_DS1, [1]) ]) - remapEftNodeValueLabel(eft1, [ 7 ], Node.VALUE_LABEL_D_DS3, [ ( Node.VALUE_LABEL_D_DS2, []) ]) - remapEftNodeValueLabel(eft1, [ 8 ], Node.VALUE_LABEL_D_DS3, [ ( Node.VALUE_LABEL_D_DS2, []), ( Node.VALUE_LABEL_D_DS3, []) ]) + remapEftNodeValueLabel(eft1, [ 7 ], Node.VALUE_LABEL_D_DS3, [ ( Node.VALUE_LABEL_D_DS1, []), ( Node.VALUE_LABEL_D_DS3, []) ]) ln_map = [ 1, 2, 1, 2, 3, 3, 4, 4 ] remapEftLocalNodes(eft1, 4, ln_map) meshGroups += [ rvMeshGroup ] @@ -655,9 +651,6 @@ def generateBaseMesh(cls, region, options): remapEftNodeValueLabel(eft1, [ 1, 3 ], Node.VALUE_LABEL_D_DS2, []) remapEftNodeValueLabel(eft1, [ 3 ], Node.VALUE_LABEL_D_DS1, [ ( Node.VALUE_LABEL_D_DS1, [] ), ( Node.VALUE_LABEL_D_DS2, []) ]) remapEftNodeValueLabel(eft1, [ 3 ], Node.VALUE_LABEL_D_DS3, [ ( Node.VALUE_LABEL_D_DS2, [] ), ( Node.VALUE_LABEL_D_DS3, []) ]) - remapEftNodeValueLabel(eft1, [ 7 ], Node.VALUE_LABEL_D_DS1, [ ( Node.VALUE_LABEL_D_DS3, [1]) ]) - remapEftNodeValueLabel(eft1, [ 7 ], Node.VALUE_LABEL_D_DS2, [ ( Node.VALUE_LABEL_D_DS1, [1] ) ]) - remapEftNodeValueLabel(eft1, [ 7 ], Node.VALUE_LABEL_D_DS3, [ ( Node.VALUE_LABEL_D_DS2, [] ), ( Node.VALUE_LABEL_D_DS3, []) ]) remapEftNodeValueLabel(eft1, [ 4, 8 ], Node.VALUE_LABEL_D_DS1, [ ( Node.VALUE_LABEL_D_DS1, [] ), ( Node.VALUE_LABEL_D_DS2, []) ]) ln_map = [ 1, 2, 1, 3, 4, 5, 6, 7 ] remapEftLocalNodes(eft1, 7, ln_map) @@ -774,10 +767,9 @@ def generateBaseMesh(cls, region, options): tricubichermite.setEftLinearDerivative(eft1, [ 4, 8 ], Node.VALUE_LABEL_D_DS3, 4, 8, 1) remapEftNodeValueLabel(eft1, [ 5 ], Node.VALUE_LABEL_D_DS1, [ ( Node.VALUE_LABEL_D_DS2, [] ) ]) remapEftNodeValueLabel(eft1, [ 5 ], Node.VALUE_LABEL_D_DS3, [ ( Node.VALUE_LABEL_D_DS1, [] ), ( Node.VALUE_LABEL_D_DS3, []) ]) - remapEftNodeValueLabel(eft1, [ 6 ], Node.VALUE_LABEL_D_DS1, [ ( Node.VALUE_LABEL_D_DS1, [1]) ]) - remapEftNodeValueLabel(eft1, [ 6 ], Node.VALUE_LABEL_D_DS2, [ ( Node.VALUE_LABEL_D2_DS1DS2, []) ]) # swap begin - remapEftNodeValueLabel(eft1, [ 6 ], Node.VALUE_LABEL_D_DS3, [ ( Node.VALUE_LABEL_D_DS2, []) ]) - remapEftNodeValueLabel(eft1, [ 6 ], Node.VALUE_LABEL_D2_DS1DS2, [ ( Node.VALUE_LABEL_D_DS3, []) ]) # swap end + remapEftNodeValueLabel(eft1, [ 6 ], Node.VALUE_LABEL_D_DS2, [ ( Node.VALUE_LABEL_D_DS1, [1]) ]) + remapEftNodeValueLabel(eft1, [ 6 ], Node.VALUE_LABEL_D_DS1, [ ( Node.VALUE_LABEL_D_DS2, []) ]) + remapEftNodeValueLabel(eft1, [ 6 ], Node.VALUE_LABEL_D_DS3, [ ( Node.VALUE_LABEL_D_DS1, []), ( Node.VALUE_LABEL_D_DS3, []) ]) ln_map = [ 1, 1, 2, 3, 4, 5, 6, 7 ] remapEftLocalNodes(eft1, 7, ln_map) meshGroups += [ conusArteriosusMeshGroup ] @@ -792,10 +784,13 @@ def generateBaseMesh(cls, region, options): remapEftNodeValueLabel(eft1, [ 2 ], Node.VALUE_LABEL_D_DS3, [ ( Node.VALUE_LABEL_D_DS2, [] ), ( Node.VALUE_LABEL_D_DS3, [] ) ]) tricubichermite.setEftLinearDerivative(eft1, [ 3, 7 ], Node.VALUE_LABEL_D_DS3, 3, 7, 1) tricubichermite.setEftLinearDerivative(eft1, [ 4, 8 ], Node.VALUE_LABEL_D_DS3, 4, 8, 1) - remapEftNodeValueLabel(eft1, [ 5, 6 ], Node.VALUE_LABEL_D_DS1, [ ( Node.VALUE_LABEL_D_DS1, [1]) ]) - remapEftNodeValueLabel(eft1, [ 5, 6 ], Node.VALUE_LABEL_D_DS2, [ ( Node.VALUE_LABEL_D2_DS1DS2, []) ]) # swap begin - remapEftNodeValueLabel(eft1, [ 5, 6 ], Node.VALUE_LABEL_D_DS3, [ ( Node.VALUE_LABEL_D_DS2, []) ]) - remapEftNodeValueLabel(eft1, [ 5, 6 ], Node.VALUE_LABEL_D2_DS1DS2, [ ( Node.VALUE_LABEL_D_DS3, []) ]) # swap end + remapEftNodeValueLabel(eft1, [ 5 ], Node.VALUE_LABEL_D_DS1, [ ( Node.VALUE_LABEL_D_DS1, [1]), ( Node.VALUE_LABEL_D_DS2, []) ]) + remapEftNodeValueLabel(eft1, [ 5 ], Node.VALUE_LABEL_D_DS2, [ ( Node.VALUE_LABEL_D_DS1, [1]) ]) + remapEftNodeValueLabel(eft1, [ 5 ], Node.VALUE_LABEL_D_DS3, [ ( Node.VALUE_LABEL_D_DS1, []), ( Node.VALUE_LABEL_D_DS3, []) ]) + remapEftNodeValueLabel(eft1, [ 6 ], Node.VALUE_LABEL_D_DS1, [ ( Node.VALUE_LABEL_D_DS1, [1]) ]) + remapEftNodeValueLabel(eft1, [ 6 ], Node.VALUE_LABEL_D_DS2, [ ( Node.VALUE_LABEL_D2_DS1DS2, []) ]) # swap begin + remapEftNodeValueLabel(eft1, [ 6 ], Node.VALUE_LABEL_D_DS3, [ ( Node.VALUE_LABEL_D_DS2, []) ]) + remapEftNodeValueLabel(eft1, [ 6 ], Node.VALUE_LABEL_D2_DS1DS2, [ ( Node.VALUE_LABEL_D_DS3, []) ]) # swap end meshGroups += [ conusArteriosusMeshGroup ] else: continue @@ -895,9 +890,11 @@ def generateBaseMesh(cls, region, options): remapEftNodeValueLabel(eft1, [ 2 ], Node.VALUE_LABEL_D_DS2, [ ( Node.VALUE_LABEL_D_DS2, [] ), ( Node.VALUE_LABEL_D_DS3, [] ) ]) remapEftNodeValueLabel(eft1, [ 2 ], Node.VALUE_LABEL_D_DS3, [ ( Node.VALUE_LABEL_D_DS1, [1] ), ( Node.VALUE_LABEL_D_DS3, [] ) ]) # general linear map d3 adjacent to collapsed anterior interventricular sulcus - remapEftNodeValueLabel(eft1, [ 4 ], Node.VALUE_LABEL_D_DS2, [ ( Node.VALUE_LABEL_D_DS2, [] ), ( Node.VALUE_LABEL_D_DS3, [] ) ]) + remapEftNodeValueLabel(eft1, [ 4, 8 ], Node.VALUE_LABEL_D_DS1, [ (Node.VALUE_LABEL_D_DS1, [] ), (Node.VALUE_LABEL_D_DS2, [1] ) ]) + remapEftNodeValueLabel(eft1, [ 4 ], Node.VALUE_LABEL_D_DS2, [ (Node.VALUE_LABEL_D_DS3, [] ) ]) remapEftNodeValueLabel(eft1, [ 6 ], Node.VALUE_LABEL_D_DS2, [ ( Node.VALUE_LABEL_D_DS1, [] ), ( Node.VALUE_LABEL_D_DS2, [] ), ( Node.VALUE_LABEL_D_DS3, [] ) ]) remapEftNodeValueLabel(eft1, [ 6 ], Node.VALUE_LABEL_D_DS3, [ ( Node.VALUE_LABEL_D_DS1, [1] ), ( Node.VALUE_LABEL_D_DS3, [1] ) ]) + remapEftNodeValueLabel(eft1, [ 8 ], Node.VALUE_LABEL_D_DS2, [ (Node.VALUE_LABEL_D_DS1, [] ), (Node.VALUE_LABEL_D_DS3, [] ) ]) else: scaleEftNodeValueLabels(eft1, [ 5, 6 ], [ Node.VALUE_LABEL_D_DS3 ], [ 1 ]) remapEftNodeValueLabel(eft1, [ 1, 2, 5, 6 ], Node.VALUE_LABEL_D_DS2, [ ( Node.VALUE_LABEL_D_DS2, [] ), ( Node.VALUE_LABEL_D_DS3, [] ) ]) @@ -1085,12 +1082,14 @@ def refineMesh(cls, meshrefinement, options): numberInXi2 = refineElementsCountSurface numberInXi3 = None elementId = element.getIdentifier() - if elementId < startBaseRvElementIdentifier: + if elementId == startBaseLvElementIdentifier: + # collapsed element on anterior interventricular sulcus: + numberInXi1 = numberInXi3 = refineElementsCountThroughLVWall + elif elementId < startBaseRvElementIdentifier: numberInXi3 = refineElementsCountThroughLVWall elif elementId == startBaseRvElementIdentifier: - # collapsed elements on posterior or anterior interventricular sulcus: - numberInXi1 = refineElementsCountThroughLVWall - numberInXi3 = refineElementsCountThroughLVWall + # collapsed element on posterior interventricular sulcus: + numberInXi1 = numberInXi3 = refineElementsCountThroughLVWall elif elementId < startBaseSeptumElementIdentifier: numberInXi3 = refineElementsCountThroughWall elif elementId < startBaseRv2ElementIdentifier: From 32b50e16b042495d9e984847ef8cd195cc703982 Mon Sep 17 00:00:00 2001 From: Richard Christie Date: Mon, 12 Nov 2018 11:14:47 +1300 Subject: [PATCH 18/25] Add more LV closure elements --- .../meshtype_3d_heartventriclesbase1.py | 116 +++++++++++++++--- 1 file changed, 101 insertions(+), 15 deletions(-) diff --git a/scaffoldmaker/meshtypes/meshtype_3d_heartventriclesbase1.py b/scaffoldmaker/meshtypes/meshtype_3d_heartventriclesbase1.py index 87afea28..c75e2ae3 100644 --- a/scaffoldmaker/meshtypes/meshtype_3d_heartventriclesbase1.py +++ b/scaffoldmaker/meshtypes/meshtype_3d_heartventriclesbase1.py @@ -410,6 +410,8 @@ def generateBaseMesh(cls, region, options): noa = (elementsCountAroundAtrialSeptum - 1 + elementsCountAroundAtria - n1)%elementsCountAroundAtria nov = -n1 ravInnerd2[0][noa] = interpolateHermiteLagrangeDerivative(rvInnerx[nov], rvInnerd2[nov], ravInnerx[0][noa], 1.0) + # special fix for left cfb + lavInnerd2[0][1] = interpolateHermiteLagrangeDerivative(lvOutletInnerx[-1], [ -d for d in lvOutletd2 ], lavInnerx[0][1], 1.0) # special fix for right cfb noa = elementsCountAroundAtria - 2 nov = -elementsCountAroundAtrialSeptum - 1 @@ -736,7 +738,7 @@ def generateBaseMesh(cls, region, options): vOuterNodeId[nov], vOuterNodeId[novp], rvOutletNodeId[1][2], rvOutletNodeId[1][3] ] eft1 = tricubichermite.createEftNoCrossDerivatives() setEftScaleFactorIds(eft1, [1], []) - scalefactors = [ 1.0 ] + scalefactors = [ -1.0 ] tricubichermite.setEftLinearDerivative(eft1, [ 3, 7 ], Node.VALUE_LABEL_D_DS3, 3, 7, 1) tricubichermite.setEftLinearDerivative(eft1, [ 4, 8 ], Node.VALUE_LABEL_D_DS3, 4, 8, 1) meshGroups += [ conusArteriosusMeshGroup ] @@ -746,7 +748,7 @@ def generateBaseMesh(cls, region, options): vOuterNodeId[nov], vOuterNodeId[novp], rvOutletNodeId[1][3], rvOutletNodeId[1][4] ] eft1 = tricubichermite.createEftNoCrossDerivatives() setEftScaleFactorIds(eft1, [1], []) - scalefactors = [ 1.0 ] + scalefactors = [ -1.0 ] tricubichermite.setEftLinearDerivative(eft1, [ 3, 7 ], Node.VALUE_LABEL_D_DS3, 3, 7, 1) tricubichermite.setEftLinearDerivative(eft1, [ 4, 8 ], Node.VALUE_LABEL_D_DS3, 4, 8, 1) remapEftNodeValueLabel(eft1, [ 2, 6 ], Node.VALUE_LABEL_D_DS2, [ ( Node.VALUE_LABEL_D_DS1, [1] ), ( Node.VALUE_LABEL_D_DS2, []) ]) @@ -759,7 +761,7 @@ def generateBaseMesh(cls, region, options): vOuterNodeId[0], avsNodeId, rvOutletNodeId[1][4], rvOutletNodeId[1][5] ] eft1 = tricubichermite.createEftNoCrossDerivatives() setEftScaleFactorIds(eft1, [1], []) - scalefactors = [ 1.0 ] + scalefactors = [ -1.0 ] remapEftNodeValueLabel(eft1, [ 1, 2 ], Node.VALUE_LABEL_D_DS1, []) remapEftNodeValueLabel(eft1, [ 1, 5 ], Node.VALUE_LABEL_D_DS2, [ ( Node.VALUE_LABEL_D_DS1, [1] ), ( Node.VALUE_LABEL_D_DS2, [] ) ]) remapEftNodeValueLabel(eft1, [ 2 ], Node.VALUE_LABEL_D_DS3, [ ( Node.VALUE_LABEL_D_DS1, [] ), ( Node.VALUE_LABEL_D_DS2, [] ), ( Node.VALUE_LABEL_D_DS3, [] ) ]) @@ -779,7 +781,7 @@ def generateBaseMesh(cls, region, options): avsNodeId, lvOutletNodeId[1][3], rvOutletNodeId[1][5], rvOutletNodeId[1][0] ] eft1 = tricubichermite.createEftNoCrossDerivatives() setEftScaleFactorIds(eft1, [1], []) - scalefactors = [ 1.0 ] + scalefactors = [ -1.0 ] remapEftNodeValueLabel(eft1, [ 1 ], Node.VALUE_LABEL_D_DS3, [ ( Node.VALUE_LABEL_D_DS1, [] ), ( Node.VALUE_LABEL_D_DS2, [] ), ( Node.VALUE_LABEL_D_DS3, [] ) ]) remapEftNodeValueLabel(eft1, [ 2 ], Node.VALUE_LABEL_D_DS3, [ ( Node.VALUE_LABEL_D_DS2, [] ), ( Node.VALUE_LABEL_D_DS3, [] ) ]) tricubichermite.setEftLinearDerivative(eft1, [ 3, 7 ], Node.VALUE_LABEL_D_DS3, 3, 7, 1) @@ -914,7 +916,81 @@ def generateBaseMesh(cls, region, options): for meshGroup in meshGroups: meshGroup.addElement(element) - # RV base elements remainder + + # LV base elements row 2 + for e in range(8): + eft1 = eft + nids = None + scalefactors = None + meshGroups = [ lvMeshGroup ] + + if e == 0: + # 7 node collapsed element where LV outlet ring expands into + nids = [ lvInnerNodeId[-1], lvInnerNodeId[0], lvOutletNodeId[0][3], lvOutletNodeId[0][4], + lvOutletNodeId[1][3], avsNodeId, lvOutletNodeId[1][4] ] + eft1 = tricubichermite.createEftNoCrossDerivatives() + setEftScaleFactorIds(eft1, [1], []) + scalefactors = [ -1.0 ] + remapEftNodeValueLabel(eft1, [ 1, 2 ], Node.VALUE_LABEL_D_DS3, [ ( Node.VALUE_LABEL_D_DS2, [] ), ( Node.VALUE_LABEL_D_DS3, [] ) ]) + tricubichermite.setEftLinearDerivative(eft1, [ 3, 7 ], Node.VALUE_LABEL_D_DS3, 3, 7, 1) + tricubichermite.setEftLinearDerivative(eft1, [ 4, 8 ], Node.VALUE_LABEL_D_DS3, 4, 8, 1) + remapEftNodeValueLabel(eft1, [ 5, 7 ], Node.VALUE_LABEL_D_DS2, []) + remapEftNodeValueLabel(eft1, [ 5, 7 ], Node.VALUE_LABEL_D_DS3, [ ( Node.VALUE_LABEL_D_DS2, [] ) ]) + remapEftNodeValueLabel(eft1, [ 6 ], Node.VALUE_LABEL_D_DS1, [ ( Node.VALUE_LABEL_D_DS1, [] ), ( Node.VALUE_LABEL_D_DS2, [1]) ]) + ln_map = [ 1, 2, 3, 4, 5, 6, 5, 7 ] + remapEftLocalNodes(eft1, 7, ln_map) + elif e == 1: + # 6 node collapsed wedge element mid rv 'crest' + nids = [ lvInnerNodeId[0], lavInnerNodeId[0][2], lvOutletNodeId[0][4], + avsNodeId, lavOuterNodeId[0][2], lvOutletNodeId[1][4] ] + eft1 = tricubichermite.createEftNoCrossDerivatives() + setEftScaleFactorIds(eft1, [1], []) + scalefactors = [ -1.0 ] + remapEftNodeValueLabel(eft1, [ 1, 2, 6 ], Node.VALUE_LABEL_D_DS1, [ ( Node.VALUE_LABEL_D_DS1, [] ), ( Node.VALUE_LABEL_D_DS2, [] ) ]) + remapEftNodeValueLabel(eft1, [ 1 ], Node.VALUE_LABEL_D_DS3, [ ( Node.VALUE_LABEL_D_DS2, [] ), ( Node.VALUE_LABEL_D_DS3, [] ) ]) + remapEftNodeValueLabel(eft1, [ 2, 6 ], Node.VALUE_LABEL_D_DS2, [ ( Node.VALUE_LABEL_D_DS1, [1] ) ]) + tricubichermite.setEftLinearDerivative(eft1, [ 3, 7 ], Node.VALUE_LABEL_D_DS3, 3, 7, 1) + tricubichermite.setEftLinearDerivative(eft1, [ 4, 8 ], Node.VALUE_LABEL_D_DS3, 4, 8, 1) + remapEftNodeValueLabel(eft1, [ 3, 4, 7, 8 ], Node.VALUE_LABEL_D_DS1, []) + remapEftNodeValueLabel(eft1, [ 4, 8 ], Node.VALUE_LABEL_D_DS2, [ ( Node.VALUE_LABEL_D_DS1, [1] ), ( Node.VALUE_LABEL_D_DS2, [] ) ]) + ln_map = [ 1, 2, 3, 3, 4, 5, 6, 6 ] + remapEftLocalNodes(eft1, 6, ln_map) + elif e == 2: + # 7 node collapsed element where LV outlet ring expands into + nids = [ lavInnerNodeId[0][2], lavInnerNodeId[0][1], lvOutletNodeId[0][4], lvOutletNodeId[0][5], + lavOuterNodeId[0][2], lavOuterNodeId[0][1], lvOutletNodeId[1][4] ] + eft1 = tricubichermite.createEftNoCrossDerivatives() + setEftScaleFactorIds(eft1, [1], []) + scalefactors = [ -1.0 ] + # GRC fix d1 and d2 are colinear: + remapEftNodeValueLabel(eft1, [ 1, 2, 5 ], Node.VALUE_LABEL_D_DS1, [ ( Node.VALUE_LABEL_D_DS1, [1] ) ]) + remapEftNodeValueLabel(eft1, [ 1, 5 ], Node.VALUE_LABEL_D_DS2, [ ( Node.VALUE_LABEL_D_DS1, [1] ) ]) + remapEftNodeValueLabel(eft1, [ 2 ], Node.VALUE_LABEL_D_DS2, [ ( Node.VALUE_LABEL_D_DS2, [1] ) ]) + remapEftNodeValueLabel(eft1, [ 3, 7 ], Node.VALUE_LABEL_D_DS2, [ ( Node.VALUE_LABEL_D_DS1, [1] ), ( Node.VALUE_LABEL_D_DS2, [] ) ]) + tricubichermite.setEftLinearDerivative(eft1, [ 3, 7 ], Node.VALUE_LABEL_D_DS3, 3, 7, 1) + tricubichermite.setEftLinearDerivative(eft1, [ 4, 6, 8 ], Node.VALUE_LABEL_D_DS3, 4, 8, 1) + remapEftNodeValueLabel(eft1, [ 6 ], Node.VALUE_LABEL_D_DS1, [ ( Node.VALUE_LABEL_D_DS1, [] ), ( Node.VALUE_LABEL_D_DS3, [1] ) ]) + remapEftNodeValueLabel(eft1, [ 6, 8 ], Node.VALUE_LABEL_D_DS2, []) + ln_map = [ 1, 2, 3, 4, 5, 6, 7, 6 ] + remapEftLocalNodes(eft1, 7, ln_map) + else: + continue + + result = elementtemplate1.defineField(coordinates, -1, eft1) + element = mesh.createElement(elementIdentifier, elementtemplate1) + result2 = element.setNodesByIdentifier(eft1, nids) + if scalefactors: + result3 = element.setScaleFactors(eft1, scalefactors) + else: + result3 = 7 + print('create element lv base r2', elementIdentifier, result, result2, result3, nids) + elementIdentifier += 1 + + for meshGroup in meshGroups: + meshGroup.addElement(element) + + + # RV base elements row 2 for e in range(5): eft1 = eft nids = None @@ -1074,7 +1150,8 @@ def refineMesh(cls, meshrefinement, options): startBaseRvElementIdentifier = startBaseLvElementIdentifier + elementsCountAroundLVFreeWall + 1 elementsCountRVHanging = 1 startBaseSeptumElementIdentifier = startBaseRvElementIdentifier + elementsCountAroundRVFreeWall + elementsCountRVHanging + 2 - startBaseRv2ElementIdentifier = startBaseSeptumElementIdentifier + elementsCountAroundVSeptum + 1 + startBaseLv2ElementIdentifier = startBaseSeptumElementIdentifier + elementsCountAroundVSeptum + 1 + startBaseRv2ElementIdentifier = startBaseLv2ElementIdentifier + 3 limitBaseElementIdentifier = startBaseRv2ElementIdentifier + 5 #print(startBaseLvElementIdentifier, startBaseRvElementIdentifier, startBaseSeptumElementIdentifier, limitBaseElementIdentifier) while element.isValid(): @@ -1082,19 +1159,28 @@ def refineMesh(cls, meshrefinement, options): numberInXi2 = refineElementsCountSurface numberInXi3 = None elementId = element.getIdentifier() - if elementId == startBaseLvElementIdentifier: - # collapsed element on anterior interventricular sulcus: - numberInXi1 = numberInXi3 = refineElementsCountThroughLVWall - elif elementId < startBaseRvElementIdentifier: - numberInXi3 = refineElementsCountThroughLVWall - elif elementId == startBaseRvElementIdentifier: - # collapsed element on posterior interventricular sulcus: - numberInXi1 = numberInXi3 = refineElementsCountThroughLVWall + if elementId < startBaseRvElementIdentifier: + # LV row 1 + if elementId == startBaseLvElementIdentifier: + # collapsed element on anterior interventricular sulcus: + numberInXi1 = numberInXi3 = refineElementsCountThroughLVWall + else: + numberInXi3 = refineElementsCountThroughLVWall elif elementId < startBaseSeptumElementIdentifier: - numberInXi3 = refineElementsCountThroughWall + # RV row 1 + if elementId == startBaseRvElementIdentifier: + # collapsed element on posterior interventricular sulcus: + numberInXi1 = numberInXi3 = refineElementsCountThroughLVWall + else: + numberInXi3 = refineElementsCountThroughWall + elif elementId < startBaseLv2ElementIdentifier: + # V septum + numberInXi3 = refineElementsCountThroughLVWall elif elementId < startBaseRv2ElementIdentifier: + # LV row 2 numberInXi3 = refineElementsCountThroughLVWall else: + # RV row 2 numberInXi3 = refineElementsCountThroughWall meshrefinement.refineElementCubeStandard3d(element, numberInXi1, numberInXi2, numberInXi3) if elementId == (limitBaseElementIdentifier - 1): From f5a645cc36c1c0a7e6dfe0f2084ec26cdd4b19d0 Mon Sep 17 00:00:00 2001 From: Richard Christie Date: Mon, 12 Nov 2018 13:10:48 +1300 Subject: [PATCH 19/25] Fix derivative map around LA/LV freewall --- .../meshtype_3d_heartventriclesbase1.py | 28 +++++++++++++------ 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/scaffoldmaker/meshtypes/meshtype_3d_heartventriclesbase1.py b/scaffoldmaker/meshtypes/meshtype_3d_heartventriclesbase1.py index c75e2ae3..9d974af0 100644 --- a/scaffoldmaker/meshtypes/meshtype_3d_heartventriclesbase1.py +++ b/scaffoldmaker/meshtypes/meshtype_3d_heartventriclesbase1.py @@ -398,6 +398,11 @@ def generateBaseMesh(cls, region, options): nov = elementsCountAroundLVFreeWall - elementsCountLVFreeWallRegular + n1 lavInnerd2[0][noa] = interpolateHermiteLagrangeDerivative(lvInnerx[nov], lvInnerd2[nov], lavInnerx[0][noa], 1.0) lavOuterd2[0][noa] = interpolateHermiteLagrangeDerivative(vOuterx[nov], vOuterd2[nov], lavOuterx[0][noa], 1.0) + if n1 == 0: + # add d1 to d2 since subtracted in use: + for c in range(3): + lavInnerd2[0][noa][c] += lavInnerd1[0][noa][c] + lavOuterd2[0][noa][c] += lavOuterd1[0][noa][c] for n1 in range(1, elementsCountRVFreeWallRegular + 1): noa = elementsCountAroundAtrialSeptum + n1 - 1 nov = elementsCountAroundLVFreeWall + n1 @@ -432,20 +437,19 @@ def generateBaseMesh(cls, region, options): lvOutletOuterd3[-1] = [ -d for d in lavOuterd3[0][1] ] # create point above anterior ventricular septum end - xi = 0.6 + xi = 0.5 fd2 = [ (lvOutletOuterx[4][c] - vOuterx[1][c]) for c in range(3) ] mag = 1.0*vector.magnitude(fd2) fd2 = vector.setMagnitude([ fd2[0], fd2[1], 0.0 ], mag) x = interpolateCubicHermite(vOuterx[0], vOuterd2[0], lvOutletOuterx[4], fd2, xi) d2 = interpolateCubicHermiteDerivative(vOuterx[0], vOuterd2[0], lvOutletOuterx[4], fd2, xi) pd2 = smoothCubicHermiteDerivativesLine([ vOuterx[0], x, lvOutletOuterx[4] ], [ vOuterd2[0], d2, lvOutletd2 ], fixAllDirections=True, fixStartDerivative=True, fixEndDerivative=True) - fd1 = [ (lavOuterd1[0][2][c] + lavOuterd2[0][2][c]) for c in range(3) ] - pd1 = smoothCubicHermiteDerivativesLine([ rvOutletOuterx[-1], x, lavOuterx[0][2] ], [ [ -d for d in rvOutletd2 ], zero, fd1 ], fixStartDerivative=True, fixEndDerivative=True, magnitudeScalingMode=DerivativeScalingMode.HARMONIC_MEAN) + pd1 = smoothCubicHermiteDerivativesLine([ rvOutletOuterx[-1], x, lavOuterx[0][2] ], [ [ -d for d in rvOutletd2 ], zero, lavOuterd2[0][2] ], fixStartDerivative=True, fixEndDirection=True, magnitudeScalingMode=DerivativeScalingMode.HARMONIC_MEAN) avsx = x avsd1 = pd1[1] avsd2 = pd2[1] - sf = math.cos(math.pi*0.25) avsd3 = interpolateHermiteLagrangeDerivative(lvInnerx[0], lvInnerd2[0], avsx, 1.0) + lavOuterd2[0][2] = pd1[2] # create points on bottom and top of RV supraventricular crest ns = elementsCountAroundRVFreeWall//2 + 1 @@ -653,9 +657,15 @@ def generateBaseMesh(cls, region, options): remapEftNodeValueLabel(eft1, [ 1, 3 ], Node.VALUE_LABEL_D_DS2, []) remapEftNodeValueLabel(eft1, [ 3 ], Node.VALUE_LABEL_D_DS1, [ ( Node.VALUE_LABEL_D_DS1, [] ), ( Node.VALUE_LABEL_D_DS2, []) ]) remapEftNodeValueLabel(eft1, [ 3 ], Node.VALUE_LABEL_D_DS3, [ ( Node.VALUE_LABEL_D_DS2, [] ), ( Node.VALUE_LABEL_D_DS3, []) ]) - remapEftNodeValueLabel(eft1, [ 4, 8 ], Node.VALUE_LABEL_D_DS1, [ ( Node.VALUE_LABEL_D_DS1, [] ), ( Node.VALUE_LABEL_D_DS2, []) ]) + remapEftNodeValueLabel(eft1, [ 4, 8 ], Node.VALUE_LABEL_D_DS2, [ ( Node.VALUE_LABEL_D_DS1, [1] ), ( Node.VALUE_LABEL_D_DS2, []) ]) + remapEftNodeValueLabel(eft1, [ 4, 8 ], Node.VALUE_LABEL_D_DS1, [ ( Node.VALUE_LABEL_D_DS2, []) ]) ln_map = [ 1, 2, 1, 3, 4, 5, 6, 7 ] remapEftLocalNodes(eft1, 7, ln_map) + elif e == 1: + eft1 = tricubichermite.createEftNoCrossDerivatives() + setEftScaleFactorIds(eft1, [1], []) + scalefactors = [ -1.0 ] + remapEftNodeValueLabel(eft1, [ 3, 7 ], Node.VALUE_LABEL_D_DS2, [ ( Node.VALUE_LABEL_D_DS1, [1] ), ( Node.VALUE_LABEL_D_DS2, []) ]) elif e == (elementsCountAroundLVFreeWall - 1): # general linear map d3 adjacent to collapsed crux, transition to atria eft1 = tricubichermite.createEftNoCrossDerivatives() @@ -946,9 +956,10 @@ def generateBaseMesh(cls, region, options): eft1 = tricubichermite.createEftNoCrossDerivatives() setEftScaleFactorIds(eft1, [1], []) scalefactors = [ -1.0 ] - remapEftNodeValueLabel(eft1, [ 1, 2, 6 ], Node.VALUE_LABEL_D_DS1, [ ( Node.VALUE_LABEL_D_DS1, [] ), ( Node.VALUE_LABEL_D_DS2, [] ) ]) + remapEftNodeValueLabel(eft1, [ 1 ], Node.VALUE_LABEL_D_DS1, [ ( Node.VALUE_LABEL_D_DS1, [] ), ( Node.VALUE_LABEL_D_DS2, [] ) ]) remapEftNodeValueLabel(eft1, [ 1 ], Node.VALUE_LABEL_D_DS3, [ ( Node.VALUE_LABEL_D_DS2, [] ), ( Node.VALUE_LABEL_D_DS3, [] ) ]) - remapEftNodeValueLabel(eft1, [ 2, 6 ], Node.VALUE_LABEL_D_DS2, [ ( Node.VALUE_LABEL_D_DS1, [1] ) ]) + remapEftNodeValueLabel(eft1, [ 2, 6 ], Node.VALUE_LABEL_D_DS2, [ ( Node.VALUE_LABEL_D_DS1, [1] ), ( Node.VALUE_LABEL_D_DS2, [1] ) ]) + remapEftNodeValueLabel(eft1, [ 2, 6 ], Node.VALUE_LABEL_D_DS1, [ ( Node.VALUE_LABEL_D_DS2, [] ) ]) tricubichermite.setEftLinearDerivative(eft1, [ 3, 7 ], Node.VALUE_LABEL_D_DS3, 3, 7, 1) tricubichermite.setEftLinearDerivative(eft1, [ 4, 8 ], Node.VALUE_LABEL_D_DS3, 4, 8, 1) remapEftNodeValueLabel(eft1, [ 3, 4, 7, 8 ], Node.VALUE_LABEL_D_DS1, []) @@ -962,9 +973,8 @@ def generateBaseMesh(cls, region, options): eft1 = tricubichermite.createEftNoCrossDerivatives() setEftScaleFactorIds(eft1, [1], []) scalefactors = [ -1.0 ] - # GRC fix d1 and d2 are colinear: remapEftNodeValueLabel(eft1, [ 1, 2, 5 ], Node.VALUE_LABEL_D_DS1, [ ( 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_DS2, [ ( Node.VALUE_LABEL_D_DS1, [1] ), ( Node.VALUE_LABEL_D_DS2, [1] ) ]) remapEftNodeValueLabel(eft1, [ 2 ], Node.VALUE_LABEL_D_DS2, [ ( Node.VALUE_LABEL_D_DS2, [1] ) ]) remapEftNodeValueLabel(eft1, [ 3, 7 ], Node.VALUE_LABEL_D_DS2, [ ( Node.VALUE_LABEL_D_DS1, [1] ), ( Node.VALUE_LABEL_D_DS2, [] ) ]) tricubichermite.setEftLinearDerivative(eft1, [ 3, 7 ], Node.VALUE_LABEL_D_DS3, 3, 7, 1) From 62df04f15c2b0e5f7a7fe78dea0a03be873d7f3a Mon Sep 17 00:00:00 2001 From: Richard Christie Date: Tue, 13 Nov 2018 16:28:37 +1300 Subject: [PATCH 20/25] Close base --- .../meshtype_3d_heartventriclesbase1.py | 101 +++++++++++++----- 1 file changed, 76 insertions(+), 25 deletions(-) diff --git a/scaffoldmaker/meshtypes/meshtype_3d_heartventriclesbase1.py b/scaffoldmaker/meshtypes/meshtype_3d_heartventriclesbase1.py index 9d974af0..78a83ded 100644 --- a/scaffoldmaker/meshtypes/meshtype_3d_heartventriclesbase1.py +++ b/scaffoldmaker/meshtypes/meshtype_3d_heartventriclesbase1.py @@ -266,19 +266,23 @@ def generateBaseMesh(cls, region, options): vOuterNodeId = [ (startVOuterNodeId + n1) for n1 in range(elementsCountAroundLV) ] for nodeId in [ lvInnerNodeId, rvInnerNodeId, vOuterNodeId ]: vx = [] + #vd1 = [] vd2 = [] - vd3 = [] + #vd3 = [] for n1 in range(len(nodeId)): node = nodes.findNodeByIdentifier(nodeId[n1]) cache.setNode(node) result, x = coordinates.getNodeParameters(cache, -1, Node.VALUE_LABEL_VALUE, 1, 3) + vx.append(x) result, d2 = coordinates.getNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS2, 1, 3) - result, d3 = coordinates.getNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS3, 1, 3) - vx .append(x) vd2.append(d2) - vd3.append(d3) + #if nodeId is lvInnerNodeId: + # result, d1 = coordinates.getNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS1, 1, 3) + # vd1.append(d1) + # result, d3 = coordinates.getNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS3, 1, 3) + # vd3.append(d3) if nodeId is lvInnerNodeId: - lvInnerx, lvInnerd2, lvInnerd3 = vx, vd2, vd3 + lvInnerx, lvInnerd2 = vx, vd2 elif nodeId is rvInnerNodeId: rvInnerx, rvInnerd2 = vx, vd2 else: @@ -417,6 +421,10 @@ def generateBaseMesh(cls, region, options): ravInnerd2[0][noa] = interpolateHermiteLagrangeDerivative(rvInnerx[nov], rvInnerd2[nov], ravInnerx[0][noa], 1.0) # special fix for left cfb lavInnerd2[0][1] = interpolateHermiteLagrangeDerivative(lvOutletInnerx[-1], [ -d for d in lvOutletd2 ], lavInnerx[0][1], 1.0) + # special fix for left-central cfb (fix from above) + sd2 = [ (-lvOutletInnerd1[0][c] - lvOutletd2[c]) for c in range(3) ] + pd2 = smoothCubicHermiteDerivativesLine([ lvOutletInnerx[0], lavInnerx[0][0] ], [ sd2, lavInnerd2[0][0] ], fixStartDerivative=True, fixEndDirection=True) + lavInnerd2[0][0] = pd2[1] # special fix for right cfb noa = elementsCountAroundAtria - 2 nov = -elementsCountAroundAtrialSeptum - 1 @@ -862,7 +870,7 @@ def generateBaseMesh(cls, region, options): ra1 = elementsCountAroundAtrialSeptum - 1 - e nids = [ lvInnerNodeId[lv1], lvOutletNodeId[1][0], lavInnerNodeId[0][la1], rvInnerNodeId[rv1], ravInnerNodeId[0][ra1] ] scaleEftNodeValueLabels(eft1, [ 5 ], [ Node.VALUE_LABEL_D_DS3 ], [ 1 ]) - remapEftNodeValueLabel(eft1, [ 1 ], Node.VALUE_LABEL_D_DS1, [ ( Node.VALUE_LABEL_D_DS1, [] ), ( Node.VALUE_LABEL_D_DS2, [] ), ( Node.VALUE_LABEL_D_DS3, [] ) ]) + remapEftNodeValueLabel(eft1, [ 1 ], Node.VALUE_LABEL_D_DS1, [ ( Node.VALUE_LABEL_D_DS2, [] ), ( Node.VALUE_LABEL_D_DS3, [] ) ]) tricubichermite.setEftLinearDerivative(eft1, [ 2, 4 ], Node.VALUE_LABEL_D_DS2, 2, 4, 1) remapEftNodeValueLabel(eft1, [ 2 ], Node.VALUE_LABEL_D_DS1, [ ( Node.VALUE_LABEL_D_DS2, []) ]) remapEftNodeValueLabel(eft1, [ 4 ], Node.VALUE_LABEL_D_DS1, [ ( Node.VALUE_LABEL_D_DS1, [] ), ( Node.VALUE_LABEL_D_DS3, []) ]) @@ -872,7 +880,7 @@ def generateBaseMesh(cls, region, options): remapEftNodeValueLabel(eft1, [ 3 ], Node.VALUE_LABEL_D_DS1, [ ( Node.VALUE_LABEL_D_DS3, [] ) ]) remapEftNodeValueLabel(eft1, [ 7 ], Node.VALUE_LABEL_D_DS3, [ ( Node.VALUE_LABEL_D_DS1, [1] ), ( Node.VALUE_LABEL_D_DS3, [1] ) ]) remapEftNodeValueLabel(eft1, [ 7 ], Node.VALUE_LABEL_D_DS1, [ ( Node.VALUE_LABEL_D_DS3, [] ) ]) - remapEftNodeValueLabel(eft1, [ 6 ], Node.VALUE_LABEL_D_DS1, [ ( Node.VALUE_LABEL_D_DS1, [1] ), ( Node.VALUE_LABEL_D_DS2, []) ]) + remapEftNodeValueLabel(eft1, [ 6 ], Node.VALUE_LABEL_D_DS1, [ ( Node.VALUE_LABEL_D_DS1, [1] ), ( Node.VALUE_LABEL_D_DS3, []) ]) remapEftNodeValueLabel(eft1, [ 8 ], Node.VALUE_LABEL_D_DS1, [ ( Node.VALUE_LABEL_D_DS1, [1] ), ( Node.VALUE_LABEL_D_DS3, []) ]) tricubichermite.setEftLinearDerivative(eft1, [ 6, 8 ], Node.VALUE_LABEL_D_DS2, 2, 4, 1) ln_map = [ 1, 2, 3, 2, 4, 2, 5, 2 ] @@ -891,10 +899,10 @@ def generateBaseMesh(cls, region, options): remapEftNodeValueLabel(eft1, [ 3, 4, 7, 8 ], Node.VALUE_LABEL_D_DS3, []) if lo1 == 0: scaleEftNodeValueLabels(eft1, [ 5, 6 ], [ Node.VALUE_LABEL_D_DS3 ], [ 1 ]) - remapEftNodeValueLabel(eft1, [ 1 ], Node.VALUE_LABEL_D_DS2, [ ( Node.VALUE_LABEL_D_DS1, [] ), ( Node.VALUE_LABEL_D_DS2, [] ), ( Node.VALUE_LABEL_D_DS3, [] ) ]) + remapEftNodeValueLabel(eft1, [ 1 ], Node.VALUE_LABEL_D_DS2, [ ( Node.VALUE_LABEL_D_DS2, [] ), ( Node.VALUE_LABEL_D_DS3, [] ) ]) remapEftNodeValueLabel(eft1, [ 2, 6 ], Node.VALUE_LABEL_D_DS2, [ ( Node.VALUE_LABEL_D_DS2, [] ), ( Node.VALUE_LABEL_D_DS3, [] ) ]) remapEftNodeValueLabel(eft1, [ 5 ], Node.VALUE_LABEL_D_DS2, [ ( Node.VALUE_LABEL_D_DS1, [1] ), ( Node.VALUE_LABEL_D_DS2, [] ), ( Node.VALUE_LABEL_D_DS3, [] ) ]) - remapEftNodeValueLabel(eft1, [ 7 ], Node.VALUE_LABEL_D_DS2, [ ( Node.VALUE_LABEL_D_DS1, [1] ), ( Node.VALUE_LABEL_D_DS2, []) ]) + remapEftNodeValueLabel(eft1, [ 7 ], Node.VALUE_LABEL_D_DS2, [ ( Node.VALUE_LABEL_D_DS1, [1] ), ( Node.VALUE_LABEL_D_DS3, []) ]) elif lv2 == 0: # final element on v septum nids[3] = avsNodeId scaleEftNodeValueLabels(eft1, [ 5 ], [ Node.VALUE_LABEL_D_DS3 ], [ 1 ]) @@ -934,13 +942,13 @@ def generateBaseMesh(cls, region, options): scalefactors = None meshGroups = [ lvMeshGroup ] + eft1 = tricubichermite.createEftNoCrossDerivatives() + setEftScaleFactorIds(eft1, [1], []) + scalefactors = [ -1.0 ] if e == 0: - # 7 node collapsed element where LV outlet ring expands into + # 7 node collapsed element where lv outlet ring expands into nids = [ lvInnerNodeId[-1], lvInnerNodeId[0], lvOutletNodeId[0][3], lvOutletNodeId[0][4], lvOutletNodeId[1][3], avsNodeId, lvOutletNodeId[1][4] ] - eft1 = tricubichermite.createEftNoCrossDerivatives() - setEftScaleFactorIds(eft1, [1], []) - scalefactors = [ -1.0 ] remapEftNodeValueLabel(eft1, [ 1, 2 ], Node.VALUE_LABEL_D_DS3, [ ( Node.VALUE_LABEL_D_DS2, [] ), ( Node.VALUE_LABEL_D_DS3, [] ) ]) tricubichermite.setEftLinearDerivative(eft1, [ 3, 7 ], Node.VALUE_LABEL_D_DS3, 3, 7, 1) tricubichermite.setEftLinearDerivative(eft1, [ 4, 8 ], Node.VALUE_LABEL_D_DS3, 4, 8, 1) @@ -950,12 +958,9 @@ def generateBaseMesh(cls, region, options): ln_map = [ 1, 2, 3, 4, 5, 6, 5, 7 ] remapEftLocalNodes(eft1, 7, ln_map) elif e == 1: - # 6 node collapsed wedge element mid rv 'crest' + # 6 node collapsed wedge element mid lv 'crest' nids = [ lvInnerNodeId[0], lavInnerNodeId[0][2], lvOutletNodeId[0][4], avsNodeId, lavOuterNodeId[0][2], lvOutletNodeId[1][4] ] - eft1 = tricubichermite.createEftNoCrossDerivatives() - setEftScaleFactorIds(eft1, [1], []) - scalefactors = [ -1.0 ] remapEftNodeValueLabel(eft1, [ 1 ], Node.VALUE_LABEL_D_DS1, [ ( Node.VALUE_LABEL_D_DS1, [] ), ( Node.VALUE_LABEL_D_DS2, [] ) ]) remapEftNodeValueLabel(eft1, [ 1 ], Node.VALUE_LABEL_D_DS3, [ ( Node.VALUE_LABEL_D_DS2, [] ), ( Node.VALUE_LABEL_D_DS3, [] ) ]) remapEftNodeValueLabel(eft1, [ 2, 6 ], Node.VALUE_LABEL_D_DS2, [ ( Node.VALUE_LABEL_D_DS1, [1] ), ( Node.VALUE_LABEL_D_DS2, [1] ) ]) @@ -967,24 +972,70 @@ def generateBaseMesh(cls, region, options): ln_map = [ 1, 2, 3, 3, 4, 5, 6, 6 ] remapEftLocalNodes(eft1, 6, ln_map) elif e == 2: - # 7 node collapsed element where LV outlet ring expands into + # 7 node collapsed element where lv outlet ring expands into nids = [ lavInnerNodeId[0][2], lavInnerNodeId[0][1], lvOutletNodeId[0][4], lvOutletNodeId[0][5], lavOuterNodeId[0][2], lavOuterNodeId[0][1], lvOutletNodeId[1][4] ] - eft1 = tricubichermite.createEftNoCrossDerivatives() - setEftScaleFactorIds(eft1, [1], []) - scalefactors = [ -1.0 ] remapEftNodeValueLabel(eft1, [ 1, 2, 5 ], Node.VALUE_LABEL_D_DS1, [ ( Node.VALUE_LABEL_D_DS1, [1] ) ]) remapEftNodeValueLabel(eft1, [ 1, 5 ], Node.VALUE_LABEL_D_DS2, [ ( Node.VALUE_LABEL_D_DS1, [1] ), ( Node.VALUE_LABEL_D_DS2, [1] ) ]) remapEftNodeValueLabel(eft1, [ 2 ], Node.VALUE_LABEL_D_DS2, [ ( Node.VALUE_LABEL_D_DS2, [1] ) ]) remapEftNodeValueLabel(eft1, [ 3, 7 ], Node.VALUE_LABEL_D_DS2, [ ( Node.VALUE_LABEL_D_DS1, [1] ), ( Node.VALUE_LABEL_D_DS2, [] ) ]) tricubichermite.setEftLinearDerivative(eft1, [ 3, 7 ], Node.VALUE_LABEL_D_DS3, 3, 7, 1) - tricubichermite.setEftLinearDerivative(eft1, [ 4, 6, 8 ], Node.VALUE_LABEL_D_DS3, 4, 8, 1) + tricubichermite.setEftLinearDerivative(eft1, [ 4, 8 ], Node.VALUE_LABEL_D_DS3, 4, 8, 1) remapEftNodeValueLabel(eft1, [ 6 ], Node.VALUE_LABEL_D_DS1, [ ( Node.VALUE_LABEL_D_DS1, [] ), ( Node.VALUE_LABEL_D_DS3, [1] ) ]) remapEftNodeValueLabel(eft1, [ 6, 8 ], Node.VALUE_LABEL_D_DS2, []) + remapEftNodeValueLabel(eft1, [ 6 ], Node.VALUE_LABEL_D_DS3, [ ( Node.VALUE_LABEL_D_DS3, [1] ) ]) ln_map = [ 1, 2, 3, 4, 5, 6, 7, 6 ] remapEftLocalNodes(eft1, 7, ln_map) + elif e == 3: + # 6 node wedge element bridge/curtain between mitral and aortic valve orifices + no = e - 4 + nids = [ lavInnerNodeId[0][1], lavInnerNodeId[0][0], lvOutletNodeId[0][no], lvOutletNodeId[0][no + 1], lvOutletNodeId[1][no], lvOutletNodeId[1][no + 1] ] + remapEftNodeValueLabel(eft1, [ 1, 2 ], Node.VALUE_LABEL_D_DS1, [ ( Node.VALUE_LABEL_D_DS1, [1] ) ]) + remapEftNodeValueLabel(eft1, [ 1 ], Node.VALUE_LABEL_D_DS2, [ ( Node.VALUE_LABEL_D_DS2, [1] ) ]) + remapEftNodeValueLabel(eft1, [ 2 ], Node.VALUE_LABEL_D_DS2, [ ( Node.VALUE_LABEL_D_DS1, [] ), ( Node.VALUE_LABEL_D_DS2, [1] ) ]) + remapEftNodeValueLabel(eft1, [ 4 ], Node.VALUE_LABEL_D_DS2, [ ( Node.VALUE_LABEL_D_DS1, [] ), ( Node.VALUE_LABEL_D_DS2, [] ) ]) + remapEftNodeValueLabel(eft1, [ 5 ], Node.VALUE_LABEL_D_DS3, [ ( Node.VALUE_LABEL_D_DS3, [1] ) ]) + # GRC cfb d3 should be reversed in future + remapEftNodeValueLabel(eft1, [ 6 ], Node.VALUE_LABEL_D_DS3, [ ( Node.VALUE_LABEL_D_DS1, [] ), ( Node.VALUE_LABEL_D_DS3, [] ) ]) + tricubichermite.setEftLinearDerivative(eft1, [ 3, 7 ], Node.VALUE_LABEL_D_DS3, 3, 7, 1) + tricubichermite.setEftLinearDerivative(eft1, [ 4, 8 ], Node.VALUE_LABEL_D_DS3, 4, 8, 1) + remapEftNodeValueLabel(eft1, [ 5, 6, 7, 8 ], Node.VALUE_LABEL_D_DS2, []) + ln_map = [ 1, 2, 3, 4, 5, 6, 5, 6 ] + remapEftLocalNodes(eft1, 6, ln_map) + elif e == 4: + # tetrahedral cfb shim-bridge connector element + ni = elementsCountAroundLVFreeWall + elementsCountAroundAtrialSeptum + nids = [ lavInnerNodeId[0][0], lvInnerNodeId[ni], lvOutletNodeId[0][0], lvOutletNodeId[1][0] ] + remapEftNodeValueLabel(eft1, [ 1, 2 ], Node.VALUE_LABEL_D_DS1, [ ( Node.VALUE_LABEL_D_DS2, [1] ) ]) + remapEftNodeValueLabel(eft1, [ 1 ], Node.VALUE_LABEL_D_DS2, [ ( Node.VALUE_LABEL_D_DS1, [] ), ( Node.VALUE_LABEL_D_DS2, [1] ) ]) + remapEftNodeValueLabel(eft1, [ 2 ], Node.VALUE_LABEL_D_DS2, [ ( Node.VALUE_LABEL_D_DS1, [] ), ( Node.VALUE_LABEL_D_DS2, [] ) ]) + remapEftNodeValueLabel(eft1, [ 2 ], Node.VALUE_LABEL_D_DS3, [ ( Node.VALUE_LABEL_D_DS2, [] ), ( Node.VALUE_LABEL_D_DS3, [] ) ]) + remapEftNodeValueLabel(eft1, [ 3, 4 ], Node.VALUE_LABEL_D_DS1, []) + remapEftNodeValueLabel(eft1, [ 3 ], Node.VALUE_LABEL_D_DS2, [ ( Node.VALUE_LABEL_D_DS1, [] ), ( Node.VALUE_LABEL_D_DS2, [] ) ]) + tricubichermite.setEftLinearDerivative(eft1, [ 3, 4, 7, 8 ], Node.VALUE_LABEL_D_DS3, 3, 7, 1) + remapEftNodeValueLabel(eft1, [ 5, 6, 7, 8 ], Node.VALUE_LABEL_D_DS1, []) + remapEftNodeValueLabel(eft1, [ 5, 6, 7, 8 ], Node.VALUE_LABEL_D_DS2, []) + remapEftNodeValueLabel(eft1, [ 5 ], Node.VALUE_LABEL_D_DS3, [ ( Node.VALUE_LABEL_D_DS1, [] ), ( Node.VALUE_LABEL_D_DS3, [] ) ]) + remapEftNodeValueLabel(eft1, [ 6 ], Node.VALUE_LABEL_D_DS3, [ ( Node.VALUE_LABEL_D_DS2, [] ) ]) + ln_map = [ 1, 2, 3, 3, 4, 4, 4, 4 ] + remapEftLocalNodes(eft1, 4, ln_map) else: - continue + # 6 node shim wedge elements around LV outlet + no = e - 5 + ni = elementsCountAroundLVFreeWall + elementsCountAroundAtrialSeptum + no + nids = [ lvInnerNodeId[ni], lvInnerNodeId[ni + 1], lvOutletNodeId[0][no], lvOutletNodeId[0][no + 1], lvOutletNodeId[1][no], lvOutletNodeId[1][no + 1] ] + if no == 0: + remapEftNodeValueLabel(eft1, [ 1 ], Node.VALUE_LABEL_D_DS2, [ ( Node.VALUE_LABEL_D_DS1, [] ), ( Node.VALUE_LABEL_D_DS2, [] ) ]) + remapEftNodeValueLabel(eft1, [ 1 ], Node.VALUE_LABEL_D_DS3, [ ( Node.VALUE_LABEL_D_DS2, [] ), ( Node.VALUE_LABEL_D_DS3, [] ) ]) + remapEftNodeValueLabel(eft1, [ 2 ], Node.VALUE_LABEL_D_DS3, [ ( Node.VALUE_LABEL_D_DS2, [] ), ( Node.VALUE_LABEL_D_DS3, [] ) ]) + else: + remapEftNodeValueLabel(eft1, [ 1, 2 ], Node.VALUE_LABEL_D_DS3, [ ( Node.VALUE_LABEL_D_DS2, [] ), ( Node.VALUE_LABEL_D_DS3, [] ) ]) + tricubichermite.setEftLinearDerivative(eft1, [ 3, 7 ], Node.VALUE_LABEL_D_DS3, 3, 7, 1) + tricubichermite.setEftLinearDerivative(eft1, [ 4, 8 ], Node.VALUE_LABEL_D_DS3, 4, 8, 1) + remapEftNodeValueLabel(eft1, [ 5, 6, 7, 8 ], Node.VALUE_LABEL_D_DS2, []) + remapEftNodeValueLabel(eft1, [ 5, 6 ], Node.VALUE_LABEL_D_DS3, [ ( Node.VALUE_LABEL_D_DS2, [] ) ]) + ln_map = [ 1, 2, 3, 4, 5, 6, 5, 6 ] + remapEftLocalNodes(eft1, 6, ln_map) result = elementtemplate1.defineField(coordinates, -1, eft1) element = mesh.createElement(elementIdentifier, elementtemplate1) @@ -1015,7 +1066,7 @@ def generateBaseMesh(cls, region, options): scalefactors = [ -1.0 ] setEftScaleFactorIds(eft1, [1], []) remapEftNodeValueLabel(eft1, [ 1, 2, 3, 4 ], Node.VALUE_LABEL_D_DS2, []) - remapEftNodeValueLabel(eft1, [ 1 ], Node.VALUE_LABEL_D_DS3, [ ( Node.VALUE_LABEL_D_DS1, [] ), ( Node.VALUE_LABEL_D_DS2, [1] ) ]) + remapEftNodeValueLabel(eft1, [ 1 ], Node.VALUE_LABEL_D_DS3, [ ( Node.VALUE_LABEL_D_DS1, [] ), ( Node.VALUE_LABEL_D_DS3, [1] ) ]) remapEftNodeValueLabel(eft1, [ 2 ], Node.VALUE_LABEL_D_DS3, [ ( Node.VALUE_LABEL_D_DS2, [1] ) ]) remapEftNodeValueLabel(eft1, [ 3 ], Node.VALUE_LABEL_D_DS3, [ ( Node.VALUE_LABEL_D_DS1, [] ), ( Node.VALUE_LABEL_D_DS3, [1] ) ]) scaleEftNodeValueLabels(eft1, [ 5, 6 ], [ Node.VALUE_LABEL_D_DS1 ], [ 1 ]) @@ -1161,7 +1212,7 @@ def refineMesh(cls, meshrefinement, options): elementsCountRVHanging = 1 startBaseSeptumElementIdentifier = startBaseRvElementIdentifier + elementsCountAroundRVFreeWall + elementsCountRVHanging + 2 startBaseLv2ElementIdentifier = startBaseSeptumElementIdentifier + elementsCountAroundVSeptum + 1 - startBaseRv2ElementIdentifier = startBaseLv2ElementIdentifier + 3 + startBaseRv2ElementIdentifier = startBaseLv2ElementIdentifier + 8 limitBaseElementIdentifier = startBaseRv2ElementIdentifier + 5 #print(startBaseLvElementIdentifier, startBaseRvElementIdentifier, startBaseSeptumElementIdentifier, limitBaseElementIdentifier) while element.isValid(): From 47d926ca01aa5fd81a96cc62bf6f373290130cc1 Mon Sep 17 00:00:00 2001 From: Richard Christie Date: Wed, 14 Nov 2018 08:55:58 +1300 Subject: [PATCH 21/25] Fix mapping at cfb element --- .../meshtypes/meshtype_3d_heartventriclesbase1.py | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/scaffoldmaker/meshtypes/meshtype_3d_heartventriclesbase1.py b/scaffoldmaker/meshtypes/meshtype_3d_heartventriclesbase1.py index 78a83ded..1ea03bf7 100644 --- a/scaffoldmaker/meshtypes/meshtype_3d_heartventriclesbase1.py +++ b/scaffoldmaker/meshtypes/meshtype_3d_heartventriclesbase1.py @@ -869,20 +869,19 @@ def generateBaseMesh(cls, region, options): la1 = (elementsCountAroundAtrialFreeWall + e)%elementsCountAroundAtria ra1 = elementsCountAroundAtrialSeptum - 1 - e nids = [ lvInnerNodeId[lv1], lvOutletNodeId[1][0], lavInnerNodeId[0][la1], rvInnerNodeId[rv1], ravInnerNodeId[0][ra1] ] - scaleEftNodeValueLabels(eft1, [ 5 ], [ Node.VALUE_LABEL_D_DS3 ], [ 1 ]) remapEftNodeValueLabel(eft1, [ 1 ], Node.VALUE_LABEL_D_DS1, [ ( Node.VALUE_LABEL_D_DS2, [] ), ( Node.VALUE_LABEL_D_DS3, [] ) ]) - tricubichermite.setEftLinearDerivative(eft1, [ 2, 4 ], Node.VALUE_LABEL_D_DS2, 2, 4, 1) + remapEftNodeValueLabel(eft1, [ 2, 4, 6, 8 ], Node.VALUE_LABEL_D_DS2, []) remapEftNodeValueLabel(eft1, [ 2 ], Node.VALUE_LABEL_D_DS1, [ ( Node.VALUE_LABEL_D_DS2, []) ]) - remapEftNodeValueLabel(eft1, [ 4 ], Node.VALUE_LABEL_D_DS1, [ ( Node.VALUE_LABEL_D_DS1, [] ), ( Node.VALUE_LABEL_D_DS3, []) ]) remapEftNodeValueLabel(eft1, [ 2, 4, 6, 8 ], Node.VALUE_LABEL_D_DS3, []) - remapEftNodeValueLabel(eft1, [ 5 ], Node.VALUE_LABEL_D_DS1, [ ( Node.VALUE_LABEL_D_DS1, [1] ), ( Node.VALUE_LABEL_D_DS2, [] ), ( Node.VALUE_LABEL_D_DS3, [] ) ]) remapEftNodeValueLabel(eft1, [ 3 ], Node.VALUE_LABEL_D_DS3, [ ( Node.VALUE_LABEL_D_DS1, [1] ), ( Node.VALUE_LABEL_D_DS3, [] ) ]) remapEftNodeValueLabel(eft1, [ 3 ], Node.VALUE_LABEL_D_DS1, [ ( Node.VALUE_LABEL_D_DS3, [] ) ]) + remapEftNodeValueLabel(eft1, [ 4 ], Node.VALUE_LABEL_D_DS1, [ ( Node.VALUE_LABEL_D_DS1, [] ), ( Node.VALUE_LABEL_D_DS3, []) ]) + remapEftNodeValueLabel(eft1, [ 5 ], Node.VALUE_LABEL_D_DS1, [ ( Node.VALUE_LABEL_D_DS1, [1] ), ( Node.VALUE_LABEL_D_DS2, [] ), ( Node.VALUE_LABEL_D_DS3, [] ) ]) + remapEftNodeValueLabel(eft1, [ 5 ], Node.VALUE_LABEL_D_DS3, [ ( Node.VALUE_LABEL_D_DS3, [1] ) ]) + remapEftNodeValueLabel(eft1, [ 6 ], Node.VALUE_LABEL_D_DS1, [ ( Node.VALUE_LABEL_D_DS1, [1] ), ( Node.VALUE_LABEL_D_DS3, []) ]) remapEftNodeValueLabel(eft1, [ 7 ], Node.VALUE_LABEL_D_DS3, [ ( Node.VALUE_LABEL_D_DS1, [1] ), ( Node.VALUE_LABEL_D_DS3, [1] ) ]) remapEftNodeValueLabel(eft1, [ 7 ], Node.VALUE_LABEL_D_DS1, [ ( Node.VALUE_LABEL_D_DS3, [] ) ]) - remapEftNodeValueLabel(eft1, [ 6 ], Node.VALUE_LABEL_D_DS1, [ ( Node.VALUE_LABEL_D_DS1, [1] ), ( Node.VALUE_LABEL_D_DS3, []) ]) remapEftNodeValueLabel(eft1, [ 8 ], Node.VALUE_LABEL_D_DS1, [ ( Node.VALUE_LABEL_D_DS1, [1] ), ( Node.VALUE_LABEL_D_DS3, []) ]) - tricubichermite.setEftLinearDerivative(eft1, [ 6, 8 ], Node.VALUE_LABEL_D_DS2, 2, 4, 1) ln_map = [ 1, 2, 3, 2, 4, 2, 5, 2 ] remapEftLocalNodes(eft1, 5, ln_map) else: From 608dfc40454c8a9d119dfa970c406b2e5920dc97 Mon Sep 17 00:00:00 2001 From: Richard Christie Date: Wed, 14 Nov 2018 09:08:25 +1300 Subject: [PATCH 22/25] Reverse derivative 3 at cfb --- .../meshtype_3d_heartventriclesbase1.py | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/scaffoldmaker/meshtypes/meshtype_3d_heartventriclesbase1.py b/scaffoldmaker/meshtypes/meshtype_3d_heartventriclesbase1.py index 1ea03bf7..69fd14ec 100644 --- a/scaffoldmaker/meshtypes/meshtype_3d_heartventriclesbase1.py +++ b/scaffoldmaker/meshtypes/meshtype_3d_heartventriclesbase1.py @@ -439,8 +439,8 @@ def generateBaseMesh(cls, region, options): ravInnerd2[0][ravsvcn1] = vector.setMagnitude(vector.crossproduct3(ravInnerd3[0][ravsvcn1], ravInnerd1[0][ravsvcn1]), mag) ravOuterd2[0][ravsvcn1] = vector.setMagnitude(vector.crossproduct3(ravOuterd3[0][ravsvcn1], ravOuterd1[0][ravsvcn1]), mag) - # copy derivative 3 from av points to LV outlet at centre, left and right cfb: - lvOutletOuterd3[0] = lavOuterd3[0][0] + # copy derivative 3 from av points to LV outlet at centre, left and right cfb; negate as d1 is reversed: + lvOutletOuterd3[0] = [ -d for d in lavOuterd3[0][0] ] lvOutletOuterd3[1] = [ -d for d in ravOuterd3[0][-2] ] lvOutletOuterd3[-1] = [ -d for d in lavOuterd3[0][1] ] @@ -875,13 +875,13 @@ def generateBaseMesh(cls, region, options): remapEftNodeValueLabel(eft1, [ 2, 4, 6, 8 ], Node.VALUE_LABEL_D_DS3, []) remapEftNodeValueLabel(eft1, [ 3 ], Node.VALUE_LABEL_D_DS3, [ ( Node.VALUE_LABEL_D_DS1, [1] ), ( Node.VALUE_LABEL_D_DS3, [] ) ]) remapEftNodeValueLabel(eft1, [ 3 ], Node.VALUE_LABEL_D_DS1, [ ( Node.VALUE_LABEL_D_DS3, [] ) ]) - remapEftNodeValueLabel(eft1, [ 4 ], Node.VALUE_LABEL_D_DS1, [ ( Node.VALUE_LABEL_D_DS1, [] ), ( Node.VALUE_LABEL_D_DS3, []) ]) + remapEftNodeValueLabel(eft1, [ 4 ], Node.VALUE_LABEL_D_DS1, [ ( Node.VALUE_LABEL_D_DS1, [] ), ( Node.VALUE_LABEL_D_DS3, [1]) ]) remapEftNodeValueLabel(eft1, [ 5 ], Node.VALUE_LABEL_D_DS1, [ ( Node.VALUE_LABEL_D_DS1, [1] ), ( Node.VALUE_LABEL_D_DS2, [] ), ( Node.VALUE_LABEL_D_DS3, [] ) ]) remapEftNodeValueLabel(eft1, [ 5 ], Node.VALUE_LABEL_D_DS3, [ ( Node.VALUE_LABEL_D_DS3, [1] ) ]) - remapEftNodeValueLabel(eft1, [ 6 ], Node.VALUE_LABEL_D_DS1, [ ( Node.VALUE_LABEL_D_DS1, [1] ), ( Node.VALUE_LABEL_D_DS3, []) ]) + remapEftNodeValueLabel(eft1, [ 6 ], Node.VALUE_LABEL_D_DS1, [ ( Node.VALUE_LABEL_D_DS1, [1] ), ( Node.VALUE_LABEL_D_DS3, [1]) ]) remapEftNodeValueLabel(eft1, [ 7 ], Node.VALUE_LABEL_D_DS3, [ ( Node.VALUE_LABEL_D_DS1, [1] ), ( Node.VALUE_LABEL_D_DS3, [1] ) ]) remapEftNodeValueLabel(eft1, [ 7 ], Node.VALUE_LABEL_D_DS1, [ ( Node.VALUE_LABEL_D_DS3, [] ) ]) - remapEftNodeValueLabel(eft1, [ 8 ], Node.VALUE_LABEL_D_DS1, [ ( Node.VALUE_LABEL_D_DS1, [1] ), ( Node.VALUE_LABEL_D_DS3, []) ]) + remapEftNodeValueLabel(eft1, [ 8 ], Node.VALUE_LABEL_D_DS1, [ ( Node.VALUE_LABEL_D_DS1, [1] ), ( Node.VALUE_LABEL_D_DS3, [1]) ]) ln_map = [ 1, 2, 3, 2, 4, 2, 5, 2 ] remapEftLocalNodes(eft1, 5, ln_map) else: @@ -901,7 +901,7 @@ def generateBaseMesh(cls, region, options): remapEftNodeValueLabel(eft1, [ 1 ], Node.VALUE_LABEL_D_DS2, [ ( Node.VALUE_LABEL_D_DS2, [] ), ( Node.VALUE_LABEL_D_DS3, [] ) ]) remapEftNodeValueLabel(eft1, [ 2, 6 ], Node.VALUE_LABEL_D_DS2, [ ( Node.VALUE_LABEL_D_DS2, [] ), ( Node.VALUE_LABEL_D_DS3, [] ) ]) remapEftNodeValueLabel(eft1, [ 5 ], Node.VALUE_LABEL_D_DS2, [ ( Node.VALUE_LABEL_D_DS1, [1] ), ( Node.VALUE_LABEL_D_DS2, [] ), ( Node.VALUE_LABEL_D_DS3, [] ) ]) - remapEftNodeValueLabel(eft1, [ 7 ], Node.VALUE_LABEL_D_DS2, [ ( Node.VALUE_LABEL_D_DS1, [1] ), ( Node.VALUE_LABEL_D_DS3, []) ]) + remapEftNodeValueLabel(eft1, [ 7 ], Node.VALUE_LABEL_D_DS2, [ ( Node.VALUE_LABEL_D_DS1, [1] ), ( Node.VALUE_LABEL_D_DS3, [1]) ]) elif lv2 == 0: # final element on v septum nids[3] = avsNodeId scaleEftNodeValueLabels(eft1, [ 5 ], [ Node.VALUE_LABEL_D_DS3 ], [ 1 ]) @@ -995,7 +995,7 @@ def generateBaseMesh(cls, region, options): remapEftNodeValueLabel(eft1, [ 4 ], Node.VALUE_LABEL_D_DS2, [ ( Node.VALUE_LABEL_D_DS1, [] ), ( Node.VALUE_LABEL_D_DS2, [] ) ]) remapEftNodeValueLabel(eft1, [ 5 ], Node.VALUE_LABEL_D_DS3, [ ( Node.VALUE_LABEL_D_DS3, [1] ) ]) # GRC cfb d3 should be reversed in future - remapEftNodeValueLabel(eft1, [ 6 ], Node.VALUE_LABEL_D_DS3, [ ( Node.VALUE_LABEL_D_DS1, [] ), ( Node.VALUE_LABEL_D_DS3, [] ) ]) + remapEftNodeValueLabel(eft1, [ 6 ], Node.VALUE_LABEL_D_DS3, [ ( Node.VALUE_LABEL_D_DS1, [] ), ( Node.VALUE_LABEL_D_DS3, [1] ) ]) tricubichermite.setEftLinearDerivative(eft1, [ 3, 7 ], Node.VALUE_LABEL_D_DS3, 3, 7, 1) tricubichermite.setEftLinearDerivative(eft1, [ 4, 8 ], Node.VALUE_LABEL_D_DS3, 4, 8, 1) remapEftNodeValueLabel(eft1, [ 5, 6, 7, 8 ], Node.VALUE_LABEL_D_DS2, []) @@ -1014,7 +1014,7 @@ def generateBaseMesh(cls, region, options): tricubichermite.setEftLinearDerivative(eft1, [ 3, 4, 7, 8 ], Node.VALUE_LABEL_D_DS3, 3, 7, 1) remapEftNodeValueLabel(eft1, [ 5, 6, 7, 8 ], Node.VALUE_LABEL_D_DS1, []) remapEftNodeValueLabel(eft1, [ 5, 6, 7, 8 ], Node.VALUE_LABEL_D_DS2, []) - remapEftNodeValueLabel(eft1, [ 5 ], Node.VALUE_LABEL_D_DS3, [ ( Node.VALUE_LABEL_D_DS1, [] ), ( Node.VALUE_LABEL_D_DS3, [] ) ]) + remapEftNodeValueLabel(eft1, [ 5 ], Node.VALUE_LABEL_D_DS3, [ ( Node.VALUE_LABEL_D_DS1, [] ), ( Node.VALUE_LABEL_D_DS3, [1] ) ]) remapEftNodeValueLabel(eft1, [ 6 ], Node.VALUE_LABEL_D_DS3, [ ( Node.VALUE_LABEL_D_DS2, [] ) ]) ln_map = [ 1, 2, 3, 3, 4, 4, 4, 4 ] remapEftLocalNodes(eft1, 4, ln_map) @@ -1065,9 +1065,8 @@ def generateBaseMesh(cls, region, options): scalefactors = [ -1.0 ] setEftScaleFactorIds(eft1, [1], []) remapEftNodeValueLabel(eft1, [ 1, 2, 3, 4 ], Node.VALUE_LABEL_D_DS2, []) - remapEftNodeValueLabel(eft1, [ 1 ], Node.VALUE_LABEL_D_DS3, [ ( Node.VALUE_LABEL_D_DS1, [] ), ( Node.VALUE_LABEL_D_DS3, [1] ) ]) + remapEftNodeValueLabel(eft1, [ 1, 3 ], Node.VALUE_LABEL_D_DS3, [ ( Node.VALUE_LABEL_D_DS1, [] ), ( Node.VALUE_LABEL_D_DS3, [] ) ]) remapEftNodeValueLabel(eft1, [ 2 ], Node.VALUE_LABEL_D_DS3, [ ( Node.VALUE_LABEL_D_DS2, [1] ) ]) - remapEftNodeValueLabel(eft1, [ 3 ], Node.VALUE_LABEL_D_DS3, [ ( Node.VALUE_LABEL_D_DS1, [] ), ( Node.VALUE_LABEL_D_DS3, [1] ) ]) scaleEftNodeValueLabels(eft1, [ 5, 6 ], [ Node.VALUE_LABEL_D_DS1 ], [ 1 ]) remapEftNodeValueLabel(eft1, [ 5 ], Node.VALUE_LABEL_D_DS3, [ ( Node.VALUE_LABEL_D_DS1, [] ), ( Node.VALUE_LABEL_D_DS2, [1] ), ( Node.VALUE_LABEL_D_DS3, [1] ) ]) remapEftNodeValueLabel(eft1, [ 6 ], Node.VALUE_LABEL_D_DS3, [ ( Node.VALUE_LABEL_D_DS2, [1] ), ( Node.VALUE_LABEL_D_DS3, [1] ) ]) From c5edf66cbb1a84e3e204b7a97bdee974ad30c86e Mon Sep 17 00:00:00 2001 From: Richard Christie Date: Thu, 15 Nov 2018 10:06:21 +1300 Subject: [PATCH 23/25] Add fibrous ring to heart1 --- scaffoldmaker/annotation/annotationgroup.py | 19 +- scaffoldmaker/meshtypes/meshtype_3d_heart1.py | 282 ++++++++++++++++++ .../meshtypes/meshtype_3d_heartatria1.py | 40 ++- .../meshtypes/meshtype_3d_heartventricles1.py | 4 +- .../meshtype_3d_heartventriclesbase1.py | 46 ++- 5 files changed, 369 insertions(+), 22 deletions(-) diff --git a/scaffoldmaker/annotation/annotationgroup.py b/scaffoldmaker/annotation/annotationgroup.py index 5c67205e..96b08fd2 100644 --- a/scaffoldmaker/annotation/annotationgroup.py +++ b/scaffoldmaker/annotation/annotationgroup.py @@ -45,13 +45,23 @@ def getGroup(self): def getFieldElementGroup(self, mesh): ''' :param mesh: The Zinc mesh to manage a sub group of. - :return: The Zinc meshGroup for adding elements of mesh in this AnnotationGroup. + :return: The Zinc element group field for mesh in this AnnotationGroup. ''' elementGroup = self._group.getFieldElementGroup(mesh) if not elementGroup.isValid(): elementGroup = self._group.createFieldElementGroup(mesh) return elementGroup + def getFieldNodeGroup(self, nodeset): + ''' + :param nodeset: The Zinc nodeset to manage a sub group of. + :return: The Zinc node group field for nodeset in this AnnotationGroup. + ''' + nodeGroup = self._group.getFieldNodeGroup(nodeset) + if not nodeGroup.isValid(): + nodeGroup = self._group.createFieldNodeGroup(nodeset) + return nodeGroup + def getMeshGroup(self, mesh): ''' :param mesh: The Zinc mesh to manage a sub group of. @@ -59,6 +69,13 @@ def getMeshGroup(self, mesh): ''' return self.getFieldElementGroup(mesh).getMeshGroup() + def getNodesetGroup(self, nodeset): + ''' + :param nodeset: The Zinc nodeset to manage a sub group of. + :return: The Zinc nodesetGroup for adding nodes from nodeset in this AnnotationGroup. + ''' + return self.getFieldNodeGroup(nodeset).getNodesetGroup() + def addSubelements(self): ''' Call after group is complete and faces have been defined to add faces and diff --git a/scaffoldmaker/meshtypes/meshtype_3d_heart1.py b/scaffoldmaker/meshtypes/meshtype_3d_heart1.py index 09a0de4e..5f33137d 100644 --- a/scaffoldmaker/meshtypes/meshtype_3d_heart1.py +++ b/scaffoldmaker/meshtypes/meshtype_3d_heart1.py @@ -7,8 +7,13 @@ from scaffoldmaker.annotation.annotationgroup import AnnotationGroup, findAnnotationGroupByName from scaffoldmaker.meshtypes.meshtype_3d_heartatria1 import MeshType_3d_heartatria1 from scaffoldmaker.meshtypes.meshtype_3d_heartventriclesbase1 import MeshType_3d_heartventriclesbase1 +from scaffoldmaker.utils.eft_utils import * +from scaffoldmaker.utils.eftfactory_bicubichermitelinear import eftfactory_bicubichermitelinear from scaffoldmaker.utils.zinc_utils import * from scaffoldmaker.utils.meshrefinement import MeshRefinement +from opencmiss.zinc.element import Element +#from opencmiss.zinc.field import Field +#from opencmiss.zinc.node import Node class MeshType_3d_heart1(object): ''' @@ -75,6 +80,10 @@ def generateBaseMesh(cls, region, options): """ # set dependent outer diameter used in atria2 options['Aorta outer plus diameter'] = options['LV outlet inner diameter'] + 2.0*options['LV outlet wall thickness'] + elementsCountAroundAtrialFreeWall = options['Number of elements around atrial free wall'] + elementsCountAroundAtrialSeptum = options['Number of elements around atrial septum'] + elementsCountAroundAtria = elementsCountAroundAtrialFreeWall + elementsCountAroundAtrialSeptum + useCrossDerivatives = False fm = region.getFieldmodule() fm.beginChange() @@ -84,6 +93,258 @@ def generateBaseMesh(cls, region, options): # generate heartventriclesbase2 model and put atria2 on it annotationGroups = MeshType_3d_heartventriclesbase1.generateBaseMesh(region, options) annotationGroups += MeshType_3d_heartatria1.generateBaseMesh(region, options) + lFibrousRingGroup = AnnotationGroup(region, 'left fibrous ring', FMANumber = 77124, lyphID = 'Lyph ID unknown') + rFibrousRingGroup = AnnotationGroup(region, 'right fibrous ring', FMANumber = 77125, lyphID = 'Lyph ID unknown') + annotationGroups += [ lFibrousRingGroup, rFibrousRingGroup ] + + ############## + # Create nodes + ############## + + nodes = fm.findNodesetByFieldDomainType(Field.DOMAIN_TYPE_NODES) + + # discover left and right fibrous ring nodes from ventricles and atria + # because nodes are iterated in identifier order, the lowest and first are on the lv outlet cfb, right and left on lower outer layers + lavInnerNodeId = [ [], [] ] # [n2][n1] + lavOuterNodeId = [ [], [] ] # [n2][n1] + iter = lFibrousRingGroup.getNodesetGroup(nodes).createNodeiterator() + cfbNodeId = iter.next().getIdentifier() + cfbLeftNodeId = iter.next().getIdentifier() + for n1 in range(elementsCountAroundAtria): + lavInnerNodeId[0].append(iter.next().getIdentifier()) + lavOuterNodeId[0].append(cfbNodeId) + lavOuterNodeId[0].append(cfbLeftNodeId) + for n1 in range(elementsCountAroundAtrialFreeWall - 1): + lavOuterNodeId[0].append(iter.next().getIdentifier()) + for n1 in range(elementsCountAroundAtrialSeptum - 1): + lavOuterNodeId[0].append(None) + for n1 in range(elementsCountAroundAtria): + lavInnerNodeId[1].append(iter.next().getIdentifier()) + for n1 in range(elementsCountAroundAtrialFreeWall + 1): + lavOuterNodeId[1].append(iter.next().getIdentifier()) + for n1 in range(elementsCountAroundAtrialSeptum - 1): + lavOuterNodeId[1].append(None) + ravInnerNodeId = [ [], [] ] # [n2][n1] + ravOuterNodeId = [ [], [] ] # [n2][n1] + iter = rFibrousRingGroup.getNodesetGroup(nodes).createNodeiterator() + cfbNodeId = iter.next().getIdentifier() + cfbRightNodeId = iter.next().getIdentifier() + for n1 in range(elementsCountAroundAtria): + ravInnerNodeId[0].append(iter.next().getIdentifier()) + for n1 in range(elementsCountAroundAtrialSeptum - 1): + ravOuterNodeId[0].append(None) + for n1 in range(elementsCountAroundAtrialFreeWall - 1): + ravOuterNodeId[0].append(iter.next().getIdentifier()) + ravOuterNodeId[0].append(cfbRightNodeId) + ravOuterNodeId[0].append(cfbNodeId) + for n1 in range(elementsCountAroundAtria): + ravInnerNodeId[1].append(iter.next().getIdentifier()) + for n1 in range(elementsCountAroundAtrialSeptum - 1): + ravOuterNodeId[1].append(None) + cfbUpperNodeId = iter.next().getIdentifier() # cfb from left will be first + for n1 in range(elementsCountAroundAtrialFreeWall): + ravOuterNodeId[1].append(iter.next().getIdentifier()) + ravOuterNodeId[1].append(cfbUpperNodeId) + + #for n2 in range(2): + # print('n2', n2) + # print('lavInnerNodeId', lavInnerNodeId[n2]) + # print('lavOuterNodeId', lavOuterNodeId[n2]) + # print('ravInnerNodeId', ravInnerNodeId[n2]) + # print('ravOuterNodeId', ravOuterNodeId[n2]) + + ################# + # Create elements + ################# + + mesh = fm.findMeshByDimension(3) + + lFibrousRingMeshGroup = lFibrousRingGroup.getMeshGroup(mesh) + rFibrousRingMeshGroup = rFibrousRingGroup.getMeshGroup(mesh) + + elementIdentifier = startElementIdentifier = getMaximumElementIdentifier(mesh) + 1 + + elementtemplate1 = mesh.createElementtemplate() + elementtemplate1.setElementShapeType(Element.SHAPE_TYPE_CUBE) + + # create fibrous ring elements + + bicubichermitelinear = eftfactory_bicubichermitelinear(mesh, useCrossDerivatives, linearAxis = 2, d_ds1 = Node.VALUE_LABEL_D_DS1, d_ds2 = Node.VALUE_LABEL_D_DS3) + eftFibrousRing = bicubichermitelinear.createEftBasic() + + # left fibrous ring, starting at crux / collapsed posterior interatrial sulcus + for e in range(-1, elementsCountAroundAtrialFreeWall): + eft1 = eftFibrousRing + n1 = e + nids = [ + lavInnerNodeId[0][n1], lavInnerNodeId[0][n1 + 1], lavInnerNodeId[1][n1], lavInnerNodeId[1][n1 + 1], + lavOuterNodeId[0][n1], lavOuterNodeId[0][n1 + 1], lavOuterNodeId[1][n1], lavOuterNodeId[1][n1 + 1]] + scalefactors = None + meshGroups = [ lFibrousRingMeshGroup ] + + if e == -1: + # interatrial groove straddles left and right atria, collapsed to 6 node wedge + nids[0] = ravInnerNodeId[0][-1] + nids[2] = ravInnerNodeId[1][-1] + nids.pop(6) + nids.pop(4) + eft1 = bicubichermitelinear.createEftNoCrossDerivatives() + setEftScaleFactorIds(eft1, [1], []) + scalefactors = [ -1.0 ] + remapEftNodeValueLabel(eft1, [ 1, 3 ], Node.VALUE_LABEL_D_DS1, [ ( Node.VALUE_LABEL_D_DS1, [] ), ( Node.VALUE_LABEL_D_DS3, [] ) ]) + remapEftNodeValueLabel(eft1, [ 2, 4 ], Node.VALUE_LABEL_D_DS1, [ ( Node.VALUE_LABEL_D_DS1, [] ), ( Node.VALUE_LABEL_D_DS3, [1] ) ]) + remapEftNodeValueLabel(eft1, [ 5, 6, 7, 8 ], Node.VALUE_LABEL_D_DS1, []) + # reverse d3 on cfb: + remapEftNodeValueLabel(eft1, [ 5 ], Node.VALUE_LABEL_D_DS3, [ ( Node.VALUE_LABEL_D_DS1, [1] ), ( Node.VALUE_LABEL_D_DS3, [1]) ]) + remapEftNodeValueLabel(eft1, [ 6 ], Node.VALUE_LABEL_D_DS3, [ ( Node.VALUE_LABEL_D_DS1, [0] ), ( Node.VALUE_LABEL_D_DS3, [1]) ]) + remapEftNodeValueLabel(eft1, [ 7 ], Node.VALUE_LABEL_D_DS3, [ ( Node.VALUE_LABEL_D_DS1, [] ), ( Node.VALUE_LABEL_D_DS3, []) ]) + remapEftNodeValueLabel(eft1, [ 8 ], Node.VALUE_LABEL_D_DS3, [ ( Node.VALUE_LABEL_D_DS1, [1] ), ( Node.VALUE_LABEL_D_DS3, []) ]) + ln_map = [ 1, 2, 3, 4, 5, 5, 6, 6 ] + remapEftLocalNodes(eft1, 6, ln_map) + meshGroups += [ rFibrousRingMeshGroup ] + elif e == 0: + # general linear map d3 adjacent to collapsed sulcus + eft1 = bicubichermitelinear.createEftNoCrossDerivatives() + setEftScaleFactorIds(eft1, [1], []) + scalefactors = [ -1.0 ] + # reverse d1, d3 on cfb, left cfb: + scaleEftNodeValueLabels(eft1, [ 6 ], [ Node.VALUE_LABEL_D_DS1, Node.VALUE_LABEL_D_DS3 ], [ 1 ]) + remapEftNodeValueLabel(eft1, [ 5 ], Node.VALUE_LABEL_D_DS1, [ ( Node.VALUE_LABEL_D_DS1, [1] ) ]) + remapEftNodeValueLabel(eft1, [ 5 ], Node.VALUE_LABEL_D_DS3, [ ( Node.VALUE_LABEL_D_DS1, [] ), ( Node.VALUE_LABEL_D_DS3, [1]) ]) + remapEftNodeValueLabel(eft1, [ 7 ], Node.VALUE_LABEL_D_DS3, [ ( Node.VALUE_LABEL_D_DS1, [1] ), ( Node.VALUE_LABEL_D_DS3, []) ]) + elif e == 1: + # reverse d1, d3 on left cfb: + eft1 = bicubichermitelinear.createEftNoCrossDerivatives() + setEftScaleFactorIds(eft1, [1], []) + scalefactors = [ -1.0 ] + remapEftNodeValueLabel(eft1, [ 5 ], Node.VALUE_LABEL_D_DS1, [ ( Node.VALUE_LABEL_D_DS1, [1] ), ( Node.VALUE_LABEL_D_DS3, []) ]) + remapEftNodeValueLabel(eft1, [ 5 ], Node.VALUE_LABEL_D_DS3, [ ( Node.VALUE_LABEL_D_DS3, [1]) ]) + elif e == (elementsCountAroundAtrialFreeWall - 1): + # general linear map d3 adjacent to collapsed sulcus + eft1 = bicubichermitelinear.createEftNoCrossDerivatives() + setEftScaleFactorIds(eft1, [1], []) + scalefactors = [ -1.0 ] + remapEftNodeValueLabel(eft1, [ 6, 8 ], Node.VALUE_LABEL_D_DS3, [ ( Node.VALUE_LABEL_D_DS1, [] ), ( Node.VALUE_LABEL_D_DS3, []) ]) + + result = elementtemplate1.defineField(coordinates, -1, eft1) + element = mesh.createElement(elementIdentifier, elementtemplate1) + result2 = element.setNodesByIdentifier(eft1, nids) + if scalefactors: + result3 = element.setScaleFactors(eft1, scalefactors) + else: + result3 = 7 + #print('create element fibrous ring left', elementIdentifier, result, result2, result3, nids) + elementIdentifier += 1 + + for meshGroup in meshGroups: + meshGroup.addElement(element) + + # right fibrous ring, starting at crux / collapsed posterior interatrial sulcus + ran1FreeWallStart = elementsCountAroundAtrialSeptum - 1 + for e in range(-1, elementsCountAroundAtrialFreeWall): + eft1 = eftFibrousRing + n1 = ran1FreeWallStart + e + nids = [ + ravInnerNodeId[0][n1], ravInnerNodeId[0][n1 + 1], ravInnerNodeId[1][n1], ravInnerNodeId[1][n1 + 1], + ravOuterNodeId[0][n1], ravOuterNodeId[0][n1 + 1], ravOuterNodeId[1][n1], ravOuterNodeId[1][n1 + 1]] + scalefactors = None + meshGroups = [ rFibrousRingMeshGroup ] + + if e == -1: + # interatrial groove straddles left and right atria, collapsed to 6 node wedge + nids[0] = lavInnerNodeId[0][elementsCountAroundAtrialFreeWall] + nids[2] = lavInnerNodeId[1][elementsCountAroundAtrialFreeWall] + nids.pop(6) + nids.pop(4) + eft1 = bicubichermitelinear.createEftNoCrossDerivatives() + setEftScaleFactorIds(eft1, [1], []) + scalefactors = [ -1.0 ] + remapEftNodeValueLabel(eft1, [ 1, 3 ], Node.VALUE_LABEL_D_DS1, [ ( Node.VALUE_LABEL_D_DS1, [] ), ( Node.VALUE_LABEL_D_DS3, [] ) ]) + remapEftNodeValueLabel(eft1, [ 2, 4 ], Node.VALUE_LABEL_D_DS1, [ ( Node.VALUE_LABEL_D_DS1, [] ), ( Node.VALUE_LABEL_D_DS3, [1] ) ]) + remapEftNodeValueLabel(eft1, [ 5, 6, 7, 8 ], Node.VALUE_LABEL_D_DS1, []) + remapEftNodeValueLabel(eft1, [ 5, 7 ], Node.VALUE_LABEL_D_DS3, [ ( Node.VALUE_LABEL_D_DS1, [] ), ( Node.VALUE_LABEL_D_DS3, []) ]) + remapEftNodeValueLabel(eft1, [ 6, 8 ], Node.VALUE_LABEL_D_DS3, [ ( Node.VALUE_LABEL_D_DS1, [1] ), ( Node.VALUE_LABEL_D_DS3, []) ]) + ln_map = [ 1, 2, 3, 4, 5, 5, 6, 6 ] + remapEftLocalNodes(eft1, 6, ln_map) + meshGroups += [ lFibrousRingMeshGroup ] + elif e == 0: + # general linear map d3 adjacent to collapsed crux/posterior sulcus + eft1 = bicubichermitelinear.createEftNoCrossDerivatives() + setEftScaleFactorIds(eft1, [1], []) + scalefactors = [ -1.0 ] + remapEftNodeValueLabel(eft1, [ 5, 7 ], Node.VALUE_LABEL_D_DS3, [ ( Node.VALUE_LABEL_D_DS1, [1] ), ( Node.VALUE_LABEL_D_DS3, []) ]) + elif e == (elementsCountAroundAtrialFreeWall - 2): + # reverse d1, d3 on right cfb: + eft1 = bicubichermitelinear.createEftNoCrossDerivatives() + setEftScaleFactorIds(eft1, [1], []) + scalefactors = [ -1.0 ] + remapEftNodeValueLabel(eft1, [ 6 ], Node.VALUE_LABEL_D_DS1, [ ( Node.VALUE_LABEL_D_DS1, [1] ), ( Node.VALUE_LABEL_D_DS3, [1]) ]) + remapEftNodeValueLabel(eft1, [ 6 ], Node.VALUE_LABEL_D_DS3, [ ( Node.VALUE_LABEL_D_DS3, [1]) ]) + elif e == (elementsCountAroundAtrialFreeWall - 1): + # general linear map d3 adjacent to collapsed cfb/anterior sulcus + eft1 = bicubichermitelinear.createEftNoCrossDerivatives() + setEftScaleFactorIds(eft1, [1], []) + scalefactors = [ -1.0 ] + # reverse d1, d3 on right cfb, cfb: + scaleEftNodeValueLabels(eft1, [ 5 ], [ Node.VALUE_LABEL_D_DS1, Node.VALUE_LABEL_D_DS3 ], [ 1 ]) + remapEftNodeValueLabel(eft1, [ 6 ], Node.VALUE_LABEL_D_DS1, [ ( Node.VALUE_LABEL_D_DS1, [1] ) ]) + remapEftNodeValueLabel(eft1, [ 6 ], Node.VALUE_LABEL_D_DS3, [ ( Node.VALUE_LABEL_D_DS1, [1] ), ( Node.VALUE_LABEL_D_DS3, [1]) ]) + remapEftNodeValueLabel(eft1, [ 8 ], Node.VALUE_LABEL_D_DS3, [ ( Node.VALUE_LABEL_D_DS1, [] ), ( Node.VALUE_LABEL_D_DS3, []) ]) + + result = elementtemplate1.defineField(coordinates, -1, eft1) + element = mesh.createElement(elementIdentifier, elementtemplate1) + result2 = element.setNodesByIdentifier(eft1, nids) + if scalefactors: + result3 = element.setScaleFactors(eft1, scalefactors) + else: + result3 = 7 + #print('create element fibrous ring right', elementIdentifier, result, result2, result3, nids) + elementIdentifier += 1 + + for meshGroup in meshGroups: + meshGroup.addElement(element) + + # fibrous ring septum: + meshGroups = [ lFibrousRingMeshGroup, rFibrousRingMeshGroup ] + for e in range(elementsCountAroundAtrialSeptum): + eft1 = eftFibrousRing + nlm = elementsCountAroundAtrialFreeWall + e + nlp = (nlm + 1)%elementsCountAroundAtria + nrm = elementsCountAroundAtrialSeptum - 1 - e + nrp = nrm - 1 + nids = [ lavInnerNodeId[0][nlm], lavInnerNodeId[0][nlp], lavInnerNodeId[1][nlm], lavInnerNodeId[1][nlp], + ravInnerNodeId[0][nrm], ravInnerNodeId[0][nrp], ravInnerNodeId[1][nrm], ravInnerNodeId[1][nrp] ] + + eft1 = bicubichermitelinear.createEftNoCrossDerivatives() + setEftScaleFactorIds(eft1, [1], []) + scalefactors = [ -1.0 ] + if e == 0: + # general linear map d3 adjacent to collapsed posterior interventricular sulcus + scaleEftNodeValueLabels(eft1, [ 5, 6, 7, 8 ], [ Node.VALUE_LABEL_D_DS1 ], [ 1 ]) + scaleEftNodeValueLabels(eft1, [ 6, 8 ], [ Node.VALUE_LABEL_D_DS3 ], [ 1 ]) + remapEftNodeValueLabel(eft1, [ 1, 3 ], Node.VALUE_LABEL_D_DS3, [ ( Node.VALUE_LABEL_D_DS1, [] ), ( Node.VALUE_LABEL_D_DS3, [] ) ]) + remapEftNodeValueLabel(eft1, [ 5, 7 ], Node.VALUE_LABEL_D_DS3, [ ( Node.VALUE_LABEL_D_DS1, [] ), ( Node.VALUE_LABEL_D_DS3, [1] ) ]) + elif e == (elementsCountAroundAtrialSeptum - 1): + # general linear map d3 adjacent to cfb + scaleEftNodeValueLabels(eft1, [ 5, 6, 7, 8 ], [ Node.VALUE_LABEL_D_DS1 ], [ 1 ]) + scaleEftNodeValueLabels(eft1, [ 5, 7 ], [ Node.VALUE_LABEL_D_DS3 ], [ 1 ]) + remapEftNodeValueLabel(eft1, [ 2, 4 ], Node.VALUE_LABEL_D_DS3, [ ( Node.VALUE_LABEL_D_DS1, [1] ), ( Node.VALUE_LABEL_D_DS3, [] ) ]) + remapEftNodeValueLabel(eft1, [ 6, 8 ], Node.VALUE_LABEL_D_DS3, [ ( Node.VALUE_LABEL_D_DS1, [1] ), ( Node.VALUE_LABEL_D_DS3, [1] ) ]) + else: + scaleEftNodeValueLabels(eft1, [ 5, 6, 7, 8 ], [ Node.VALUE_LABEL_D_DS1, Node.VALUE_LABEL_D_DS3 ], [ 1 ]) + + result = elementtemplate1.defineField(coordinates, -1, eft1) + element = mesh.createElement(elementIdentifier, elementtemplate1) + result2 = element.setNodesByIdentifier(eft1, nids) + if scalefactors: + result3 = element.setScaleFactors(eft1, scalefactors) + else: + result3 = 7 + #print('create element fibrous ring septum', elementIdentifier, result, result2, result3, nids) + elementIdentifier += 1 + + for meshGroup in meshGroups: + meshGroup.addElement(element) fm.endChange() return annotationGroups @@ -96,8 +357,29 @@ def refineMesh(cls, meshrefinement, options): :param options: Dict containing options. See getDefaultOptions(). """ assert isinstance(meshrefinement, MeshRefinement) + elementsCountAroundAtrialFreeWall = options['Number of elements around atrial free wall'] + elementsCountAroundAtrialSeptum = options['Number of elements around atrial septum'] + elementsCountAroundAtria = elementsCountAroundAtrialFreeWall + elementsCountAroundAtrialSeptum + refineElementsCountSurface = options['Refine number of elements surface'] + refineElementsCountThroughWall = options['Refine number of elements through wall'] MeshType_3d_heartventriclesbase1.refineMesh(meshrefinement, options) MeshType_3d_heartatria1.refineMesh(meshrefinement, options) + element = meshrefinement._sourceElementiterator.next() + startFibrousRingElementIdentifier = element.getIdentifier() + lastFibrousRingElementIdentifier = startFibrousRingElementIdentifier + 2*elementsCountAroundAtrialFreeWall + elementsCountAroundAtrialSeptum + 1 + i = -1 + while element.isValid(): + numberInXi1 = refineElementsCountSurface + numberInXi2 = 1 # since fibrous ring is thin + numberInXi3 = refineElementsCountThroughWall + elementIdentifier = element.getIdentifier() + if i in [ -1, elementsCountAroundAtrialFreeWall ]: + numberInXi1 = refineElementsCountThroughWall + meshrefinement.refineElementCubeStandard3d(element, numberInXi1, numberInXi2, numberInXi3) + if elementIdentifier == lastFibrousRingElementIdentifier: + return # finish on last so can continue elsewhere + element = meshrefinement._sourceElementiterator.next() + i += 1 @classmethod def generateMesh(cls, region, options): diff --git a/scaffoldmaker/meshtypes/meshtype_3d_heartatria1.py b/scaffoldmaker/meshtypes/meshtype_3d_heartatria1.py index e9d1e573..2a2a975c 100644 --- a/scaffoldmaker/meshtypes/meshtype_3d_heartatria1.py +++ b/scaffoldmaker/meshtypes/meshtype_3d_heartatria1.py @@ -247,12 +247,16 @@ def generateBaseMesh(cls, region, options): ivcInletGroup = AnnotationGroup(region, 'inferior vena cava inlet', FMANumber = 10951, lyphID = 'Lyph ID unknown') svcInletGroup = AnnotationGroup(region, 'superior vena cava inlet', FMANumber = 4720, lyphID = 'Lyph ID unknown') annotationGroups = [ laGroup, raGroup, aSeptumGroup, fossaGroup, lipvGroup, lspvGroup, ripvGroup, rspvGroup, ivcInletGroup, svcInletGroup ] + # av boundary nodes are put in left and right fibrous ring groups only so they can be found by heart1 + lFibrousRingGroup = AnnotationGroup(region, 'left fibrous ring', FMANumber = 77124, lyphID = 'Lyph ID unknown') + rFibrousRingGroup = AnnotationGroup(region, 'right fibrous ring', FMANumber = 77125, lyphID = 'Lyph ID unknown') ############## # Create nodes ############## nodes = fm.findNodesetByFieldDomainType(Field.DOMAIN_TYPE_NODES) + nodetemplate = nodes.createNodetemplate() nodetemplate.defineField(coordinates) nodetemplate.setValueNumberOfVersions(coordinates, -1, Node.VALUE_LABEL_VALUE, 1) @@ -746,6 +750,17 @@ def generateBaseMesh(cls, region, options): aNodeId[n1] = aNodeId[ran1FreeWallStart + elementsCountRidgeVenous] raNodeId[n3].append(aNodeId) + # add nodes to left/right fibrous ring groups so heart1 can find them + for i in range(2): + fibrousRingGroup = lFibrousRingGroup if (i == 0) else rFibrousRingGroup + fibrousRingNodesetGroup = fibrousRingGroup.getNodesetGroup(nodes) + for n3 in range(2): + aNodeId = laNodeId[n3][0] if (i == 0) else raNodeId[n3][0] + for n1 in range(elementsCountAroundAtria): + if aNodeId[n1]: + node = nodes.findNodeByIdentifier(aNodeId[n1]) + fibrousRingNodesetGroup.addNode(node) + # create fossa ovalis nodes fossaCentreNodeId = [] fossaNodeId = [ [], [] ] @@ -822,10 +837,9 @@ def generateBaseMesh(cls, region, options): if (e1 >= rspve1min) and (e1 <= rspve1max) and (e2 >= rspve2min) and (e2 <= rspve2max): continue # rspv inlet location if e1 == -1: - # cfb/anterior interatrial groove straddles left and right atria + # cfb/anterior interatrial groove straddles left and right atria, collapsed to 6 node wedge nids[0] = raNodeId[0][e2][-1] nids[2] = raNodeId[0][e2 + 1][-1] - # collapsed to 6 element wedge nids.pop(6) nids.pop(4) meshGroups += [ raMeshGroup ] @@ -912,10 +926,9 @@ def generateBaseMesh(cls, region, options): if (e1 >= svce1min) and (e1 <= svce1max) and (e2 >= svce2min) and (e2 <= svce2max): continue # svc inlet location if e1 == -1: - # crux/posterior interatrial groove straddles left and right atria + # crux/posterior interatrial groove straddles left and right atria, collapsed to 6 node wedge nids[0] = laNodeId[0][e2][elementsCountAroundAtrialFreeWall] nids[2] = laNodeId[0][e2 + 1][elementsCountAroundAtrialFreeWall] - # collapsed to 6 element wedge nids.pop(6) nids.pop(4) meshGroups += [ laMeshGroup ] @@ -1655,11 +1668,15 @@ def refineMesh(cls, meshrefinement, options): :param options: Dict containing options. See getDefaultOptions(). """ assert isinstance(meshrefinement, MeshRefinement) + elementsCountAroundAtrialFreeWall = options['Number of elements around atrial free wall'] + elementsCountAroundAtrialSeptum = options['Number of elements around atrial septum'] + elementsCountAroundAtria = elementsCountAroundAtrialFreeWall + elementsCountAroundAtrialSeptum + elementsCountUpAtria = options['Number of elements up atria'] + elementsCountInlet = options['Number of elements inlet'] refineElementsCountSurface = options['Refine number of elements surface'] refineElementsCountThroughWall = options['Refine number of elements through wall'] element = meshrefinement._sourceElementiterator.next() sourceFm = meshrefinement._sourceFm - coordinates = getOrCreateCoordinateField(sourceFm) annotationGroups = meshrefinement._sourceAnnotationGroups laGroup = findAnnotationGroupByName(annotationGroups, 'left atrium') laElementGroupField = laGroup.getFieldElementGroup(meshrefinement._sourceMesh) @@ -1668,11 +1685,22 @@ def refineMesh(cls, meshrefinement, options): aSeptumGroup = findAnnotationGroupByName(annotationGroups, 'interatrial septum') aSeptumElementGroupField = aSeptumGroup.getFieldElementGroup(meshrefinement._sourceMesh) isSeptumEdgeWedge = sourceFm.createFieldXor(sourceFm.createFieldAnd(laElementGroupField, raElementGroupField), aSeptumElementGroupField) + # last atria element is last element in svc inlet group: + svcInletGroup = findAnnotationGroupByName(annotationGroups, 'superior vena cava inlet') + svcInletMeshGroup = svcInletGroup.getMeshGroup(meshrefinement._sourceMesh) + lastSvcInletElementIdentifier = -1 + elementIter = svcInletMeshGroup.createElementiterator() + tmpElement = elementIter.next() + while tmpElement.isValid(): + lastSvcInletElementIdentifier = tmpElement.getIdentifier() + tmpElement = elementIter.next() + cache = sourceFm.createFieldcache() refineElements2 = refineElementsCountSurface refineElements3 = refineElementsCountThroughWall while element.isValid(): + elementIdentifier = element.getIdentifier() cache.setElement(element) result, isWedge = isSeptumEdgeWedge.evaluateReal(cache, 1) if isWedge: @@ -1680,6 +1708,8 @@ def refineMesh(cls, meshrefinement, options): else: refineElements1 = refineElementsCountSurface meshrefinement.refineElementCubeStandard3d(element, refineElements1, refineElements2, refineElements3) + if elementIdentifier == lastSvcInletElementIdentifier: + return # finish on last so can continue elsewhere element = meshrefinement._sourceElementiterator.next() @classmethod diff --git a/scaffoldmaker/meshtypes/meshtype_3d_heartventricles1.py b/scaffoldmaker/meshtypes/meshtype_3d_heartventricles1.py index b8e3d01e..942bc2e6 100644 --- a/scaffoldmaker/meshtypes/meshtype_3d_heartventricles1.py +++ b/scaffoldmaker/meshtypes/meshtype_3d_heartventricles1.py @@ -706,7 +706,7 @@ def generateBaseMesh(cls, region, options): nids = [ lvInnerNodeId[e2 - 1][va], lvInnerNodeId[e2 - 1][vb], lvInnerNodeId[e2][va], lvInnerNodeId[e2][vb], vOuterNodeId [e2 - 1][va], vOuterNodeId [e2 - 1][vb], vOuterNodeId [e2][va], vOuterNodeId [e2][vb] ] if e1 == -1: - # anterior interventricular sulcus: collapsed to 6 element wedge + # anterior interventricular sulcus, collapsed to 6 node wedge nids[0] = rvInnerNodeId[e2 - 1][elementsCountAroundRVFreeWall] nids[2] = rvInnerNodeId[e2 ][elementsCountAroundRVFreeWall] nids.pop(6) @@ -794,7 +794,7 @@ def generateBaseMesh(cls, region, options): ln_map = [ 1, 2, 3, 4, 5, 6, 5, 6 ] remapEftLocalNodes(eft1, 6, ln_map) elif e1 == -1: - # posterior interventricular sulcus: collapsed to 6 element wedge + # posterior interventricular sulcus, collapsed to 6 node wedge nids[0] = lvInnerNodeId[e2 - 1][elementsCountAroundLVFreeWall] nids[2] = lvInnerNodeId[e2 ][elementsCountAroundLVFreeWall] nids.pop(6) diff --git a/scaffoldmaker/meshtypes/meshtype_3d_heartventriclesbase1.py b/scaffoldmaker/meshtypes/meshtype_3d_heartventriclesbase1.py index 69fd14ec..478b968d 100644 --- a/scaffoldmaker/meshtypes/meshtype_3d_heartventriclesbase1.py +++ b/scaffoldmaker/meshtypes/meshtype_3d_heartventriclesbase1.py @@ -52,7 +52,7 @@ def getDefaultOptions(): options['Atrial base slope degrees'] = 30.0 options['Base height'] = 0.15 options['Base thickness'] = 0.06 - options['Fibrous ring thickness'] = 0.01 + options['Fibrous ring thickness'] = 0.005 options['LV outlet front incline degrees'] = 15.0 options['LV outlet inner diameter'] = 0.3 options['LV outlet wall thickness'] = 0.025 @@ -207,6 +207,11 @@ def generateBaseMesh(cls, region, options): vTranslationy = options['Ventricles translation y'] useCrossDerivatives = False + fm = region.getFieldmodule() + fm.beginChange() + coordinates = getOrCreateCoordinateField(fm) + cache = fm.createFieldcache() + # generate heartventricles1 model to add base plane to annotationGroups = MeshType_3d_heartventricles1.generateBaseMesh(region, options) @@ -215,20 +220,17 @@ def generateBaseMesh(cls, region, options): rvGroup = findAnnotationGroupByName(annotationGroups, 'right ventricle') vSeptumGroup = findAnnotationGroupByName(annotationGroups, 'interventricular septum') conusArteriosusGroup = AnnotationGroup(region, 'conus arteriosus', FMANumber = 0, lyphID = 'Lyph ID unknown') + annotationGroups += [ conusArteriosusGroup ] + # av boundary nodes are put in left and right fibrous ring groups only so they can be found by heart1 lFibrousRingGroup = AnnotationGroup(region, 'left fibrous ring', FMANumber = 77124, lyphID = 'Lyph ID unknown') rFibrousRingGroup = AnnotationGroup(region, 'right fibrous ring', FMANumber = 77125, lyphID = 'Lyph ID unknown') - annotationGroups += [ conusArteriosusGroup, lFibrousRingGroup, rFibrousRingGroup ] - - fm = region.getFieldmodule() - fm.beginChange() - coordinates = getOrCreateCoordinateField(fm) - cache = fm.createFieldcache() ################# # Create nodes ################# nodes = fm.findNodesetByFieldDomainType(Field.DOMAIN_TYPE_NODES) + nodetemplate = nodes.createNodetemplate() nodetemplate.defineField(coordinates) nodetemplate.setValueNumberOfVersions(coordinates, -1, Node.VALUE_LABEL_VALUE, 1) @@ -568,10 +570,14 @@ def generateBaseMesh(cls, region, options): avNodeId.append(None) continue if n3 == 1: - if (n2 == 0) and (((i == 0) and (n1 <= 1)) or ((i == 1) and (n1 >= (elementsCountAroundAtria - 2)))): + if n2 == 0: # substitute LV outlet node around cfb / fibrous trigones - avNodeId.append(lvOutletNodeId[1][0] if (n1 == 0) else (lvOutletNodeId[1][-1] if (n1 == 1) else lvOutletNodeId[1][1])) - continue + if (i == 0) and (n1 <= 1): + avNodeId.append(lvOutletNodeId[1][0] if (n1 == 0) else lvOutletNodeId[1][-1]) + continue + elif (i == 1) and (n1 >= (elementsCountAroundAtria - 2)): + avNodeId.append(lvOutletNodeId[1][0] if (n1 == (elementsCountAroundAtria - 1)) else lvOutletNodeId[1][1]) + continue if (i == 1) and ((n1 == (elementsCountAroundAtrialSeptum - 1)) or (n1 == (elementsCountAroundAtria - 1))): # find common nodes on right at cfb and crux avNodeId.append(lavOuterNodeId[n2][elementsCountAroundAtria - 1 - n1]) @@ -585,6 +591,20 @@ def generateBaseMesh(cls, region, options): coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS3, 1, avd3[n1]) nodeIdentifier += 1 + # add nodes to left/right fibrous ring groups so heart1 can find them + for i in range(2): + fibrousRingGroup = lFibrousRingGroup if (i == 0) else rFibrousRingGroup + fibrousRingNodesetGroup = fibrousRingGroup.getNodesetGroup(nodes) + for n3 in range(2): + if n3 == 0: + avNodeId = lavInnerNodeId[0] if (i == 0) else ravInnerNodeId[0] + else: + avNodeId = lavOuterNodeId[0] if (i == 0) else ravOuterNodeId[0] + for n1 in range(elementsCountAroundAtria): + if avNodeId[n1]: + node = nodes.findNodeByIdentifier(avNodeId[n1]) + fibrousRingNodesetGroup.addNode(node) + # nodes on bottom and top of RV supraventricular crest svciNodeId = nodeIdentifier node = nodes.createNode(nodeIdentifier, nodetemplate) @@ -613,8 +633,6 @@ def generateBaseMesh(cls, region, options): rvMeshGroup = rvGroup.getMeshGroup(mesh) vSeptumMeshGroup = vSeptumGroup.getMeshGroup(mesh) conusArteriosusMeshGroup = conusArteriosusGroup.getMeshGroup(mesh) - lFibrousRingMeshGroup = lFibrousRingGroup.getMeshGroup(mesh) - rFibrousRingMeshGroup = rFibrousRingGroup.getMeshGroup(mesh) tricubichermite = eftfactory_tricubichermite(mesh, useCrossDerivatives) eft = tricubichermite.createEftNoCrossDerivatives() @@ -708,7 +726,7 @@ def generateBaseMesh(cls, region, options): nov = elementsCountAroundLVFreeWall + niv novp = (nov + 1)%elementsCountAroundLV if e == -1: - # crux / posterior interventricular sulcus: collapsed to 6 element wedge + # crux / posterior interventricular sulcus, collapsed to 6 node wedge nids = [ lvInnerNodeId[elementsCountAroundLVFreeWall], rvInnerNodeId[nivp], lavInnerNodeId[0][elementsCountAroundAtrialFreeWall], ravInnerNodeId[0][noa + 1], vOuterNodeId[novp], ravOuterNodeId[0][noa + 1] ] eft1 = tricubichermite.createEftNoCrossDerivatives() @@ -1043,7 +1061,7 @@ def generateBaseMesh(cls, region, options): result3 = element.setScaleFactors(eft1, scalefactors) else: result3 = 7 - print('create element lv base r2', elementIdentifier, result, result2, result3, nids) + #print('create element lv base r2', elementIdentifier, result, result2, result3, nids) elementIdentifier += 1 for meshGroup in meshGroups: From be87d3b4e87631147385791051208c95c7459a24 Mon Sep 17 00:00:00 2001 From: Richard Christie Date: Thu, 15 Nov 2018 13:56:52 +1300 Subject: [PATCH 24/25] Return whether check options changed dependencies --- scaffoldmaker/meshtypes/meshtype_3d_heart1.py | 11 ++++--- scaffoldmaker/meshtypes/meshtype_3d_heart2.py | 5 +-- .../meshtypes/meshtype_3d_heartatria1.py | 5 +++ .../meshtypes/meshtype_3d_heartatria2.py | 10 ++++++ .../meshtypes/meshtype_3d_heartventricles1.py | 5 +++ .../meshtypes/meshtype_3d_heartventricles2.py | 5 +++ .../meshtype_3d_heartventriclesbase1.py | 33 ++++++++++++------- .../meshtype_3d_heartventriclesbase2.py | 11 +++++-- 8 files changed, 65 insertions(+), 20 deletions(-) diff --git a/scaffoldmaker/meshtypes/meshtype_3d_heart1.py b/scaffoldmaker/meshtypes/meshtype_3d_heart1.py index 5f33137d..6183e95e 100644 --- a/scaffoldmaker/meshtypes/meshtype_3d_heart1.py +++ b/scaffoldmaker/meshtypes/meshtype_3d_heart1.py @@ -63,12 +63,15 @@ def getOrderedOptionNames(): @staticmethod def checkOptions(options): - MeshType_3d_heartventriclesbase1.checkOptions(options) - MeshType_3d_heartatria1.checkOptions(options) - # only works with particular numbers of elements around - #options['Number of elements around atrial septum'] = 2 + ''' + :return: True if dependent options changed, otherwise False. This + happens where two or more options must change together to be valid. + ''' + dependentChanges = MeshType_3d_heartventriclesbase1.checkOptions(options) \ + or MeshType_3d_heartatria1.checkOptions(options) # set dependent outer diameter used in atria2 options['Aorta outer plus diameter'] = options['LV outlet inner diameter'] + 2.0*options['LV outlet wall thickness'] + return dependentChanges @classmethod def generateBaseMesh(cls, region, options): diff --git a/scaffoldmaker/meshtypes/meshtype_3d_heart2.py b/scaffoldmaker/meshtypes/meshtype_3d_heart2.py index 85d1952a..59c6c79b 100644 --- a/scaffoldmaker/meshtypes/meshtype_3d_heart2.py +++ b/scaffoldmaker/meshtypes/meshtype_3d_heart2.py @@ -56,12 +56,13 @@ def getOrderedOptionNames(): @staticmethod def checkOptions(options): - MeshType_3d_heartventriclesbase2.checkOptions(options) - MeshType_3d_heartatria2.checkOptions(options) + dependentChanges = MeshType_3d_heartventriclesbase2.checkOptions(options) \ + or MeshType_3d_heartatria2.checkOptions(options) # only works with particular numbers of elements around options['Number of elements around atrial septum'] = 2 # set dependent outer diameter used in atria2 options['LV outlet outer diameter'] = options['LV outlet inner diameter'] + 2.0*options['LV outlet wall thickness'] + return dependentChanges @classmethod def generateBaseMesh(cls, region, options): diff --git a/scaffoldmaker/meshtypes/meshtype_3d_heartatria1.py b/scaffoldmaker/meshtypes/meshtype_3d_heartatria1.py index 2a2a975c..5283ad71 100644 --- a/scaffoldmaker/meshtypes/meshtype_3d_heartatria1.py +++ b/scaffoldmaker/meshtypes/meshtype_3d_heartatria1.py @@ -121,6 +121,10 @@ def getOrderedOptionNames(): @staticmethod def checkOptions(options): + ''' + :return: True if dependent options changed, otherwise False. + ''' + dependentChanges = False if options['Number of elements around atrial free wall'] < 6: options['Number of elements around atrial free wall'] = 6 # need even number of elements around free wall @@ -180,6 +184,7 @@ def checkOptions(options): 'Refine number of elements through wall']: if options[key] < 1: options[key] = 1 + return dependentChanges @classmethod def generateBaseMesh(cls, region, options): diff --git a/scaffoldmaker/meshtypes/meshtype_3d_heartatria2.py b/scaffoldmaker/meshtypes/meshtype_3d_heartatria2.py index 7517fe44..491ce532 100644 --- a/scaffoldmaker/meshtypes/meshtype_3d_heartatria2.py +++ b/scaffoldmaker/meshtypes/meshtype_3d_heartatria2.py @@ -89,6 +89,14 @@ def getOrderedOptionNames(): @staticmethod def checkOptions(options): + ''' + :return: True if dependent options changed, otherwise False. This + happens where two or more options must change together to be valid. + Here the number of elements around atrial free wall must be an even number, + but the parameter is number of elements around atria, which depends on + number of elements around atrial septum. + ''' + dependentChanges = False if options['Number of elements around atria'] < 6: options['Number of elements around atria'] = 6 if options['Number of elements around atrial septum'] < 1: @@ -98,6 +106,7 @@ def checkOptions(options): # need even number of elements around free wall if ((options['Number of elements around atria'] - options['Number of elements around atrial septum']) % 2) == 1: options['Number of elements around atria'] += 1 + dependentChanges = True if options['Number of elements up atria'] < 3: options['Number of elements up atria'] = 3 for key in [ @@ -138,6 +147,7 @@ def checkOptions(options): 'Refine number of elements through atrial wall']: if options[key] < 1: options[key] = 1 + return dependentChanges @classmethod def generateBaseMesh(cls, region, options): diff --git a/scaffoldmaker/meshtypes/meshtype_3d_heartventricles1.py b/scaffoldmaker/meshtypes/meshtype_3d_heartventricles1.py index 942bc2e6..c4f10e0b 100644 --- a/scaffoldmaker/meshtypes/meshtype_3d_heartventricles1.py +++ b/scaffoldmaker/meshtypes/meshtype_3d_heartventricles1.py @@ -86,6 +86,10 @@ def getOrderedOptionNames(): @staticmethod def checkOptions(options): + ''' + :return: True if dependent options changed, otherwise False. + ''' + dependentChanges = False for key in [ 'Refine number of elements surface', 'Refine number of elements through LV wall', @@ -132,6 +136,7 @@ def checkOptions(options): options[key] = 0.1 elif options[key] > 1.0: options[key] = 1.0 + return dependentChanges @classmethod def generateBaseMesh(cls, region, options): diff --git a/scaffoldmaker/meshtypes/meshtype_3d_heartventricles2.py b/scaffoldmaker/meshtypes/meshtype_3d_heartventricles2.py index ddd94e6e..9ec57d33 100644 --- a/scaffoldmaker/meshtypes/meshtype_3d_heartventricles2.py +++ b/scaffoldmaker/meshtypes/meshtype_3d_heartventricles2.py @@ -77,6 +77,10 @@ def getOrderedOptionNames(): @staticmethod def checkOptions(options): + ''' + :return: True if dependent options changed, otherwise False. + ''' + dependentChanges = False for key in [ 'Refine number of elements surface', 'Refine number of elements through LV wall', @@ -112,6 +116,7 @@ def checkOptions(options): options['RV arc around degrees'] = 45.0 elif options['RV arc around degrees'] > 270.0: options['RV arc around degrees'] = 270.0 + return dependentChanges @classmethod def generateBaseMesh(cls, region, options): diff --git a/scaffoldmaker/meshtypes/meshtype_3d_heartventriclesbase1.py b/scaffoldmaker/meshtypes/meshtype_3d_heartventriclesbase1.py index 478b968d..304b5a30 100644 --- a/scaffoldmaker/meshtypes/meshtype_3d_heartventriclesbase1.py +++ b/scaffoldmaker/meshtypes/meshtype_3d_heartventriclesbase1.py @@ -106,20 +106,28 @@ def getOrderedOptionNames(): @staticmethod def checkOptions(options): - MeshType_3d_heartventricles1.checkOptions(options) + ''' + :return: True if dependent options changed, otherwise False. This + happens where two or more options must change together to be valid. + Here the number of elements around LV free wall is dependent on + number of elements around atrial free wall. + ''' + dependentChanges = MeshType_3d_heartventricles1.checkOptions(options) # only works with particular numbers of elements around - #options['Number of elements around LV free wall'] = 5 - #options['Number of elements around RV free wall'] = 7 - #options['Number of elements around atrial free wall'] = 8 - #options['Number of elements around atrial septum'] = 2 - # while editing, restrict to limitations of atria: - if options['Number of elements around atrial free wall'] < 6: + options['Number of elements around RV free wall'] = 7 + # Supports only 6 or 8 elements around atrial free wall: + if options['Number of elements around atrial free wall'] <= 6: options['Number of elements around atrial free wall'] = 6 - # need even number of elements around free wall - if (options['Number of elements around atrial free wall'] % 2) == 1: - options['Number of elements around atrial free wall'] += 1 - if options['Number of elements around atrial septum'] < 2: - options['Number of elements around atrial septum'] = 2 + requiredElementsCountAroundLVFreeWall = 5 + else: + options['Number of elements around atrial free wall'] = 8 + requiredElementsCountAroundLVFreeWall = 7 + if options['Number of elements around LV free wall'] != requiredElementsCountAroundLVFreeWall: + options['Number of elements around LV free wall'] = requiredElementsCountAroundLVFreeWall + dependentChanges = True + #if options['Number of elements around atrial septum'] < 2: + # options['Number of elements around atrial septum'] = 2 + options['Number of elements around atrial septum'] = 3 for key in [ 'Atria base inner major axis length', 'Atria base inner minor axis length', @@ -142,6 +150,7 @@ def checkOptions(options): options['Atria major axis rotation degrees'] = -75.0 elif options['Atria major axis rotation degrees'] > 75.0: options['Atria major axis rotation degrees'] = 75.0 + return dependentChanges @classmethod def generateBaseMesh(cls, region, options): diff --git a/scaffoldmaker/meshtypes/meshtype_3d_heartventriclesbase2.py b/scaffoldmaker/meshtypes/meshtype_3d_heartventriclesbase2.py index ff954858..b95669e2 100644 --- a/scaffoldmaker/meshtypes/meshtype_3d_heartventriclesbase2.py +++ b/scaffoldmaker/meshtypes/meshtype_3d_heartventriclesbase2.py @@ -38,6 +38,7 @@ def getDefaultOptions(): options['Number of elements around LV free wall'] = 5 options['Number of elements around ventricular septum'] = 7 options['Number of elements around atria'] = 8 + options['Number of elements around atrial septum'] = 2 # works best with particular numbers of elements up options['Number of elements up LV apex'] = 1 options['Number of elements up ventricular septum'] = 4 @@ -64,6 +65,7 @@ def getDefaultOptions(): def getOrderedOptionNames(): optionNames = MeshType_3d_heartventricles2.getOrderedOptionNames() optionNames.insert(4, 'Number of elements around atria') + optionNames.insert(5, 'Number of elements around atrial septum') optionNames += [ 'Atria base inner major axis length', 'Atria base inner minor axis length', @@ -93,11 +95,15 @@ def getOrderedOptionNames(): @staticmethod def checkOptions(options): - MeshType_3d_heartventricles2.checkOptions(options) + ''' + :return: True if dependent options changed, otherwise False. + ''' + dependentChanges = MeshType_3d_heartventricles2.checkOptions(options) # only works with particular numbers of elements around options['Number of elements around LV free wall'] = 5 options['Number of elements around ventricular septum'] = 7 options['Number of elements around atria'] = 8 + options['Number of elements around atrial septum'] = 2 for key in [ 'Atria base inner major axis length', 'Atria base inner minor axis length', @@ -119,6 +125,7 @@ def checkOptions(options): options['Atria major axis rotation degrees'] = -75.0 elif options['Atria major axis rotation degrees'] > 75.0: options['Atria major axis rotation degrees'] = 75.0 + return dependentChanges @classmethod def generateBaseMesh(cls, region, options): @@ -136,8 +143,8 @@ def generateBaseMesh(cls, region, options): elementsCountUpLV = elementsCountUpLVApex + elementsCountUpVSeptum elementsCountUpRV = elementsCountUpVSeptum + 1 elementsCountAroundRV = elementsCountAroundVSeptum + 2 - elementsCountAtrialSeptum = 2 # elementsCountAroundVSeptum - 5 elementsCountAroundAtria = options['Number of elements around atria'] + elementsCountAtrialSeptum = options['Number of elements around atrial septum'] lvOuterHeight = options['LV outer height'] lvOuterRadius = options['LV outer radius'] lvFreeWallThickness = options['LV free wall thickness'] From c468e215d1d0ce773c034d60de975a3b852d28ab Mon Sep 17 00:00:00 2001 From: Richard Christie Date: Fri, 16 Nov 2018 13:01:03 +1300 Subject: [PATCH 25/25] Support 6 or 8 elements around atrial free wall in base --- .../meshtypes/meshtype_3d_heartventricles1.py | 2 +- .../meshtype_3d_heartventriclesbase1.py | 133 +++++++++++++----- 2 files changed, 100 insertions(+), 35 deletions(-) diff --git a/scaffoldmaker/meshtypes/meshtype_3d_heartventricles1.py b/scaffoldmaker/meshtypes/meshtype_3d_heartventricles1.py index c4f10e0b..1819a574 100644 --- a/scaffoldmaker/meshtypes/meshtype_3d_heartventricles1.py +++ b/scaffoldmaker/meshtypes/meshtype_3d_heartventricles1.py @@ -43,7 +43,7 @@ def getDefaultOptions(): 'RV arc around degrees' : 155.0, 'RV arc apex fraction' : 0.6, 'RV free wall thickness' : 0.04, - 'RV width' : 0.45, + 'RV width' : 0.42, 'RV width growth factor' : 0.5, 'RV side extension' : 0.12, 'RV side extension growth factor' : 0.5, diff --git a/scaffoldmaker/meshtypes/meshtype_3d_heartventriclesbase1.py b/scaffoldmaker/meshtypes/meshtype_3d_heartventriclesbase1.py index 304b5a30..484e58b6 100644 --- a/scaffoldmaker/meshtypes/meshtype_3d_heartventriclesbase1.py +++ b/scaffoldmaker/meshtypes/meshtype_3d_heartventriclesbase1.py @@ -61,7 +61,7 @@ def getDefaultOptions(): options['RV outlet wall thickness'] = 0.025 options['Ventricles outlet element length'] = 0.1 options['Ventricles outlet spacing y'] = 0.02 - options['Ventricles outlet spacing z'] = 0.12 + options['Ventricles outlet spacing z'] = 0.1 options['Ventricles rotation degrees'] = 16.0 options['Ventricles translation x'] = -0.19 options['Ventricles translation y'] = -0.2 @@ -277,25 +277,26 @@ def generateBaseMesh(cls, region, options): vOuterNodeId = [ (startVOuterNodeId + n1) for n1 in range(elementsCountAroundLV) ] for nodeId in [ lvInnerNodeId, rvInnerNodeId, vOuterNodeId ]: vx = [] - #vd1 = [] + vd1 = [] if (nodeId is rvInnerNodeId) else None vd2 = [] - #vd3 = [] + #vd3 = [] if False else None for n1 in range(len(nodeId)): node = nodes.findNodeByIdentifier(nodeId[n1]) cache.setNode(node) result, x = coordinates.getNodeParameters(cache, -1, Node.VALUE_LABEL_VALUE, 1, 3) vx.append(x) + if vd1 is not None: + result, d1 = coordinates.getNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS1, 1, 3) + vd1.append(d1) result, d2 = coordinates.getNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS2, 1, 3) vd2.append(d2) - #if nodeId is lvInnerNodeId: - # result, d1 = coordinates.getNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS1, 1, 3) - # vd1.append(d1) + #if vd3: # result, d3 = coordinates.getNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS3, 1, 3) # vd3.append(d3) if nodeId is lvInnerNodeId: lvInnerx, lvInnerd2 = vx, vd2 elif nodeId is rvInnerNodeId: - rvInnerx, rvInnerd2 = vx, vd2 + rvInnerx, rvInnerd1, rvInnerd2 = vx, vd1, vd2 else: vOuterx, vOuterd2 = vx, vd2 @@ -407,7 +408,6 @@ def generateBaseMesh(cls, region, options): ravOuterd3[n2].append(None) # get av bottom derivative 2 from Hermite-Lagrange interpolation from top row of ventricles elementsCountLVFreeWallRegular = elementsCountAroundLVFreeWall - 1 - elementsCountRVFreeWallRegular = 3 for n1 in range(elementsCountLVFreeWallRegular + 1): noa = elementsCountAroundAtrialFreeWall - elementsCountLVFreeWallRegular + n1 nov = elementsCountAroundLVFreeWall - elementsCountLVFreeWallRegular + n1 @@ -418,11 +418,35 @@ def generateBaseMesh(cls, region, options): for c in range(3): lavInnerd2[0][noa][c] += lavInnerd1[0][noa][c] lavOuterd2[0][noa][c] += lavOuterd1[0][noa][c] - for n1 in range(1, elementsCountRVFreeWallRegular + 1): + elementsCountRVHanging = 2 if (elementsCountAroundAtrialFreeWall == 8) else 0 + elementsCountRVFreeWallRegular = 3 + elementsCountRVHanging + for n1 in range(elementsCountRVFreeWallRegular + 1): noa = elementsCountAroundAtrialSeptum + n1 - 1 + niv = n1 nov = elementsCountAroundLVFreeWall + n1 - ravInnerd2[0][noa] = interpolateHermiteLagrangeDerivative(rvInnerx[n1], rvInnerd2[n1], ravInnerx[0][noa], 1.0) - ravOuterd2[0][noa] = interpolateHermiteLagrangeDerivative(vOuterx[nov], vOuterd2[nov], ravOuterx[0][noa], 1.0) + if elementsCountRVHanging: + if n1 > 1: + niv -= 1 + nov -= 1 + if n1 > 3: + niv -= 1 + nov -= 1 + if elementsCountRVHanging and ((n1 == 2) or (n1 == 4)): + six = interpolateCubicHermite(rvInnerx[niv], rvInnerd1[niv], rvInnerx[niv + 1], rvInnerd1[niv + 1], 0.5) + sox = interpolateCubicHermite( vOuterx[nov], vOuterd2[nov], vOuterx[nov + 1], vOuterd2[nov + 1], 0.5) + sid2 = [ 0.5*(rvInnerd2[niv][c] + rvInnerd2[niv + 1][c]) for c in range(3) ] + sod2 = [ 0.5*( vOuterd2[nov][c] + vOuterd2[nov + 1][c]) for c in range(3) ] + else: + six, sid2 = rvInnerx[niv], rvInnerd2[niv] + sox, sod2 = vOuterx[nov], vOuterd2[nov] + ravInnerd2[0][noa] = interpolateHermiteLagrangeDerivative(six, sid2, ravInnerx[0][noa], 1.0) + ravOuterd2[0][noa] = interpolateHermiteLagrangeDerivative(sox, sod2, ravOuterx[0][noa], 1.0) + if elementsCountRVHanging: + # subtract d1 on last point, later map to d1 + d2 + noa = elementsCountAroundAtrialSeptum + 4 + for c in range(3): + ravInnerd2[0][noa][c] -= ravInnerd1[0][noa][c] + ravOuterd2[0][noa][c] -= ravOuterd1[0][noa][c] for n1 in range(elementsCountAroundAtrialSeptum + 1): noa = (elementsCountAroundAtrialFreeWall + n1)%elementsCountAroundAtria nov = elementsCountAroundLVFreeWall + n1 @@ -449,6 +473,7 @@ def generateBaseMesh(cls, region, options): mag = baseHeight + baseThickness ravInnerd2[0][ravsvcn1] = vector.setMagnitude(vector.crossproduct3(ravInnerd3[0][ravsvcn1], ravInnerd1[0][ravsvcn1]), mag) ravOuterd2[0][ravsvcn1] = vector.setMagnitude(vector.crossproduct3(ravOuterd3[0][ravsvcn1], ravOuterd1[0][ravsvcn1]), mag) + ravsvcn2 = elementsCountAroundAtria - 4 # copy derivative 3 from av points to LV outlet at centre, left and right cfb; negate as d1 is reversed: lvOutletOuterd3[0] = [ -d for d in lavOuterd3[0][0] ] @@ -473,32 +498,50 @@ def generateBaseMesh(cls, region, options): # create points on bottom and top of RV supraventricular crest ns = elementsCountAroundRVFreeWall//2 + 1 nf = elementsCountAroundRVFreeWall + 2 - xis = 0.6 + xis = 0.667 xif = 1.0 - xis mx = [ xis*rvInnerx[ns][0] + xif*rvInnerx[nf][0], xis*rvInnerx[ns][1] + xif*rvInnerx[nf][1], -(fibrousRingThickness + baseThickness) ] md2 = [ (rvInnerx[nf][c] - rvInnerx[ns][c]) for c in range(3) ] - pd1 = smoothCubicHermiteDerivativesLine([ ravInnerx[0][ravsvcn1], mx, rvOutletInnerx[1] ], [ [ -d for d in ravInnerd2[0][ravsvcn1] ], zero, rvOutletd2 ], - fixStartDirection=True, fixEndDerivative = True) + sd1 = [ -d for d in ravInnerd2[0][ravsvcn2] ] + if elementsCountRVHanging == 0: + for c in range(3): + sd1[c] += ravInnerd1[0][ravsvcn2][c] + fd1 = [ (rvOutletInnerd1[2][c] + rvOutletd2[c]) for c in range(3) ] + pd1 = smoothCubicHermiteDerivativesLine([ ravInnerx[0][ravsvcn2], mx, rvOutletInnerx[1] ], [ sd1, zero, fd1 ], + fixStartDerivative=True, fixEndDerivative = True) pd2 = smoothCubicHermiteDerivativesLine([ rvInnerx[ns], mx, rvInnerx[nf] ], [ rvInnerd2[ns], md2, [ -d for d in rvInnerd2[nf] ] ], fixStartDerivative = True, fixEndDerivative = True) svcix = [ mx[0], mx[1], mx[2] ] # list components to avoid reference bug svcid1 = pd1[1] svcid2 = pd2[1] svcid3 = vector.setMagnitude(vector.crossproduct3(svcid1, svcid2), baseThickness) - ravInnerd2[0][ravsvcn1] = [ -d for d in pd1[0] ] + sd2 = [ (svcid2[c] - svcid1[c]) for c in range(3) ] + pd2 = smoothCubicHermiteDerivativesLine([ mx, ravInnerx[0][ravsvcn1] ], [ sd2, ravInnerd2[0][ravsvcn1] ], + fixStartDerivative=True, fixEndDirection = True) + ravInnerd2[0][ravsvcn1] = pd2[1] + + #ravInnerd2[0][ravsvcn1] = [ -d for d in pd1[0] ] mx = [ (mx[c] + svcid3[c]) for c in range(3) ] md2 = svcid2 nf = 2 - pd1 = smoothCubicHermiteDerivativesLine([ ravOuterx[0][ravsvcn1], mx, rvOutletOuterx[1] ], [ [ -d for d in ravOuterd2[0][ravsvcn1] ], zero, rvOutletd2 ], - fixStartDirection=True, fixEndDerivative = True) + sd1 = [ -d for d in ravOuterd2[0][ravsvcn2] ] + if elementsCountRVHanging == 0: + for c in range(3): + sd1[c] += ravOuterd1[0][ravsvcn2][c] + fd1 = [ (rvOutletOuterd1[2][c] + rvOutletd2[c]) for c in range(3) ] + pd1 = smoothCubicHermiteDerivativesLine([ ravOuterx[0][ravsvcn2], mx, rvOutletOuterx[1] ], [ sd1, zero, fd1 ], + fixStartDerivative=True, fixEndDerivative = True) pd2 = smoothCubicHermiteDerivativesLine([ mx, lvOutletOuterx[nf] ], [ md2, svcid2 ], fixStartDirection=True) svcox = [ mx[0], mx[1], mx[2] ] # list components to avoid reference bug svcod1 = pd1[1] svcod2 = pd2[0] svcod3 = svcid3 - ravOuterd2[0][ravsvcn1] = [ -d for d in pd1[0] ] lvOutletOuterd3[nf] = [ -d for d in pd2[1] ] + sd2 = [ (svcod2[c] - svcod1[c]) for c in range(3) ] + pd2 = smoothCubicHermiteDerivativesLine([ mx, ravOuterx[0][ravsvcn1] ], [ sd2, ravOuterd2[0][ravsvcn1] ], + fixStartDerivative=True, fixEndDirection = True) + ravOuterd2[0][ravsvcn1] = pd2[1] # LV outlet nodes lvOutletNodeId = [ [], [] ] @@ -720,8 +763,8 @@ def generateBaseMesh(cls, region, options): meshGroup.addElement(element) # RV base elements row 1, starting at crux / posterior interventricular sulcus - elementsCountRVHanging = 1 - for e in range(-1, elementsCountAroundRVFreeWall + elementsCountRVHanging + 1): + eInfundibulumStart = elementsCountAroundRVFreeWall + elementsCountRVHanging - 3 + for e in range(-1, elementsCountAroundRVFreeWall + elementsCountRVHanging + 2): eft1 = eft nids = None scalefactors = None @@ -729,8 +772,11 @@ def generateBaseMesh(cls, region, options): noa = elementsCountAroundAtrialSeptum - 1 + e niv = e - #if e > (elementsCountAroundRVFreeWall + elementsCountRVHanging - 2): - # niv -= 1 + if elementsCountRVHanging: + if e > 1: + niv -= 1 + if e > 3: + niv -= 1 nivp = niv + 1 nov = elementsCountAroundLVFreeWall + niv novp = (nov + 1)%elementsCountAroundLV @@ -759,6 +805,20 @@ def generateBaseMesh(cls, region, options): setEftScaleFactorIds(eft1, [1], []) scalefactors = [ -1.0 ] remapEftNodeValueLabel(eft1, [ 5, 7 ], Node.VALUE_LABEL_D_DS3, [ ( Node.VALUE_LABEL_D_DS1, [1] ), ( Node.VALUE_LABEL_D_DS3, []) ]) + elif elementsCountRVHanging: + eft1 = tricubichermite.createEftNoCrossDerivatives() + setEftScaleFactorIds(eft1, [1, 102, 104, 108, 304], []) + scalefactors = scalefactors5hanging + if e in [ 1, 3 ]: + # 1st of pair of elements with hanging nodes at xi1=0.5 on xi2 == 0 plane + tricubichermite.setEftMidsideXi1HangingNode(eft1, 2, 1, 1, 2, [1, 2, 3, 4, 5]) + tricubichermite.setEftMidsideXi1HangingNode(eft1, 6, 5, 5, 6, [1, 2, 3, 4, 5]) + else: # e in [ 2, 4 ]: + # 2nd of pair of elements with hanging nodes at xi1=0.5 on xi2 == 0 plane + tricubichermite.setEftMidsideXi1HangingNode(eft1, 1, 2, 1, 2, [1, 2, 3, 4, 5]) + tricubichermite.setEftMidsideXi1HangingNode(eft1, 5, 6, 5, 6, [1, 2, 3, 4, 5]) + if e == 4: + remapEftNodeValueLabel(eft1, [ 4, 8 ], Node.VALUE_LABEL_D_DS2, [ ( Node.VALUE_LABEL_D_DS1, [] ), ( Node.VALUE_LABEL_D_DS2, []) ]) elif e == elementsCountRVFreeWallRegular: # supraventricular crest outer 1 nids = [ rvInnerNodeId[niv], rvInnerNodeId[nivp], ravInnerNodeId[0][noa], svciNodeId, @@ -766,8 +826,12 @@ def generateBaseMesh(cls, region, options): eft1 = tricubichermite.createEftNoCrossDerivatives() setEftScaleFactorIds(eft1, [1], []) scalefactors = [ -1.0 ] - remapEftNodeValueLabel(eft1, [ 3, 7 ], Node.VALUE_LABEL_D_DS1, [ ( Node.VALUE_LABEL_D_DS1, [] ), ( Node.VALUE_LABEL_D_DS2, [1] ) ]) - elif e == (elementsCountRVFreeWallRegular + 1): + if elementsCountRVHanging: + remapEftNodeValueLabel(eft1, [ 3, 7 ], Node.VALUE_LABEL_D_DS1, [ ( Node.VALUE_LABEL_D_DS2, [1] ) ]) + remapEftNodeValueLabel(eft1, [ 3, 7 ], Node.VALUE_LABEL_D_DS2, [ ( Node.VALUE_LABEL_D_DS1, [] ), ( Node.VALUE_LABEL_D_DS2, []) ]) + else: + remapEftNodeValueLabel(eft1, [ 3, 7 ], Node.VALUE_LABEL_D_DS1, [ ( Node.VALUE_LABEL_D_DS1, [] ), ( Node.VALUE_LABEL_D_DS2, [1] ) ]) + elif e == eInfundibulumStart: # supraventricular crest outer 2, outer infundibulum 1 nids = [ rvInnerNodeId[niv], rvInnerNodeId[nivp], svciNodeId, rvOutletNodeId[0][2], vOuterNodeId[nov], vOuterNodeId[novp], svcoNodeId, rvOutletNodeId[1][2] ] @@ -777,7 +841,7 @@ def generateBaseMesh(cls, region, options): tricubichermite.setEftLinearDerivative(eft1, [ 4, 8 ], Node.VALUE_LABEL_D_DS3, 4, 8, 1) remapEftNodeValueLabel(eft1, [ 4, 8 ], Node.VALUE_LABEL_D_DS1, [ ( Node.VALUE_LABEL_D_DS1, [] ), ( Node.VALUE_LABEL_D_DS2, [] ) ]) meshGroups += [ conusArteriosusMeshGroup ] - elif e == (elementsCountAroundRVFreeWall + elementsCountRVHanging - 3): + elif e == (eInfundibulumStart + 1): # supraventricular crest outer 3, outer infundibulum 2 nids = [ rvInnerNodeId[niv], rvInnerNodeId[nivp], rvOutletNodeId[0][2], rvOutletNodeId[0][3], vOuterNodeId[nov], vOuterNodeId[novp], rvOutletNodeId[1][2], rvOutletNodeId[1][3] ] @@ -787,7 +851,7 @@ def generateBaseMesh(cls, region, options): tricubichermite.setEftLinearDerivative(eft1, [ 3, 7 ], Node.VALUE_LABEL_D_DS3, 3, 7, 1) tricubichermite.setEftLinearDerivative(eft1, [ 4, 8 ], Node.VALUE_LABEL_D_DS3, 4, 8, 1) meshGroups += [ conusArteriosusMeshGroup ] - elif e == (elementsCountAroundRVFreeWall + elementsCountRVHanging - 2): + elif e == (eInfundibulumStart + 2): # outer infundibulum 3 nids = [ rvInnerNodeId[niv], rvInnerNodeId[nivp], rvOutletNodeId[0][3], rvOutletNodeId[0][4], vOuterNodeId[nov], vOuterNodeId[novp], rvOutletNodeId[1][3], rvOutletNodeId[1][4] ] @@ -799,7 +863,7 @@ def generateBaseMesh(cls, region, options): remapEftNodeValueLabel(eft1, [ 2, 6 ], Node.VALUE_LABEL_D_DS2, [ ( Node.VALUE_LABEL_D_DS1, [1] ), ( Node.VALUE_LABEL_D_DS2, []) ]) remapEftNodeValueLabel(eft1, [ 6 ], Node.VALUE_LABEL_D_DS3, [ ( Node.VALUE_LABEL_D_DS1, [] ), ( Node.VALUE_LABEL_D_DS3, []) ]) meshGroups += [ conusArteriosusMeshGroup ] - elif e == (elementsCountAroundRVFreeWall + elementsCountRVHanging - 1): + elif e == (eInfundibulumStart + 3): # outer infundibulum 4, above septum end # 7 node collapsed element above tetrahedral septum end nids = [ rvInnerNodeId[niv], rvOutletNodeId[0][4], rvOutletNodeId[0][5], @@ -820,7 +884,7 @@ def generateBaseMesh(cls, region, options): ln_map = [ 1, 1, 2, 3, 4, 5, 6, 7 ] remapEftLocalNodes(eft1, 7, ln_map) meshGroups += [ conusArteriosusMeshGroup ] - elif e == (elementsCountAroundRVFreeWall + elementsCountRVHanging): + elif e == (eInfundibulumStart + 4): # outer infundibulum 5, above septum nids = [ rvInnerNodeId[niv - 1], rvInnerNodeId[niv], rvOutletNodeId[0][5], rvOutletNodeId[0][0], avsNodeId, lvOutletNodeId[1][3], rvOutletNodeId[1][5], rvOutletNodeId[1][0] ] @@ -839,8 +903,6 @@ def generateBaseMesh(cls, region, options): remapEftNodeValueLabel(eft1, [ 6 ], Node.VALUE_LABEL_D_DS3, [ ( Node.VALUE_LABEL_D_DS2, []) ]) remapEftNodeValueLabel(eft1, [ 6 ], Node.VALUE_LABEL_D2_DS1DS2, [ ( Node.VALUE_LABEL_D_DS3, []) ]) # swap end meshGroups += [ conusArteriosusMeshGroup ] - else: - continue result = elementtemplate1.defineField(coordinates, -1, eft1) element = mesh.createElement(elementIdentifier, elementtemplate1) @@ -1131,7 +1193,11 @@ def generateBaseMesh(cls, region, options): eft1 = tricubichermite.createEftNoCrossDerivatives() setEftScaleFactorIds(eft1, [1], []) scalefactors = [ -1.0 ] - remapEftNodeValueLabel(eft1, [ 1, 3, 5, 7 ], Node.VALUE_LABEL_D_DS1, [ ( Node.VALUE_LABEL_D_DS1, [] ), ( Node.VALUE_LABEL_D_DS2, [1] ) ]) # must do before following + if elementsCountRVHanging: + remapEftNodeValueLabel(eft1, [ 1, 5 ], Node.VALUE_LABEL_D_DS1, [ ( Node.VALUE_LABEL_D_DS2, [1] ) ]) + else: + remapEftNodeValueLabel(eft1, [ 1, 5 ], Node.VALUE_LABEL_D_DS1, [ ( Node.VALUE_LABEL_D_DS1, [] ), ( Node.VALUE_LABEL_D_DS2, [1] ) ]) + remapEftNodeValueLabel(eft1, [ 3, 7 ], Node.VALUE_LABEL_D_DS1, [ ( Node.VALUE_LABEL_D_DS1, [] ), ( Node.VALUE_LABEL_D_DS2, [1] ) ]) remapEftNodeValueLabel(eft1, [ 1, 3, 5, 7 ], Node.VALUE_LABEL_D_DS2, [ ( Node.VALUE_LABEL_D_DS1, [] )]) remapEftNodeValueLabel(eft1, [ 4 ], Node.VALUE_LABEL_D_DS1, [ ( Node.VALUE_LABEL_D_DS1, [1] ), ( Node.VALUE_LABEL_D_DS2, [1] ) ]) remapEftNodeValueLabel(eft1, [ 4 ], Node.VALUE_LABEL_D_DS2, [ ( Node.VALUE_LABEL_D_DS2, [1] ) ]) @@ -1226,7 +1292,6 @@ def refineMesh(cls, meshrefinement, options): elementsCountAroundAtrialFreeWall = options['Number of elements around atrial free wall'] elementsCountAroundAtrialSeptum = options['Number of elements around atrial septum'] elementsCountAroundAtria = elementsCountAroundAtrialFreeWall + elementsCountAroundAtrialSeptum - elementsCountRVFreeWallRegular = 3 refineElementsCountSurface = options['Refine number of elements surface'] refineElementsCountThroughLVWall = options['Refine number of elements through LV wall'] refineElementsCountThroughWall = options['Refine number of elements through wall'] @@ -1234,8 +1299,8 @@ def refineMesh(cls, meshrefinement, options): element = meshrefinement._sourceElementiterator.next() startBaseLvElementIdentifier = element.getIdentifier() startBaseRvElementIdentifier = startBaseLvElementIdentifier + elementsCountAroundLVFreeWall + 1 - elementsCountRVHanging = 1 - startBaseSeptumElementIdentifier = startBaseRvElementIdentifier + elementsCountAroundRVFreeWall + elementsCountRVHanging + 2 + elementsCountRVHanging = 2 if (elementsCountAroundAtrialFreeWall == 8) else 0 + startBaseSeptumElementIdentifier = startBaseRvElementIdentifier + elementsCountAroundRVFreeWall + elementsCountRVHanging + 3 startBaseLv2ElementIdentifier = startBaseSeptumElementIdentifier + elementsCountAroundVSeptum + 1 startBaseRv2ElementIdentifier = startBaseLv2ElementIdentifier + 8 limitBaseElementIdentifier = startBaseRv2ElementIdentifier + 5