From abd363e5da0e4a3f78155fb8f94a7132a7a2f987 Mon Sep 17 00:00:00 2001 From: Mabelle Lin Date: Fri, 21 Dec 2018 14:09:40 +1300 Subject: [PATCH 01/44] Interpolate from and inner and outer ellipses --- scaffoldmaker/utils/tubemesh.py | 180 +++++++++++++++++++------------- 1 file changed, 105 insertions(+), 75 deletions(-) diff --git a/scaffoldmaker/utils/tubemesh.py b/scaffoldmaker/utils/tubemesh.py index 52eaf8e9..b6472322 100644 --- a/scaffoldmaker/utils/tubemesh.py +++ b/scaffoldmaker/utils/tubemesh.py @@ -50,6 +50,9 @@ def generatetubemesh(region, # Sample central line to get same number of elements as elementsCountAlong sx, sd1, se, sxi, _ = sampleCubicHermiteCurves(cx, cd1, elementsCountAlong) + # sd1Smoothed = smoothCubicHermiteDerivativesLine(sx, sd1) + # sd1 = [] + # sd1 = sd1Smoothed sd2 = interpolateSampleLinear(cd2, se, sxi) sd3 = interpolateSampleLinear(cd3, se, sxi) st2 = interpolateSampleLinear(t2, se, sxi) @@ -130,90 +133,117 @@ def generatetubemesh(region, dx_ds1 = [ 0.0, 0.0, 0.0 ] dx_ds2 = [ 0.0, 0.0, 0.0 ] dx_ds3 = [ 0.0, 0.0, 0.0 ] + innerx = [] + innerdx_ds1 = [] + outerx = [] + dWall = [] + unitNormalInner = [] + curvatureAlong = [] + ellipsex = [] + ellipsedx_ds1 = [] + tubex = [] + tubedx_ds1 = [] + tubedx_ds2 = [] + tubedx_ds3 = [] - for n3 in range(elementsCountThroughWall + 1): - for n2 in range(elementsCountAlong+1): - aThroughWallElement = sd2[n2][1] + st2[n2]*(n3/elementsCountThroughWall) - bThroughWallElement = sd3[n2][2] + st3[n2]*(n3/elementsCountThroughWall) - perimeterAroundWallElement = getApproximateEllipsePerimeter(aThroughWallElement, bThroughWallElement) - arcLengthPerElementAround = perimeterAroundWallElement / elementsCountAround - prevRadiansAround = updateEllipseAngleByArcLength(aThroughWallElement, bThroughWallElement, 0.0, arcLengthPerElementAround) - st2PerWallElement = st2[n2]/elementsCountThroughWall - st3PerWallElement = st3[n2]/elementsCountThroughWall + # Calculate node location and derivatives for inner and outer surface + for n2 in range(elementsCountAlong+1): + aInner = sd2[n2][1] + bInner = sd3[n2][2] + perimeterInner = getApproximateEllipsePerimeter(aInner, bInner) + arcLengthPerElementAroundInner = perimeterInner / elementsCountAround + prevRadiansAroundInner = updateEllipseAngleByArcLength(aInner, bInner, 0.0, arcLengthPerElementAroundInner) + + aOuter = sd2[n2][1] + st2[n2] + bOuter = sd3[n2][2] + st3[n2] + perimeterOuter = getApproximateEllipsePerimeter(aOuter, bOuter) + arcLengthPerElementAroundOuter = perimeterOuter / elementsCountAround + prevRadiansAroundOuter = updateEllipseAngleByArcLength(aOuter, bOuter, 0.0, arcLengthPerElementAroundOuter) + + for n1 in range(elementsCountAround): + arcLengthAroundInner = n1*arcLengthPerElementAroundInner + radiansAroundInner = -1*updateEllipseAngleByArcLength(aInner, bInner, 0.0, arcLengthAroundInner) + cosRadiansAroundInner = math.cos(radiansAroundInner) + sinRadiansAroundInner = math.sin(radiansAroundInner) + xInner = [sx[n2][j] + aInner*cosRadiansAroundInner*sBinormal[n2][j] + bInner*sinRadiansAroundInner*sNormal[n2][j] for j in range(3)] + innerx.append(xInner) + dx_ds1Inner = [(radiansAroundInner - prevRadiansAroundInner)*(aInner*-sinRadiansAroundInner*sBinormal[n2][j] + bInner*cosRadiansAroundInner*sNormal[n2][j]) for j in range(3)] + innerdx_ds1.append(dx_ds1Inner) + prevRadiansAroundInner = radiansAroundInner + unitNormalInnerNode = normalise([aInner*cosRadiansAroundInner*sBinormal[n2][j] + bInner*sinRadiansAroundInner*sNormal[n2][j] for j in range(3)]) + unitNormalInner.append(unitNormalInnerNode) + + arcLengthAroundOuter = n1*arcLengthPerElementAroundOuter + radiansAroundOuter = -1*updateEllipseAngleByArcLength(aOuter, bOuter, 0.0, arcLengthAroundOuter) + cosRadiansAroundOuter = math.cos(radiansAroundOuter) + sinRadiansAroundOuter = math.sin(radiansAroundOuter) + xOuter = [sx[n2][j] + aOuter*cosRadiansAroundOuter*sBinormal[n2][j] + bOuter*sinRadiansAroundOuter*sNormal[n2][j] for j in range(3)] + outerx.append(xOuter) + prevRadiansAroundOuter = radiansAroundOuter + + d = [ xOuter[i] - xInner[i] for i in range(3)] + dWall.append(d) - if n2 < elementsCountAlong: - aThroughWallElementNext = sd2[n2+1][1] + st2[n2+1]*(n3/elementsCountThroughWall) - bThroughWallElementNext = sd3[n2+1][2] + st3[n2+1]*(n3/elementsCountThroughWall) - perimeterAroundWallElementNext = getApproximateEllipsePerimeter(aThroughWallElementNext, bThroughWallElementNext) - arcLengthPerElementAroundNext = perimeterAroundWallElementNext / elementsCountAround + if n2 == 0: + curvature = getCubicHermiteCurvature(sx[n2], sd1[n2], sx[n2+1], sd1[n2+1], unitNormalInnerNode, 0.0) + elif n2 == elementsCountAlong: + curvature = getCubicHermiteCurvature(sx[n2-1], sd1[n2-1], sx[n2], sd1[n2], unitNormalInnerNode, 1.0) + else: + curvature = 0.5*( + getCubicHermiteCurvature(sx[n2-1], sd1[n2-1], sx[n2], sd1[n2], unitNormalInnerNode, 1.0) + + getCubicHermiteCurvature(sx[n2], sd1[n2], sx[n2+1], sd1[n2+1], unitNormalInnerNode, 0.0)) + curvatureAlong.append(curvature) + # Interpolate to get nodes through wall + for n3 in range(elementsCountThroughWall + 1): + xi = 1/elementsCountThroughWall*n3 + + for n2 in range(elementsCountAlong+1): for n1 in range(elementsCountAround): - arcLengthAround = n1*arcLengthPerElementAround - radiansAround = -1*updateEllipseAngleByArcLength(aThroughWallElement, bThroughWallElement, 0.0, arcLengthAround) - cosRadiansAround = math.cos(radiansAround) - sinRadiansAround = math.sin(radiansAround) - x = [sx[n2][j] + aThroughWallElement*cosRadiansAround*sBinormal[n2][j] + bThroughWallElement*sinRadiansAround*sNormal[n2][j] for j in range(3)] - dx_ds1 = [(radiansAround - prevRadiansAround)*(aThroughWallElement*-sinRadiansAround*sBinormal[n2][j] + bThroughWallElement*cosRadiansAround*sNormal[n2][j]) for j in range(3)] + n = n2*elementsCountAround + n1 - # Calculate curvature to find d1 for node - unitNormal = normalise([aThroughWallElement*cosRadiansAround*sBinormal[n2][j] + bThroughWallElement*sinRadiansAround*sNormal[n2][j] for j in range(3)]) - if n2 == 0: - curvature = getCubicHermiteCurvature(sx[n2], sd1[n2], sx[n2+1], sd1[n2+1], unitNormal, 0.0) - elif n2 == elementsCountAlong: - curvature = getCubicHermiteCurvature(sx[n2-1], sd1[n2-1], sx[n2], sd1[n2], unitNormal, 1.0) - else: - curvature = 0.5*( - getCubicHermiteCurvature(sx[n2-1], sd1[n2-1], sx[n2], sd1[n2], unitNormal, 1.0) + - getCubicHermiteCurvature(sx[n2], sd1[n2], sx[n2+1], sd1[n2+1], unitNormal, 0.0)) - wallDistance = magnitude([aThroughWallElement*cosRadiansAround*sBinormal[n2][j] + bThroughWallElement*sinRadiansAround*sNormal[n2][j] for j in range(3)]) - factor = 1.0 - curvature*wallDistance - d1Wall = [ factor*c for c in sd1[n2]] + # x + x = interpolateCubicHermite(innerx[n], dWall[n], outerx[n], dWall[n], xi) + tubex.append(x) - # Calculate curvature to find d1 for downstream node - if n2 < elementsCountAlong: - arcLengthAroundNext = n1*arcLengthPerElementAroundNext - radiansAroundNext = -1*updateEllipseAngleByArcLength(aThroughWallElementNext, bThroughWallElementNext, 0.0, arcLengthAroundNext) - cosRadiansAroundNext = math.cos(radiansAroundNext) - sinRadiansAroundNext = math.sin(radiansAroundNext) - xNext = [sx[n2+1][j] + aThroughWallElementNext*cosRadiansAroundNext*sBinormal[n2+1][j] + bThroughWallElementNext*sinRadiansAroundNext*sNormal[n2+1][j] for j in range(3)] - unitNormalNext = normalise([aThroughWallElementNext*cosRadiansAroundNext*sBinormal[n2+1][j] + bThroughWallElementNext*sinRadiansAroundNext*sNormal[n2+1][j] for j in range(3)]) - if n2 + 1 == elementsCountAlong: - curvatureNext = getCubicHermiteCurvature(sx[n2], sd1[n2], sx[n2+1], sd1[n2+1], unitNormalNext, 1.0) - else: - curvatureNext = 0.5*( - getCubicHermiteCurvature(sx[n2], sd1[n2], sx[n2+1], sd1[n2+1], unitNormalNext, 1.0) + - getCubicHermiteCurvature(sx[n2+1], sd1[n2+1], sx[n2+2], sd1[n2+2], unitNormalNext, 0.0)) - wallDistanceNext = magnitude([aThroughWallElementNext*cosRadiansAroundNext*sBinormal[n2+1][j] + bThroughWallElementNext*sinRadiansAroundNext*sNormal[n2+1][j] for j in range(3)]) - factorNext = 1.0 - curvatureNext*wallDistanceNext - d1WallNext = [ factorNext*c for c in sd1[n2+1] ] - arcLength = computeCubicHermiteArcLength(x, d1Wall, xNext, d1WallNext, True) - dx_ds2 = [arcLength*c for c in normalise(d1Wall)] - if n2 == elementsCountAlong - 1: - secondLastX = x - secondLastd1Wall = d1Wall - lastX = xNext - lastd1Wall = d1WallNext + # dx_ds1 + if n1 == 0: + prevIdx = (((n+1)//elementsCountAround + 1)*elementsCountAround)-1 + ellipsex = [] + ellipsedx_ds1 = [] else: - arcLength = computeCubicHermiteArcLength(secondLastX, secondLastd1Wall, lastX, lastd1Wall, True) - dx_ds2 = [arcLength*c for c in normalise(lastd1Wall)] + prevIdx = n - 1 + nextIdx = ((n+1)//(elementsCountAround +1) * elementsCountAround + ((n+1)%elementsCountAround) + 1)-1 + curvatureAround = 0.5*( + getCubicHermiteCurvature(innerx[prevIdx], innerdx_ds1[prevIdx], innerx[n], innerdx_ds1[n], unitNormalInner[n], 1.0) + + getCubicHermiteCurvature(innerx[n], innerdx_ds1[n], innerx[nextIdx], innerdx_ds1[nextIdx], unitNormalInner[n], 0.0)) + wallDistance = magnitude(dWall[n])*xi + factor = 1.0 - curvatureAround*wallDistance + dx_ds1 = [ factor*c for c in innerdx_ds1[n]] + ellipsedx_ds1.append(dx_ds1) + ellipsex.append(x) + if n1 == elementsCountAround-1: + smoothdx_ds1 = smoothCubicHermiteDerivativesLoop(ellipsex, ellipsedx_ds1) + tubedx_ds1 = tubedx_ds1 + smoothdx_ds1 + + # dx_ds2 + factor = 1.0 - curvatureAlong[n]*wallDistance + d1AlongTube = [ factor*c for c in sd1[n2]] + tubedx_ds2.append(d1AlongTube) - dx_ds3 = [ st2PerWallElement*cosRadiansAround*sBinormal[n2][j] + st3PerWallElement*sinRadiansAround*sNormal[n2][j] for j in range(3)] # Modify later to calculate with interpolation + #dx_ds3 + dx_ds3 = interpolateCubicHermiteDerivative(innerx[n], dWall[n], outerx[n], dWall[n], xi) + dx_ds3 = [c/elementsCountThroughWall for c in dx_ds3] + tubedx_ds3.append(dx_ds3) - node = nodes.createNode(nodeIdentifier, nodetemplate) - cache.setNode(node) - coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_VALUE, 1, x) - coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS1, 1, dx_ds1) - coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS2, 1, dx_ds2) - if useCubicHermiteThroughWall: - coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS3, 1, dx_ds3) - if useCrossDerivatives: - coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D2_DS1DS2, 1, zero) - if useCubicHermiteThroughWall: - coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D2_DS1DS3, 1, zero) - coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D2_DS2DS3, 1, zero) - coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D3_DS1DS2DS3, 1, zero) - nodeIdentifier = nodeIdentifier + 1 - prevRadiansAround = radiansAround + for n in range((elementsCountAround)*(elementsCountAlong+1)*(elementsCountThroughWall+1)): + node = nodes.createNode(nodeIdentifier, nodetemplate) + cache.setNode(node) + coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_VALUE, 1, tubex[n]) + coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS1, 1, tubedx_ds1[n]) + coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS2, 1, tubedx_ds2[n]) + coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS3, 1, tubedx_ds3[n]) + nodeIdentifier = nodeIdentifier + 1 # # For debugging - Nodes along central line # for pt in range(len(sx)): From f34de1e8e4c093016dbc44047dae581143360bca Mon Sep 17 00:00:00 2001 From: Mabelle Lin Date: Tue, 12 Feb 2019 10:24:46 +1300 Subject: [PATCH 02/44] Create haustra profile --- scaffoldmaker/meshtypes/meshtype_3d_colon1.py | 539 ++++++++++++++++++ scaffoldmaker/scaffolds.py | 2 + 2 files changed, 541 insertions(+) create mode 100644 scaffoldmaker/meshtypes/meshtype_3d_colon1.py diff --git a/scaffoldmaker/meshtypes/meshtype_3d_colon1.py b/scaffoldmaker/meshtypes/meshtype_3d_colon1.py new file mode 100644 index 00000000..c40a2a1a --- /dev/null +++ b/scaffoldmaker/meshtypes/meshtype_3d_colon1.py @@ -0,0 +1,539 @@ +""" +Generates a 3-D colon mesh along the central line, with variable +numbers of elements around, along and through wall, with +variable radius and thickness along. +""" + +from scaffoldmaker.utils.meshrefinement import MeshRefinement +from scaffoldmaker.utils.tubemesh import * + +class MeshType_3d_colon1(object): + ''' + Generates a 3-D colon mesh with variable numbers + of elements around, along the central line, and through wall. + The colon has a triangular profile with rounded corners at the + interhaustral septa which is created from a central line and + lateral axes data. The colon presents a clover profile outside + the septa. + ''' + @staticmethod + def getName(): + return '3D Colon 1' + + @staticmethod + def getDefaultOptions(): + return { + 'Number of elements around' : 9, + 'Number of elements along haustra' : 3, + 'Number of elements through wall' : 1, + 'Inner radius': 0.5, + 'Corner radius fraction': 0.5, + 'Wall thickness': 0.01, + 'Number of haustra': 2, + 'Haustra radius fraction': 0.5, + 'Haustra length': 1.0, + 'Interhaustra fold factor': 0.5, + 'Haustra curvature factor': 1.0, + 'Tube type': 1, + 'Use cross derivatives' : False, + 'Use linear through wall' : False, + 'Refine' : False, + 'Refine number of elements around' : 1, + 'Refine number of elements along' : 1, + 'Refine number of elements through wall' : 1 + } + + @staticmethod + def getOrderedOptionNames(): + return [ + 'Number of elements around', + 'Number of elements along haustra', + 'Number of elements through wall', + 'Inner radius', + 'Corner radius fraction', + 'Wall thickness', + 'Number of haustra', + 'Haustra radius fraction', + 'Haustra length', + 'Interhaustra fold factor', + 'Haustra curvature factor', + 'Tube type', + 'Use cross derivatives', + 'Use linear through wall', + 'Refine', + 'Refine number of elements around', + 'Refine number of elements along', + 'Refine number of elements through wall' + ] + + @staticmethod + def checkOptions(options): + for key in [ + 'Number of elements through wall', + 'Number of haustra', + 'Refine number of elements around', + 'Refine number of elements along', + 'Refine number of elements through wall']: + if options[key] < 1: + options[key] = 1 + if (options['Number of elements around'] < 9) : + options['Number of elements around'] = 9 + if (options['Number of elements around'] % 3 != 0) : + options['Number of elements around'] = (options['Number of elements around']//3)*3 + for key in [ + 'Inner radius', + 'Corner radius fraction', + 'Wall thickness', + 'Haustra radius fraction', + 'Haustra length', + 'Interhaustra fold factor', + 'Haustra curvature factor']: + if options[key] < 0.0: + options[key] = 0.0 + if options['Corner radius fraction'] < 0.1: + options['Corner radius fraction'] = 0.1 + for key in [ + 'Corner radius fraction', + 'Interhaustra fold factor']: + if options[key] > 1.0: + options[key] = 1.0 + if (options['Tube type'] < 1 or options['Tube type'] > 3 ) : + options['Tube type'] = 1 + + @staticmethod + def generateBaseMesh(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: None + """ + elementsCountAround = options['Number of elements around'] + elementsCountAlongHaustra = options['Number of elements along haustra'] + elementsCountThroughWall = options['Number of elements through wall'] + radius = options['Inner radius'] + cornerRadiusFraction = options['Corner radius fraction'] + wallThickness = options['Wall thickness'] + haustraCount = options['Number of haustra'] + haustraRadiusFraction = options['Haustra radius fraction'] + haustraLength = options['Haustra length'] + foldFactor = options['Interhaustra fold factor'] + haustraCurvatureFactor = options['Haustra curvature factor'] + tubeType = options['Tube type'] + useCrossDerivatives = options['Use cross derivatives'] + useCubicHermiteThroughWall = not(options['Use linear through wall']) + elementsCountAlong = int(elementsCountAlongHaustra*haustraCount) + + # if tubeType == 1: + # # Straight tube + # cx = [[0.0, 0.0, 0.0], [ 0.0, 0.0, 5.0 ] ] + # cd1 = [[ 0.0, 0.0, 5.0 ], [ 0.0, 0.0, 5.0 ]] + # cd2 = [ [ 0.0, 0.5, 0.0 ], [ 0.0, 0.5, 0.0 ]] + # cd3 = [ [ 0.0, 0.0, 0.5 ], [ 0.0, 0.0, 0.5 ]] + + # # thickness in cd2 and cd3 directions and derivatives (rate of change) + # t2 = [ 0.2, 0.2 ] + # t2d = [ 0.0, 0.0 ] + # t3 = [ 0.2, 0.2 ] + # t3d = [ 0.0, 0.0 ] + + fm = region.getFieldmodule() + fm.beginChange() + cache = fm.createFieldcache() + coordinates = getOrCreateCoordinateField(fm) + + nodes = fm.findNodesetByFieldDomainType(Field.DOMAIN_TYPE_NODES) + nodetemplate = nodes.createNodetemplate() + nodetemplate.defineField(coordinates) + nodetemplate.setValueNumberOfVersions(coordinates, -1, Node.VALUE_LABEL_VALUE, 1) + nodetemplate.setValueNumberOfVersions(coordinates, -1, Node.VALUE_LABEL_D_DS1, 1) + nodetemplate.setValueNumberOfVersions(coordinates, -1, Node.VALUE_LABEL_D_DS2, 1) + if useCrossDerivatives: + nodetemplate.setValueNumberOfVersions(coordinates, -1, Node.VALUE_LABEL_D2_DS1DS2, 1) + if useCubicHermiteThroughWall: + nodetemplate.setValueNumberOfVersions(coordinates, -1, Node.VALUE_LABEL_D_DS3, 1) + if useCrossDerivatives: + nodetemplate.setValueNumberOfVersions(coordinates, -1, Node.VALUE_LABEL_D2_DS1DS3, 1) + nodetemplate.setValueNumberOfVersions(coordinates, -1, Node.VALUE_LABEL_D2_DS2DS3, 1) + nodetemplate.setValueNumberOfVersions(coordinates, -1, Node.VALUE_LABEL_D3_DS1DS2DS3, 1) + + mesh = fm.findMeshByDimension(3) + + if useCubicHermiteThroughWall: + eftfactory = eftfactory_tricubichermite(mesh, useCrossDerivatives) + else: + eftfactory = eftfactory_bicubichermitelinear(mesh, useCrossDerivatives) + eft = eftfactory.createEftBasic() + + elementtemplate = mesh.createElementtemplate() + elementtemplate.setElementShapeType(Element.SHAPE_TYPE_CUBE) + result = elementtemplate.defineField(coordinates, -1, eft) + + # create nodes + nodeIdentifier = 1 #nextNodeIdentifier + zero = [ 0.0, 0.0, 0.0 ] + x = [ 0.0, 0.0, 0.0 ] + dx_ds1 = [ 0.0, 0.0, 0.0 ] + dx_ds2 = [ 0.0, 0.0, 0.0 ] + dx_ds3 = [ 0.0, 0.0, 0.0 ] + radiansRangeRC = [7*math.pi/4, 0.0, math.pi/4] + cornerRC = cornerRadiusFraction*radius + tubeAxis = [0.0, 0.0, 1.0] + haustraRadius = (haustraRadiusFraction + 1)*radius + xAround = [] + d1Around = [] + xInner = [] + dx_ds1InnerList = [] + xHaustraSide = [] + xHaustraInner = [] + d1InnerHaustraRaw = [] + xInnerRaw = [] + dx_ds2InnerRaw = [] + xInnerList = [] + dx_ds2InnerList = [] + dx_ds3InnerUnitList = [] + xList = [] + dx_ds1List = [] + dx_ds2List = [] + dx_ds3List = [] + + # Pre-calculate node locations and derivatives on inner triangle + for n1 in range(3): + radiansAround = n1*2*math.pi / 3 + cosRadiansAround = math.cos(radiansAround) + sinRadiansAround = math.sin(radiansAround) + xc = [(radius - cornerRC) * cosRadiansAround, (radius - cornerRC) * sinRadiansAround, 0.0] + + for n in range(3): + radiansRC = radiansAround + radiansRangeRC[n] + cosRadiansRC = math.cos(radiansRC) + sinRadiansRC = math.sin(radiansRC) + x = [xc[0] + cornerRC*cosRadiansRC, xc[1] + cornerRC*sinRadiansRC, 0.0] + xAround.append(x) + d1 = [ cornerRC*math.pi/4 * -sinRadiansRC, cornerRC*math.pi/4 * cosRadiansRC, 0.0] + d1Around.append(d1) + + xSample = xAround[1:9] + xSample.append(xAround[0]) + xSample.append(xAround[1]) + d1Sample = d1Around[1:9] + d1Sample.append(d1Around[0]) + d1Sample.append(d1Around[1]) + sx, sd1, se, sxi, _= sampleCubicHermiteCurves(xSample, d1Sample, elementsCountAround) + xInner = xInner + sx[0:-1] + d1Inner = smoothCubicHermiteDerivativesLoop(sx[0:-1], sd1[0:-1]) + + # Pre-calculate node locations and derivatives on haustra inner cross-section + elementsCountAroundSide = int(elementsCountAround/3) + Ax = xInner[elementsCountAroundSide][0] + Ay = xInner[elementsCountAroundSide][1] + originRC = (Ax*Ax + Ay*Ay - haustraRadius*haustraRadius) / (2*(-Ax - haustraRadius)) + RC = haustraRadius - originRC + + if originRC > -Ax: + startTheta = math.asin(Ay/RC) + thetaRC = (math.pi - startTheta)*2 + else: + startTheta = math.pi - math.asin(Ay/RC) + thetaRC = math.asin(Ay/RC)*2 + thetaPerElementAround = thetaRC/(elementsCountAround/3) + + for n in range(elementsCountAroundSide + 1): + theta = startTheta + thetaPerElementAround * n + x = [RC*math.cos(theta) - originRC, + RC*math.sin(theta), + 0.0] + xHaustraSide.append(x) + + ang = [-2/3*math.pi, 0.0, 2/3*math.pi] + + for i in range(3): + rotAng = ang[i] + cosRotAng = math.cos(rotAng) + sinRotAng = math.sin(rotAng) + for n in range(elementsCountAroundSide): + theta = startTheta + thetaPerElementAround * n + cosTheta = math.cos(theta) + sinTheta = math.sin(theta) + x = [ (RC*cosTheta - originRC)*cosRotAng - RC*sinTheta*sinRotAng, + (RC*cosTheta - originRC)*sinRotAng + RC*sinTheta*cosRotAng, + 0.0] + xHaustraInner.append(x) + dx_ds1 = [(-RC*sinTheta*cosRotAng - RC*cosTheta*sinRotAng)*thetaPerElementAround, + (-RC*sinTheta*sinRotAng + RC*cosTheta*cosRotAng)*thetaPerElementAround, + 0.0] + d1InnerHaustraRaw.append(dx_ds1) + d1InnerHaustra = smoothCubicHermiteDerivativesLoop(xHaustraInner, d1InnerHaustraRaw) + + # Sample arclength of haustra + for n1 in range(elementsCountAround): + if n1%(elementsCountAround/3) > 0.0: + v1 = [xInner[n1][0], xInner[n1][1], 0.0] + startArcLength = foldFactor * haustraLength + d1 = [ c*startArcLength for c in tubeAxis] + v2 = [xHaustraInner[n1][0], xHaustraInner[n1][1], haustraLength/2] + midArcLength = haustraCurvatureFactor * haustraLength + d2 = [ c*midArcLength for c in tubeAxis] + v3 = [xInner[n1][0], xInner[n1][1], haustraLength] + d3 = [ c*startArcLength for c in tubeAxis] + else: + v1 = [xInner[n1][0], xInner[n1][1], 0.0] + v2 = [xInner[n1][0], xInner[n1][1], haustraLength/2] + v3 = [xInner[n1][0], xInner[n1][1], haustraLength] + d3 = d2 = d1 = [c* haustraLength/3 for c in tubeAxis] + nx = [v1, v2, v3] + nd1 = [d1, d2, d3] + sx, sd1, se, sxi, _ = sampleCubicHermiteCurves(nx, nd1, elementsCountAlongHaustra) + xInnerRaw.append(sx) + dx_ds2InnerRaw.append(sd1) + # if n1 == 28: # troubleshoot issue when samples suddenly go back to start + # print('nx = ', nx) + # print(sx) + + # Re-arrange sample order & calculate dx_ds1 and dx_ds3 from dx_ds2 + dx_ds1InnerList = dx_ds1InnerList + d1Inner + for n2 in range(elementsCountAlongHaustra + 1): + xAround = [] + unitdx_ds1Around = [] + for n1 in range(elementsCountAround): + x = xInnerRaw[n1][n2] + xInnerList.append(x) + dx_ds2 = dx_ds2InnerRaw[n1][n2] + dx_ds2InnerList.append(dx_ds2) + + unitTangent = normalise(dx_ds2) + # Interhaustra + if n2 == 0 or n2 > elementsCountAlongHaustra - 1: + dx_ds1 = d1Inner[n1] + unitdx_ds3 = crossproduct3(normalise(dx_ds1), unitTangent) + else: + # Haustra + if elementsCountAlongHaustra == 2: + unitdx_ds1 = normalise(d1InnerHaustra[n1]) + else: + if n1%(elementsCountAround/3) == 0: # intersection points + unitdx_ds1 = normalise(d1InnerHaustra[n1]) + else: # points on clover + if elementsCountAlongHaustra > 3: + if n2 < int(elementsCountAlongHaustra/2): # first half of haustraLength + axisRot = crossproduct3(tubeAxis, unitTangent) + elif n2 > int(elementsCountAlongHaustra/2): # second half of haustraLength + axisRot = crossproduct3(unitTangent, tubeAxis) + elif elementsCountAlongHaustra == 3: # 3 elementsAlongHaustra + axisRot = crossproduct3(unitTangent, tubeAxis) + + rotFrame = rotationMatrixAboutAxis(axisRot, math.pi/2) + rotNormal = [rotFrame[j][0]*unitTangent[0] + rotFrame[j][1]*unitTangent[1] + rotFrame[j][2]*unitTangent[2] for j in range(3)] + unitdx_ds3 = normalise(rotNormal) + unitdx_ds1 = crossproduct3(unitTangent, unitdx_ds3) + xAround.append(x) + unitdx_ds1Around.append(unitdx_ds1) + + if n2 > 0 and n2 < elementsCountAlongHaustra: + dx_ds1InnerAroundList = [] + for n1 in range(elementsCountAround): + v1 = xAround[n1] + d1 = unitdx_ds1Around[n1] + v2 = xAround[(n1+1)%elementsCountAround] + d2 = unitdx_ds1Around[(n1+1)%elementsCountAround] + arcLengthAround = computeCubicHermiteArcLength(v1, d1, v2, d2, True) + dx_ds1 = [c*arcLengthAround for c in d1] + dx_ds1InnerAroundList.append(dx_ds1) + d1Smoothed = smoothCubicHermiteDerivativesLoop(xAround, dx_ds1InnerAroundList) + dx_ds1InnerList = dx_ds1InnerList + d1Smoothed + + dx_ds1InnerList = dx_ds1InnerList + d1Inner + + for n in range(len(xInnerList)): + dx_ds3 = crossproduct3(normalise(dx_ds1InnerList[n]), normalise( dx_ds2InnerList[n])) + unitdx_ds3 = normalise(dx_ds3) + dx_ds3InnerUnitList.append(unitdx_ds3) + + # Pre-calculate node locations and derivatives on outer boundary + xOuterList, d1Outer, curvatureInner = getOuterCoordinatesAndDerivativesFromInner(xInnerList, dx_ds1InnerList, dx_ds3InnerUnitList, wallThickness, elementsCountAlongHaustra, elementsCountAround) + + # Interpolate to get nodes through wall + for n3 in range(elementsCountThroughWall + 1): + xi3 = 1/elementsCountThroughWall * n3 + for nH in range(haustraCount): + if nH == 0: + x, dx_ds1, dx_ds2, dx_ds3 = interpolatefromInnerAndOuter( xInnerList, xOuterList, wallThickness, xi3, curvatureInner, dx_ds1InnerList, dx_ds2InnerList, dx_ds3InnerUnitList, elementsCountAround, elementsCountAlongHaustra, elementsCountThroughWall) + xList = xList + x + dx_ds1List = dx_ds1List + dx_ds1 + dx_ds2List = dx_ds2List + dx_ds2 + dx_ds3List = dx_ds3List + dx_ds3 + else: + xNext = [] + for n in range(elementsCountAround, len(x)): + newX = [x[n][0], x[n][1], x[n][2] + haustraLength*nH] + xNext.append(newX) + xList = xList + xNext + dx_ds1List = dx_ds1List + dx_ds1[elementsCountAround:] + dx_ds2List = dx_ds2List + dx_ds2[elementsCountAround:] + dx_ds3List = dx_ds3List + dx_ds3[elementsCountAround:] + + for n in range(len(xList)): + node = nodes.createNode(nodeIdentifier, nodetemplate) + cache.setNode(node) + coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_VALUE, 1, xList[n]) + coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS1, 1, dx_ds1List[n]) + coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS2, 1, dx_ds2List[n]) + coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS3, 1, dx_ds3List[n]) + # print('NodeIdentifier = ', nodeIdentifier, xList[n]) + nodeIdentifier = nodeIdentifier + 1 + + # # For debugging - Nodes along central line + # for pt in range(len(sx)): + # node = nodes.createNode(nodeIdentifier, nodetemplate) + # cache.setNode(node) + # coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_VALUE, 1, sx[pt]) + # coordinates.setNodeParametrs(cache, -1, Node.VALUE_LABEL_D_DS1, 1, sd1[pt]) + # coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS2, 1, sNormal[pt]) + # coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS3, 1, sBinormal[pt]) + # nodeIdentifier = nodeIdentifier + 1 + + # create elements + elementIdentifier = 1 # nextElementIdentifier + now = (elementsCountAlong + 1)*elementsCountAround + for e3 in range(elementsCountThroughWall): + for e2 in range(elementsCountAlong): + for e1 in range(elementsCountAround): + element = mesh.createElement(elementIdentifier, elementtemplate) + bni11 = e3*now + e2*elementsCountAround + e1 + 1 + bni12 = e3*now + e2*elementsCountAround + (e1 + 1)%elementsCountAround + 1 + bni21 = e3*now + (e2 + 1)*elementsCountAround + e1 + 1 + bni22 = e3*now + (e2 + 1)*elementsCountAround + (e1 + 1)%elementsCountAround + 1 + nodeIdentifiers = [ bni11, bni12, bni21, bni22, bni11 + now, bni12 + now, bni21 + now, bni22 + now ] + result = element.setNodesByIdentifier(eft, nodeIdentifiers) + elementIdentifier = elementIdentifier + 1 + + fm.endChange() + + @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(). + """ + if not options['Refine']: + cls.generateBaseMesh(region, options) + return + + refineElementsCountAround = options['Refine number of elements around'] + refineElementsCountAlong = options['Refine number of elements along'] + refineElementsCountThroughWall = options['Refine number of elements through wall'] + + baseRegion = region.createRegion() + cls.generateBaseMesh(baseRegion, options) + + meshrefinement = MeshRefinement(baseRegion, region) + meshrefinement.refineAllElementsCubeStandard3d(refineElementsCountAround, refineElementsCountAlong, refineElementsCountThroughWall) + +def getOuterCoordinatesAndDerivativesFromInner(xInner, d1Inner, d3Inner, wallThickness, elementsCountAlongHaustra, elementsCountAround): + """ + Generates coordinates and derivatives of outer surface from + coordinates and derivatives of inner surface using wall thickness + and normals. + param xInner: Coordinates on inner surface + param d1Inner: Derivatives on inner surface around colon + param d3Inner: Derivatives on inner surface through wall + param wallThickness: Thickness of wall + param elementsCountAlongHaustra: Number of elements along haustra + param elementsCountAround: Number of elements around colon + return xOuter: Coordinates on outer surface + return nd1: Derivatives on outer surface around colon + return curvatureInner: Curvature of coordinates on inner surface + """ + xOuter = [] + nd1 = [] + dWall = [] + curvatureInner = [] + for n2 in range(elementsCountAlongHaustra + 1): + for n1 in range(elementsCountAround): + n = n2*elementsCountAround + n1 + x = [xInner[n][i] + d3Inner[n][i]*wallThickness for i in range(3)] + prevIdx = n-1 if (n1 != 0) else (n2+1)*elementsCountAround - 1 + nextIdx = n+1 if (n1 < elementsCountAround-1) else n2*elementsCountAround + curvatureAround = 0.5*( + getCubicHermiteCurvature(xInner[prevIdx], d1Inner[prevIdx], xInner[n], d1Inner[n], d3Inner[n], 1.0) + + getCubicHermiteCurvature(xInner[n], d1Inner[n], xInner[nextIdx], d1Inner[nextIdx], d3Inner[n], 0.0)) + factor = 1.0 - curvatureAround*wallThickness + nd1Outer = [ factor*c for c in d1Inner[n]] + xOuter.append(x) + nd1.append(nd1Outer) + curvatureInner.append(curvatureAround) + + return xOuter, nd1, curvatureInner + +def interpolatefromInnerAndOuter( xInner, xOuter, thickness, xi3, curvatureInner, d1Inner, d2Inner, d3InnerUnit, elementsCountAround, elementsCountAlongHaustra, elementsCountThroughWall): + """ + Generate coordinates and derivatives at xi3 by interpolating with + inner and outer coordinates and derivatives. + param xInner: Coordinates on inner surface + param xOuter: Coordinates on outer surface + param thickness: Thickness of wall + param curvatureInner: Curvature of coordinates on inner surface + param d1Inner: Derivatives on inner surface around colon + param d2Inner: Derivatives on inner surface along haustra + param d3InnerUnit: Unit derivatives on inner surface through wall + param elementsCountAround: Number of elements around colon + param elementsCountAlongHaustra: Number of elements along haustra + param elementsCountThroughWall: Number of elements through wall + return xList, dx_ds1List, dx_ds2List, dx_ds3List: Coordinates and derivatives on xi3 + """ + xList = [] + dx_ds1List = [] + dx_ds2List = [] + dx_ds3List =[] + + for n2 in range(elementsCountAlongHaustra+1): + for n1 in range(elementsCountAround): + n = n2*elementsCountAround + n1 + # x + innerx = xInner[n] + outerx = xOuter[n] + dWall = [thickness*c for c in d3InnerUnit[n]] + x = interpolateCubicHermite(innerx, dWall, outerx, dWall, xi3) + xList.append(x) + # dx_ds1 + factor = 1.0 - curvatureInner[n]*thickness*xi3 + dx_ds1 = [ factor*c for c in d1Inner[n]] + dx_ds1List.append(dx_ds1) + # dx_ds2 + norm = d3InnerUnit[n] + if n2 > 0 and n2 < elementsCountAlongHaustra: + prevIdx = (n2-1)*elementsCountAround + n1 + nextIdx = (n2+1)*elementsCountAround + n1 + elif n2 == 0: + prevIdx = (elementsCountAlongHaustra-1)*elementsCountAround + n1 + nextIdx = (n2+1)*elementsCountAround + n1 + else: + prevIdx = (n2-1)*elementsCountAround + n1 + nextIdx = elementsCountAround + n1 + curvatureAround = 0.5*( + getCubicHermiteCurvature(xInner[prevIdx], d2Inner[prevIdx], xInner[n], d2Inner[n], norm, 1.0)+ + getCubicHermiteCurvature(xInner[n], d2Inner[n], xInner[nextIdx], d2Inner[nextIdx], norm, 0.0)) + factor = 1.0 - curvatureAround*thickness*xi3 + dx_ds2 = [ factor*c for c in d2Inner[n]] + dx_ds2List.append(dx_ds2) + #dx_ds3 + dx_ds3 = [c * thickness/elementsCountThroughWall for c in d3InnerUnit[n]] + dx_ds3List.append(dx_ds3) + + return xList, dx_ds1List, dx_ds2List, dx_ds3List + +def rotationMatrixAboutAxis(rotAxis, theta): + """ + Generate the rotation matrix for rotation about an axis. + :param rotAxis: axis of rotation + :param theta: angle of rotation + :return: rotation matrix + """ + cosTheta = math.cos(theta) + sinTheta = math.sin(theta) + C = 1 - cosTheta + rotMatrix = ([[rotAxis[0]*rotAxis[0]*C + cosTheta, rotAxis[0]*rotAxis[1]*C - rotAxis[2]*sinTheta, rotAxis[0]*rotAxis[2]*C + rotAxis[1]*sinTheta], + [rotAxis[1]*rotAxis[0]*C + rotAxis[2]*sinTheta, rotAxis[1]*rotAxis[1]*C + cosTheta, rotAxis[1]*rotAxis[2]*C - rotAxis[0]*sinTheta], + [rotAxis[2]*rotAxis[0]*C - rotAxis[1]*sinTheta, rotAxis[2]*rotAxis[1]*C + rotAxis[0]*sinTheta, rotAxis[2]*rotAxis[2]*C + cosTheta]]) + return rotMatrix \ No newline at end of file diff --git a/scaffoldmaker/scaffolds.py b/scaffoldmaker/scaffolds.py index d1004908..d0450194 100644 --- a/scaffoldmaker/scaffolds.py +++ b/scaffoldmaker/scaffolds.py @@ -9,6 +9,7 @@ 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_centrallinetube1 import MeshType_3d_centrallinetube1 +from scaffoldmaker.meshtypes.meshtype_3d_colon1 import MeshType_3d_colon1 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_heartarterialroot1 import MeshType_3d_heartarterialroot1 @@ -38,6 +39,7 @@ def __init__(self): MeshType_3d_box1, MeshType_3d_boxhole1, MeshType_3d_centrallinetube1, + MeshType_3d_colon1, MeshType_3d_heart1, MeshType_3d_heart2, MeshType_3d_heartarterialroot1, From ac2a5993d0f05cf67e30d17397b4f32bf2e30728 Mon Sep 17 00:00:00 2001 From: Mabelle Lin Date: Tue, 12 Feb 2019 14:00:23 +1300 Subject: [PATCH 03/44] Rename 3D Colon 1 to 3D Haustra 1 --- ...e_3d_colon1.py => meshtype_3d_haustra1.py} | 62 +++++-------------- scaffoldmaker/scaffolds.py | 4 +- 2 files changed, 18 insertions(+), 48 deletions(-) rename scaffoldmaker/meshtypes/{meshtype_3d_colon1.py => meshtype_3d_haustra1.py} (91%) diff --git a/scaffoldmaker/meshtypes/meshtype_3d_colon1.py b/scaffoldmaker/meshtypes/meshtype_3d_haustra1.py similarity index 91% rename from scaffoldmaker/meshtypes/meshtype_3d_colon1.py rename to scaffoldmaker/meshtypes/meshtype_3d_haustra1.py index c40a2a1a..3b60a0bb 100644 --- a/scaffoldmaker/meshtypes/meshtype_3d_colon1.py +++ b/scaffoldmaker/meshtypes/meshtype_3d_haustra1.py @@ -1,5 +1,5 @@ """ -Generates a 3-D colon mesh along the central line, with variable +Generates a 3-D haustra mesh along a central line, with variable numbers of elements around, along and through wall, with variable radius and thickness along. """ @@ -7,18 +7,16 @@ from scaffoldmaker.utils.meshrefinement import MeshRefinement from scaffoldmaker.utils.tubemesh import * -class MeshType_3d_colon1(object): +class MeshType_3d_haustra1(object): ''' - Generates a 3-D colon mesh with variable numbers + Generates a 3-D haustra mesh with variable numbers of elements around, along the central line, and through wall. - The colon has a triangular profile with rounded corners at the - interhaustral septa which is created from a central line and - lateral axes data. The colon presents a clover profile outside - the septa. + The haustra has a triangular profile with rounded corners at the + interhaustral septa, and a clover profile outside the septa. ''' @staticmethod def getName(): - return '3D Colon 1' + return '3D Haustra 1' @staticmethod def getDefaultOptions(): @@ -29,12 +27,11 @@ def getDefaultOptions(): 'Inner radius': 0.5, 'Corner radius fraction': 0.5, 'Wall thickness': 0.01, - 'Number of haustra': 2, + 'Number of haustra': 1, 'Haustra radius fraction': 0.5, 'Haustra length': 1.0, 'Interhaustra fold factor': 0.5, 'Haustra curvature factor': 1.0, - 'Tube type': 1, 'Use cross derivatives' : False, 'Use linear through wall' : False, 'Refine' : False, @@ -57,8 +54,7 @@ def getOrderedOptionNames(): 'Haustra length', 'Interhaustra fold factor', 'Haustra curvature factor', - 'Tube type', - 'Use cross derivatives', + 'Use cross derivatives', 'Use linear through wall', 'Refine', 'Refine number of elements around', @@ -97,8 +93,6 @@ def checkOptions(options): 'Interhaustra fold factor']: if options[key] > 1.0: options[key] = 1.0 - if (options['Tube type'] < 1 or options['Tube type'] > 3 ) : - options['Tube type'] = 1 @staticmethod def generateBaseMesh(region, options): @@ -119,24 +113,10 @@ def generateBaseMesh(region, options): haustraLength = options['Haustra length'] foldFactor = options['Interhaustra fold factor'] haustraCurvatureFactor = options['Haustra curvature factor'] - tubeType = options['Tube type'] useCrossDerivatives = options['Use cross derivatives'] useCubicHermiteThroughWall = not(options['Use linear through wall']) elementsCountAlong = int(elementsCountAlongHaustra*haustraCount) - # if tubeType == 1: - # # Straight tube - # cx = [[0.0, 0.0, 0.0], [ 0.0, 0.0, 5.0 ] ] - # cd1 = [[ 0.0, 0.0, 5.0 ], [ 0.0, 0.0, 5.0 ]] - # cd2 = [ [ 0.0, 0.5, 0.0 ], [ 0.0, 0.5, 0.0 ]] - # cd3 = [ [ 0.0, 0.0, 0.5 ], [ 0.0, 0.0, 0.5 ]] - - # # thickness in cd2 and cd3 directions and derivatives (rate of change) - # t2 = [ 0.2, 0.2 ] - # t2d = [ 0.0, 0.0 ] - # t3 = [ 0.2, 0.2 ] - # t3d = [ 0.0, 0.0 ] - fm = region.getFieldmodule() fm.beginChange() cache = fm.createFieldcache() @@ -178,7 +158,7 @@ def generateBaseMesh(region, options): dx_ds3 = [ 0.0, 0.0, 0.0 ] radiansRangeRC = [7*math.pi/4, 0.0, math.pi/4] cornerRC = cornerRadiusFraction*radius - tubeAxis = [0.0, 0.0, 1.0] + unitZ = [0.0, 0.0, 1.0] haustraRadius = (haustraRadiusFraction + 1)*radius xAround = [] d1Around = [] @@ -270,17 +250,17 @@ def generateBaseMesh(region, options): if n1%(elementsCountAround/3) > 0.0: v1 = [xInner[n1][0], xInner[n1][1], 0.0] startArcLength = foldFactor * haustraLength - d1 = [ c*startArcLength for c in tubeAxis] + d1 = [ c*startArcLength for c in unitZ] v2 = [xHaustraInner[n1][0], xHaustraInner[n1][1], haustraLength/2] midArcLength = haustraCurvatureFactor * haustraLength - d2 = [ c*midArcLength for c in tubeAxis] + d2 = [ c*midArcLength for c in unitZ] v3 = [xInner[n1][0], xInner[n1][1], haustraLength] - d3 = [ c*startArcLength for c in tubeAxis] + d3 = [ c*startArcLength for c in unitZ] else: v1 = [xInner[n1][0], xInner[n1][1], 0.0] v2 = [xInner[n1][0], xInner[n1][1], haustraLength/2] v3 = [xInner[n1][0], xInner[n1][1], haustraLength] - d3 = d2 = d1 = [c* haustraLength/3 for c in tubeAxis] + d3 = d2 = d1 = [c* haustraLength/3 for c in unitZ] nx = [v1, v2, v3] nd1 = [d1, d2, d3] sx, sd1, se, sxi, _ = sampleCubicHermiteCurves(nx, nd1, elementsCountAlongHaustra) @@ -316,11 +296,11 @@ def generateBaseMesh(region, options): else: # points on clover if elementsCountAlongHaustra > 3: if n2 < int(elementsCountAlongHaustra/2): # first half of haustraLength - axisRot = crossproduct3(tubeAxis, unitTangent) + axisRot = crossproduct3(unitZ, unitTangent) elif n2 > int(elementsCountAlongHaustra/2): # second half of haustraLength - axisRot = crossproduct3(unitTangent, tubeAxis) + axisRot = crossproduct3(unitTangent, unitZ) elif elementsCountAlongHaustra == 3: # 3 elementsAlongHaustra - axisRot = crossproduct3(unitTangent, tubeAxis) + axisRot = crossproduct3(unitTangent, unitZ) rotFrame = rotationMatrixAboutAxis(axisRot, math.pi/2) rotNormal = [rotFrame[j][0]*unitTangent[0] + rotFrame[j][1]*unitTangent[1] + rotFrame[j][2]*unitTangent[2] for j in range(3)] @@ -382,16 +362,6 @@ def generateBaseMesh(region, options): # print('NodeIdentifier = ', nodeIdentifier, xList[n]) nodeIdentifier = nodeIdentifier + 1 - # # For debugging - Nodes along central line - # for pt in range(len(sx)): - # node = nodes.createNode(nodeIdentifier, nodetemplate) - # cache.setNode(node) - # coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_VALUE, 1, sx[pt]) - # coordinates.setNodeParametrs(cache, -1, Node.VALUE_LABEL_D_DS1, 1, sd1[pt]) - # coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS2, 1, sNormal[pt]) - # coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS3, 1, sBinormal[pt]) - # nodeIdentifier = nodeIdentifier + 1 - # create elements elementIdentifier = 1 # nextElementIdentifier now = (elementsCountAlong + 1)*elementsCountAround diff --git a/scaffoldmaker/scaffolds.py b/scaffoldmaker/scaffolds.py index d0450194..e752c79a 100644 --- a/scaffoldmaker/scaffolds.py +++ b/scaffoldmaker/scaffolds.py @@ -9,7 +9,7 @@ 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_centrallinetube1 import MeshType_3d_centrallinetube1 -from scaffoldmaker.meshtypes.meshtype_3d_colon1 import MeshType_3d_colon1 +from scaffoldmaker.meshtypes.meshtype_3d_haustra1 import MeshType_3d_haustra1 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_heartarterialroot1 import MeshType_3d_heartarterialroot1 @@ -39,7 +39,7 @@ def __init__(self): MeshType_3d_box1, MeshType_3d_boxhole1, MeshType_3d_centrallinetube1, - MeshType_3d_colon1, + MeshType_3d_haustra1, MeshType_3d_heart1, MeshType_3d_heart2, MeshType_3d_heartarterialroot1, From 36cd65bae31ab1526cadc956604eb8bcab5d20a0 Mon Sep 17 00:00:00 2001 From: Mabelle Lin Date: Tue, 12 Feb 2019 14:16:27 +1300 Subject: [PATCH 04/44] Add cross derivatives --- scaffoldmaker/meshtypes/meshtype_3d_haustra1.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/scaffoldmaker/meshtypes/meshtype_3d_haustra1.py b/scaffoldmaker/meshtypes/meshtype_3d_haustra1.py index 3b60a0bb..74106db3 100644 --- a/scaffoldmaker/meshtypes/meshtype_3d_haustra1.py +++ b/scaffoldmaker/meshtypes/meshtype_3d_haustra1.py @@ -360,6 +360,11 @@ def generateBaseMesh(region, options): coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS2, 1, dx_ds2List[n]) coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS3, 1, dx_ds3List[n]) # print('NodeIdentifier = ', nodeIdentifier, xList[n]) + if useCrossDerivatives: + coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D2_DS1DS2, 1, zero) + coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D2_DS1DS3, 1, zero) + coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D2_DS2DS3, 1, zero) + coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D3_DS1DS2DS3, 1, zero) nodeIdentifier = nodeIdentifier + 1 # create elements From 503b53e4ed72d6461f680b3b912f531c2b272425 Mon Sep 17 00:00:00 2001 From: Mabelle Lin Date: Mon, 18 Feb 2019 17:07:12 +1300 Subject: [PATCH 05/44] Generate colon from haustra unit with tubeMesh --- scaffoldmaker/meshtypes/meshtype_3d_colon1.py | 441 ++++++++++++++++++ scaffoldmaker/scaffolds.py | 2 + scaffoldmaker/utils/tubemesh2.py | 341 ++++++++++++++ 3 files changed, 784 insertions(+) create mode 100644 scaffoldmaker/meshtypes/meshtype_3d_colon1.py create mode 100644 scaffoldmaker/utils/tubemesh2.py diff --git a/scaffoldmaker/meshtypes/meshtype_3d_colon1.py b/scaffoldmaker/meshtypes/meshtype_3d_colon1.py new file mode 100644 index 00000000..ed6eaa77 --- /dev/null +++ b/scaffoldmaker/meshtypes/meshtype_3d_colon1.py @@ -0,0 +1,441 @@ +""" +Generates a 3-D colon mesh along the central line, with variable +numbers of elements around, along and through wall, with +variable radius and thickness along. +""" + +from scaffoldmaker.utils.eftfactory_bicubichermitelinear import eftfactory_bicubichermitelinear +from scaffoldmaker.utils.eftfactory_tricubichermite import eftfactory_tricubichermite +from scaffoldmaker.utils.meshrefinement import MeshRefinement +from scaffoldmaker.utils.tubemesh2 import * +from scaffoldmaker.utils.zinc_utils import * +from scaffoldmaker.utils.geometry import * +from scaffoldmaker.utils.interpolation import * +from scaffoldmaker.utils.vector import * +from opencmiss.zinc.element import Element, Elementbasis +from opencmiss.zinc.field import Field +from opencmiss.zinc.node import Node + +class MeshType_3d_colon1(object): + ''' + Generates a 3-D colon mesh with variable numbers + of elements around, along the central line, and through wall. + The colon is created by a function that deforms haustras along + the central line based on the curvature of the line. + ''' + @staticmethod + def getName(): + return '3D Colon 1' + + @staticmethod + def getDefaultOptions(): + return { + 'Number of elements around' : 9, + 'Number of elements along haustra' : 3, + 'Number of elements through wall' : 1, + 'Number of haustra': 2, + 'Inner radius': 0.5, + 'Corner radius fraction': 0.5, + 'Haustra radius fraction': 0.5, + 'Interhaustra fold factor': 0.5, + 'Haustra curvature factor': 1.0, + 'Wall thickness': 0.01, + 'Tube type': 1, + 'Use cross derivatives' : False, + 'Use linear through wall' : False, + 'Refine' : False, + 'Refine number of elements around' : 1, + 'Refine number of elements along' : 1, + 'Refine number of elements through wall' : 1 + } + + @staticmethod + def getOrderedOptionNames(): + return [ + 'Number of elements around', + 'Number of elements along haustra', + 'Number of elements through wall', + 'Number of haustra', + 'Inner radius', + 'Corner radius fraction', + 'Haustra radius fraction', + 'Interhaustra fold factor', + 'Haustra curvature factor', + 'Wall thickness', + 'Tube type', + 'Use cross derivatives', + 'Use linear through wall', + 'Refine', + 'Refine number of elements around', + 'Refine number of elements along', + 'Refine number of elements through wall' + ] + + def checkOptions(options): + for key in [ + 'Number of elements through wall', + 'Number of haustra', + 'Refine number of elements around', + 'Refine number of elements along', + 'Refine number of elements through wall']: + if options[key] < 1: + options[key] = 1 + if (options['Number of elements around'] < 9) : + options['Number of elements around'] = 9 + if (options['Number of elements around'] % 3 != 0) : + options['Number of elements around'] = (options['Number of elements around']//3)*3 + for key in [ + 'Inner radius', + 'Haustra radius fraction', + 'Interhaustra fold factor', + 'Haustra curvature factor', + 'Wall thickness']: + if options[key] < 0.0: + options[key] = 0.0 + if options['Corner radius fraction'] < 0.1: + options['Corner radius fraction'] = 0.1 + for key in [ + 'Corner radius fraction', + 'Interhaustra fold factor']: + if options[key] > 1.0: + options[key] = 1.0 + if (options['Tube type'] < 1 or options['Tube type'] > 2 ) : + options['Tube type'] = 1 + + @staticmethod + def generateBaseMesh(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: None + """ + elementsCountAround = options['Number of elements around'] + elementsCountAlongHaustra = options['Number of elements along haustra'] + elementsCountThroughWall = options['Number of elements through wall'] + haustraCount = options['Number of haustra'] + radius = options['Inner radius'] + cornerRadiusFraction = options['Corner radius fraction'] + haustraRadiusFraction = options['Haustra radius fraction'] + foldFactor = options['Interhaustra fold factor'] + haustraCurvatureFactor = options['Haustra curvature factor'] + wallThickness = options['Wall thickness'] + tubeType = options['Tube type'] + useCrossDerivatives = options['Use cross derivatives'] + useCubicHermiteThroughWall = not(options['Use linear through wall']) + elementsCountAlong = int(elementsCountAlongHaustra*haustraCount) + + if tubeType == 1: # Straight tube + cx = [[-4.0, 1.0, 3.0], [ 1.0, 2.0, 0.0 ] ] + cd1 = [[ 5.0, 1.0, -3.0 ], [ 5.0, 1.0, -3.0 ]] + elif tubeType == 2: # Human colon + cx = [ [ 0.0, 0.0, 0.0], [0.0, 10.0, 0.0], [5.0, 9.0, 0.0], [ 10.0, 10.0, 0.0 ], [ 10.0, -2.0, 0.0], [ 7.0, -4.0, 0.0] ] + cd1 = [ [ 0.0, 10.0, 0.0 ], [ 5.0, 5.0, 0.0 ], [5.0, 0.0, 0.0], [ 5.0, -5.0, 0.0 ], [ -3.0, -5.0, 0.0 ], [ -3.0, 0.0, 0.0 ]] + + length = 0.0 + # find arclength of colon + elementsCountIn = len(cx) - 1 + for e in range(elementsCountIn): + arcLength = computeCubicHermiteArcLength(cx[e], cd1[e], cx[e + 1], cd1[e + 1], rescaleDerivatives = True) + length += arcLength + haustraLength = length / haustraCount + + # Generate outer surface of a haustra unit + xHaustraOuter, d1HaustraOuter, d2HaustraOuter, haustraAxis = getUnitHaustraOuter(elementsCountAround, elementsCountAlongHaustra, radius, cornerRadiusFraction, + haustraRadiusFraction, foldFactor, haustraCurvatureFactor, wallThickness, haustraLength) + + # Generate tube mesh + nextNodeIdentifier, nextElementIdentifier = generatetubemesh(region, elementsCountAround, elementsCountAlongHaustra, elementsCountThroughWall, haustraCount, + cx, cd1, xHaustraOuter, d1HaustraOuter, d2HaustraOuter, wallThickness, haustraAxis, haustraLength, useCrossDerivatives, useCubicHermiteThroughWall) + + @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(). + """ + if not options['Refine']: + cls.generateBaseMesh(region, options) + return + + refineElementsCountAround = options['Refine number of elements around'] + refineElementsCountAlong = options['Refine number of elements along'] + refineElementsCountThroughWall = options['Refine number of elements through wall'] + + baseRegion = region.createRegion() + cls.generateBaseMesh(baseRegion, options) + + meshrefinement = MeshRefinement(baseRegion, region) + meshrefinement.refineAllElementsCubeStandard3d(refineElementsCountAround, refineElementsCountAlong, refineElementsCountThroughWall) + +def getUnitHaustraOuter(elementsCountAround, elementsCountAlongHaustra, radius, cornerRadiusFraction, + haustraRadiusFraction, foldFactor, haustraCurvatureFactor, wallThickness, haustraLength): + """ + Generates a 3-D haustra mesh with variable numbers + of elements around, along the central line, and through wall. + The haustra has a triangular profile with rounded corners at the + interhaustral septa, and a clover profile outside the septa. + :param elementsCountAround: Number of elements around haustra. + :param elementsCountAlongHaustra: Number of elements along haustra. + :param radius: Inner radius of haustra. + :param cornerRadiusFraction: Roundness of triangular corners of + interhaustral septa. Factor is multiplied by inner haustra radius + to get a radius of curvature at the corners. + :param haustraRadiusFraction: Factor is multiplied by inner haustra + radius to obtain radius of intersecting circles in the middle cross-section + of haustra. + :param foldFactor: Factor is multiplied by haustra length to scale derivative + along the haustra at the interhaustral septa end. + :param haustraCurvatureFactor: Fator is multiplied by haustra length to + scale derivative along the haustra at the middle section of the haustra. + :param wallThickness: Thickness of haustra through wall. + :param haustraLength: Length of a haustra unit. + :return: coordinates, derivatives on outer surface + """ + + # create nodes + x = [ 0.0, 0.0, 0.0 ] + dx_ds1 = [ 0.0, 0.0, 0.0 ] + dx_ds2 = [ 0.0, 0.0, 0.0 ] + dx_ds3 = [ 0.0, 0.0, 0.0 ] + radiansRangeRC = [7*math.pi/4, 0.0, math.pi/4] + cornerRC = cornerRadiusFraction*radius + unitZ = [0.0, 0.0, 1.0] + haustraRadius = (haustraRadiusFraction + 1)*radius + xAround = [] + d1Around = [] + xInner = [] + dx_ds1InnerList = [] + xHaustraSide = [] + xHaustraInner = [] + d1InnerHaustraRaw = [] + xInnerRaw = [] + dx_ds2InnerRaw = [] + xInnerList = [] + dx_ds2InnerList = [] + dx_ds3InnerUnitList = [] + + # Pre-calculate node locations and derivatives on inner triangle + for n1 in range(3): + radiansAround = n1*2*math.pi / 3 + cosRadiansAround = math.cos(radiansAround) + sinRadiansAround = math.sin(radiansAround) + xc = [(radius - cornerRC) * cosRadiansAround, (radius - cornerRC) * sinRadiansAround, 0.0] + + for n in range(3): + radiansRC = radiansAround + radiansRangeRC[n] + cosRadiansRC = math.cos(radiansRC) + sinRadiansRC = math.sin(radiansRC) + x = [xc[0] + cornerRC*cosRadiansRC, xc[1] + cornerRC*sinRadiansRC, 0.0] + xAround.append(x) + d1 = [ cornerRC*math.pi/4 * -sinRadiansRC, cornerRC*math.pi/4 * cosRadiansRC, 0.0] + d1Around.append(d1) + + xSample = xAround[1:9] + xSample.append(xAround[0]) + xSample.append(xAround[1]) + d1Sample = d1Around[1:9] + d1Sample.append(d1Around[0]) + d1Sample.append(d1Around[1]) + sx, sd1, se, sxi, _= sampleCubicHermiteCurves(xSample, d1Sample, elementsCountAround) + xInner = xInner + sx[0:-1] + d1Inner = smoothCubicHermiteDerivativesLoop(sx[0:-1], sd1[0:-1]) + + # Pre-calculate node locations and derivatives on haustra inner cross-section + elementsCountAroundSide = int(elementsCountAround/3) + Ax = xInner[elementsCountAroundSide][0] + Ay = xInner[elementsCountAroundSide][1] + originRC = (Ax*Ax + Ay*Ay - haustraRadius*haustraRadius) / (2*(-Ax - haustraRadius)) + RC = haustraRadius - originRC + + if originRC > -Ax: + startTheta = math.asin(Ay/RC) + thetaRC = (math.pi - startTheta)*2 + else: + startTheta = math.pi - math.asin(Ay/RC) + thetaRC = math.asin(Ay/RC)*2 + thetaPerElementAround = thetaRC/(elementsCountAround/3) + + for n in range(elementsCountAroundSide + 1): + theta = startTheta + thetaPerElementAround * n + x = [RC*math.cos(theta) - originRC, + RC*math.sin(theta), + 0.0] + xHaustraSide.append(x) + + ang = [-2/3*math.pi, 0.0, 2/3*math.pi] + + for i in range(3): + rotAng = ang[i] + cosRotAng = math.cos(rotAng) + sinRotAng = math.sin(rotAng) + for n in range(elementsCountAroundSide): + theta = startTheta + thetaPerElementAround * n + cosTheta = math.cos(theta) + sinTheta = math.sin(theta) + x = [ (RC*cosTheta - originRC)*cosRotAng - RC*sinTheta*sinRotAng, + (RC*cosTheta - originRC)*sinRotAng + RC*sinTheta*cosRotAng, + 0.0] + xHaustraInner.append(x) + dx_ds1 = [(-RC*sinTheta*cosRotAng - RC*cosTheta*sinRotAng)*thetaPerElementAround, + (-RC*sinTheta*sinRotAng + RC*cosTheta*cosRotAng)*thetaPerElementAround, + 0.0] + d1InnerHaustraRaw.append(dx_ds1) + d1InnerHaustra = smoothCubicHermiteDerivativesLoop(xHaustraInner, d1InnerHaustraRaw) + + # Sample arclength of haustra + for n1 in range(elementsCountAround): + if n1%(elementsCountAround/3) > 0.0: + v1 = [xInner[n1][0], xInner[n1][1], 0.0] + startArcLength = foldFactor * haustraLength + d1 = [ c*startArcLength for c in unitZ] + v2 = [xHaustraInner[n1][0], xHaustraInner[n1][1], haustraLength/2] + midArcLength = haustraCurvatureFactor * haustraLength + d2 = [ c*midArcLength for c in unitZ] + v3 = [xInner[n1][0], xInner[n1][1], haustraLength] + d3 = [ c*startArcLength for c in unitZ] + else: + v1 = [xInner[n1][0], xInner[n1][1], 0.0] + v2 = [xInner[n1][0], xInner[n1][1], haustraLength/2] + v3 = [xInner[n1][0], xInner[n1][1], haustraLength] + d3 = d2 = d1 = [c* haustraLength/3 for c in unitZ] + nx = [v1, v2, v3] + nd1 = [d1, d2, d3] + sx, sd1, se, sxi, _ = sampleCubicHermiteCurves(nx, nd1, elementsCountAlongHaustra) + xInnerRaw.append(sx) + dx_ds2InnerRaw.append(sd1) + + # Re-arrange sample order & calculate dx_ds1 and dx_ds3 from dx_ds2 + dx_ds1InnerList = dx_ds1InnerList + d1Inner + for n2 in range(elementsCountAlongHaustra + 1): + xAround = [] + unitdx_ds1Around = [] + for n1 in range(elementsCountAround): + x = xInnerRaw[n1][n2] + xInnerList.append(x) + dx_ds2 = dx_ds2InnerRaw[n1][n2] + dx_ds2InnerList.append(dx_ds2) + + unitTangent = normalise(dx_ds2) + # Interhaustra + if n2 == 0 or n2 > elementsCountAlongHaustra - 1: + dx_ds1 = d1Inner[n1] + unitdx_ds3 = crossproduct3(normalise(dx_ds1), unitTangent) + else: + # Haustra + if elementsCountAlongHaustra == 2: + unitdx_ds1 = normalise(d1InnerHaustra[n1]) + else: + if n1%(elementsCountAround/3) == 0: # intersection points + unitdx_ds1 = normalise(d1InnerHaustra[n1]) + else: # points on clover + if elementsCountAlongHaustra > 3: + if n2 < int(elementsCountAlongHaustra/2): # first half of haustraLength + axisRot = crossproduct3(unitZ, unitTangent) + elif n2 > int(elementsCountAlongHaustra/2): # second half of haustraLength + axisRot = crossproduct3(unitTangent, unitZ) + elif elementsCountAlongHaustra == 3: # 3 elementsAlongHaustra + axisRot = crossproduct3(unitTangent, unitZ) + + rotFrame = rotationMatrixAboutAxis(axisRot, math.pi/2) + rotNormal = [rotFrame[j][0]*unitTangent[0] + rotFrame[j][1]*unitTangent[1] + rotFrame[j][2]*unitTangent[2] for j in range(3)] + unitdx_ds3 = normalise(rotNormal) + unitdx_ds1 = crossproduct3(unitTangent, unitdx_ds3) + xAround.append(x) + unitdx_ds1Around.append(unitdx_ds1) + + if n2 > 0 and n2 < elementsCountAlongHaustra: + dx_ds1InnerAroundList = [] + for n1 in range(elementsCountAround): + v1 = xAround[n1] + d1 = unitdx_ds1Around[n1] + v2 = xAround[(n1+1)%elementsCountAround] + d2 = unitdx_ds1Around[(n1+1)%elementsCountAround] + arcLengthAround = computeCubicHermiteArcLength(v1, d1, v2, d2, True) + dx_ds1 = [c*arcLengthAround for c in d1] + dx_ds1InnerAroundList.append(dx_ds1) + d1Smoothed = smoothCubicHermiteDerivativesLoop(xAround, dx_ds1InnerAroundList) + dx_ds1InnerList = dx_ds1InnerList + d1Smoothed + + dx_ds1InnerList = dx_ds1InnerList + d1Inner + + for n in range(len(xInnerList)): + dx_ds3 = crossproduct3(normalise(dx_ds1InnerList[n]), normalise(dx_ds2InnerList[n])) + unitdx_ds3 = normalise(dx_ds3) + dx_ds3InnerUnitList.append(unitdx_ds3) + + # Pre-calculate node locations and derivatives on outer boundary + xOuter, d1Outer, d2Outer = getOuterCoordinatesAndDerivativesFromInner(xInnerList, dx_ds1InnerList, dx_ds2InnerList, dx_ds3InnerUnitList, wallThickness, elementsCountAlongHaustra, elementsCountAround) + + return xOuter, d1Outer, d2Outer, unitZ + +def getOuterCoordinatesAndDerivativesFromInner(xInner, d1Inner, d2Inner, d3Inner, wallThickness, elementsCountAlongHaustra, elementsCountAround): + """ + Generates coordinates and derivatives of outer surface from + coordinates and derivatives of inner surface using wall thickness + and normals. + param xInner: Coordinates on inner surface + param d1Inner: Derivatives on inner surface around haustra + param d2Inner: Derivatives on inner surface along haustra + param d3Inner: Derivatives on inner surface through wall + param thickness: Thickness of wall + param elementsCountAlongHaustra: Number of elements along haustra + param elementsCountAround: Number of elements around haustra + return xOuter: Coordinates on outer surface + return nd1Outer: Derivatives on outer surface around haustra + return nd2Outer: Derivatives on outer surface along haustra + """ + xOuter = [] + nd1Outer = [] + nd2Outer = [] + + for n2 in range(elementsCountAlongHaustra + 1): + for n1 in range(elementsCountAround): + n = n2*elementsCountAround + n1 + x = [xInner[n][i] + d3Inner[n][i]*wallThickness for i in range(3)] + norm = d3Inner[n] + # d1 + prevIdx = n-1 if (n1 != 0) else (n2+1)*elementsCountAround - 1 + nextIdx = n+1 if (n1 < elementsCountAround-1) else n2*elementsCountAround + curvatureAround = 0.5*( + getCubicHermiteCurvature(xInner[prevIdx], d1Inner[prevIdx], xInner[n], d1Inner[n], norm, 1.0) + + getCubicHermiteCurvature(xInner[n], d1Inner[n], xInner[nextIdx], d1Inner[nextIdx], norm, 0.0)) + factor = 1.0 - curvatureAround*wallThickness + nd1 = [ factor*c for c in d1Inner[n]] + # d2 + if n2 > 0 and n2 < elementsCountAlongHaustra: + prevIdx = (n2-1)*elementsCountAround + n1 + nextIdx = (n2+1)*elementsCountAround + n1 + elif n2 == 0: + prevIdx = (elementsCountAlongHaustra-1)*elementsCountAround + n1 + nextIdx = (n2+1)*elementsCountAround + n1 + else: + prevIdx = (n2-1)*elementsCountAround + n1 + nextIdx = elementsCountAround + n1 + curvatureAlong = 0.5*( + getCubicHermiteCurvature(xInner[prevIdx], d2Inner[prevIdx], xInner[n], d2Inner[n], norm, 1.0)+ + getCubicHermiteCurvature(xInner[n], d2Inner[n], xInner[nextIdx], d2Inner[nextIdx], norm, 0.0)) + factor = 1.0 - curvatureAlong*wallThickness + nd2 = [ factor*c for c in d2Inner[n]] + + xOuter.append(x) + nd1Outer.append(nd1) + nd2Outer.append(nd2) + + return xOuter, nd1Outer, nd2Outer + +def rotationMatrixAboutAxis(rotAxis, theta): + """ + Generate the rotation matrix for rotation about an axis. + :param rotAxis: axis of rotation + :param theta: angle of rotation + :return: rotation matrix + """ + cosTheta = math.cos(theta) + sinTheta = math.sin(theta) + C = 1 - cosTheta + rotMatrix = ([[rotAxis[0]*rotAxis[0]*C + cosTheta, rotAxis[0]*rotAxis[1]*C - rotAxis[2]*sinTheta, rotAxis[0]*rotAxis[2]*C + rotAxis[1]*sinTheta], + [rotAxis[1]*rotAxis[0]*C + rotAxis[2]*sinTheta, rotAxis[1]*rotAxis[1]*C + cosTheta, rotAxis[1]*rotAxis[2]*C - rotAxis[0]*sinTheta], + [rotAxis[2]*rotAxis[0]*C - rotAxis[1]*sinTheta, rotAxis[2]*rotAxis[1]*C + rotAxis[0]*sinTheta, rotAxis[2]*rotAxis[2]*C + cosTheta]]) + return rotMatrix \ No newline at end of file diff --git a/scaffoldmaker/scaffolds.py b/scaffoldmaker/scaffolds.py index e752c79a..3b929d7d 100644 --- a/scaffoldmaker/scaffolds.py +++ b/scaffoldmaker/scaffolds.py @@ -9,6 +9,7 @@ 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_centrallinetube1 import MeshType_3d_centrallinetube1 +from scaffoldmaker.meshtypes.meshtype_3d_colon1 import MeshType_3d_colon1 from scaffoldmaker.meshtypes.meshtype_3d_haustra1 import MeshType_3d_haustra1 from scaffoldmaker.meshtypes.meshtype_3d_heart1 import MeshType_3d_heart1 from scaffoldmaker.meshtypes.meshtype_3d_heart2 import MeshType_3d_heart2 @@ -39,6 +40,7 @@ def __init__(self): MeshType_3d_box1, MeshType_3d_boxhole1, MeshType_3d_centrallinetube1, + MeshType_3d_colon1, MeshType_3d_haustra1, MeshType_3d_heart1, MeshType_3d_heart2, diff --git a/scaffoldmaker/utils/tubemesh2.py b/scaffoldmaker/utils/tubemesh2.py new file mode 100644 index 00000000..b1e168a7 --- /dev/null +++ b/scaffoldmaker/utils/tubemesh2.py @@ -0,0 +1,341 @@ +''' +Utility function for generating tubular mesh from a central line +using a unit profile. +Created on 18 Feb, 2019 + +@author: Mabelle Lin +''' +from __future__ import division +import math +from scaffoldmaker.utils.eftfactory_bicubichermitelinear import eftfactory_bicubichermitelinear +from scaffoldmaker.utils.eftfactory_tricubichermite import eftfactory_tricubichermite +from scaffoldmaker.utils.zinc_utils import * +from scaffoldmaker.utils.geometry import * +from scaffoldmaker.utils.interpolation import * +from scaffoldmaker.utils.vector import * +from opencmiss.zinc.element import Element, Elementbasis +from opencmiss.zinc.field import Field +from opencmiss.zinc.node import Node + +def generatetubemesh(region, + elementsCountAround, + elementsCountAlongUnit, + elementsCountThroughWall, + unitsCountAlong, + cx, cd1, + xOuter, d1Outer, d2Outer, wallThickness, + unitAxis, unitLength, + useCrossDerivatives, + useCubicHermiteThroughWall, # or Zinc Elementbasis.FUNCTION_TYPE_LINEAR_LAGRANGE etc. + nextNodeIdentifier = 1, nextElementIdentifier = 1 + ): + ''' + Generates a 3-D tubular mesh with variable numbers of elements + around, along the central axis, and radially through wall. The + tubular mesh is created from a unit profile which is mapped onto + the central line and lateral axes data + :param elementsCountAround: number of elements around tube + :param elementsCountAlongUnit: number of elements along unit profile + :param elementsCountThroughWall: number of elements through wall thickness + :param unitsCountAlong: number of units along the tube + :param cx: coordinates on central line + :param cd1: derivative 1 along central line + :param xOuter: coordinates on outer surface of unit profile + :param d1Outer: derivatives around outer surface of unit profile + :param d2Outer: derivatives along outer surface of unit profile + :param wallThickness: thickness of wall + :param unitAxis: axis of unit profile + :param unitLength: length of unit profile + :param useCubicHermiteThroughWall: use linear when false + ''' + zero = [0.0, 0.0, 0.0] + elementsCountAlong = elementsCountAlongUnit*unitsCountAlong + + # Sample central line to get same number of elements as elementsCountAlong + sx, sd1, se, sxi, _ = sampleCubicHermiteCurves(cx, cd1, elementsCountAlong) + + # Find unit normals and binormals at each sample points + sNormal = [] + sBinormal = [] + + # Set up normal and binormal for first frame + prevUnitTangent = normalise(sd1[0]) + if magnitude(crossproduct3(prevUnitTangent,[0.0, 0.0, 1.0])) > 0.0: + prevBinormal = crossproduct3(prevUnitTangent,[0.0, 0.0, 1.0]) + else: + prevBinormal = crossproduct3(prevUnitTangent,[0.0, -1.0, 0.0]) + prevUnitBinormal = normalise(prevBinormal) + prevUnitNormal = crossproduct3(prevUnitBinormal, prevUnitTangent) + sNormal.append(prevUnitNormal) + sBinormal.append(prevUnitBinormal) + + # Step through central line and rotate central line axes to align tangent + # to tangent from previous frame + for n in range(1, elementsCountAlong+1): + unitTangent = normalise(sd1[n]) + cp = crossproduct3(prevUnitTangent, unitTangent) + if magnitude(cp)> 0.0: + axisRot = normalise(cp) + thetaRot = math.acos(dotproduct(prevUnitTangent, unitTangent)) + rotFrame = rotationMatrixAboutAxis(axisRot, thetaRot) + rotNormal = [rotFrame[j][0]*prevUnitNormal[0] + rotFrame[j][1]*prevUnitNormal[1] + rotFrame[j][2]*prevUnitNormal[2] for j in range(3)] + unitNormal = normalise(rotNormal) + unitBinormal = crossproduct3(unitTangent, unitNormal) + prevUnitTangent = unitTangent + prevUnitNormal = unitNormal + else: + unitBinormal = prevUnitBinormal + unitNormal = prevUnitNormal + sNormal.append(unitNormal) + sBinormal.append(unitBinormal) + + fm = region.getFieldmodule() + fm.beginChange() + cache = fm.createFieldcache() + coordinates = getOrCreateCoordinateField(fm) + + nodes = fm.findNodesetByFieldDomainType(Field.DOMAIN_TYPE_NODES) + nodetemplate = nodes.createNodetemplate() + nodetemplate.defineField(coordinates) + nodetemplate.setValueNumberOfVersions(coordinates, -1, Node.VALUE_LABEL_VALUE, 1) + nodetemplate.setValueNumberOfVersions(coordinates, -1, Node.VALUE_LABEL_D_DS1, 1) + nodetemplate.setValueNumberOfVersions(coordinates, -1, Node.VALUE_LABEL_D_DS2, 1) + if useCrossDerivatives: + nodetemplate.setValueNumberOfVersions(coordinates, -1, Node.VALUE_LABEL_D2_DS1DS2, 1) + if useCubicHermiteThroughWall: + nodetemplate.setValueNumberOfVersions(coordinates, -1, Node.VALUE_LABEL_D_DS3, 1) + if useCrossDerivatives: + nodetemplate.setValueNumberOfVersions(coordinates, -1, Node.VALUE_LABEL_D2_DS1DS3, 1) + nodetemplate.setValueNumberOfVersions(coordinates, -1, Node.VALUE_LABEL_D2_DS2DS3, 1) + nodetemplate.setValueNumberOfVersions(coordinates, -1, Node.VALUE_LABEL_D3_DS1DS2DS3, 1) + + mesh = fm.findMeshByDimension(3) + + if useCubicHermiteThroughWall: + eftfactory = eftfactory_tricubichermite(mesh, useCrossDerivatives) + else: + eftfactory = eftfactory_bicubichermitelinear(mesh, useCrossDerivatives) + eft = eftfactory.createEftBasic() + + elementtemplate = mesh.createElementtemplate() + elementtemplate.setElementShapeType(Element.SHAPE_TYPE_CUBE) + result = elementtemplate.defineField(coordinates, -1, eft) + + # create nodes + nodeIdentifier = nextNodeIdentifier + x = [ 0.0, 0.0, 0.0 ] + dx_ds1 = [ 0.0, 0.0, 0.0 ] + dx_ds2 = [ 0.0, 0.0, 0.0 ] + dx_ds3 = [ 0.0, 0.0, 0.0 ] + xOuterList = [] + d1OuterList = [] + d2OuterList = [] + d3OuterUnitList = [] + xList = [] + dx_ds1List = [] + dx_ds2List = [] + dx_ds3List = [] + + # Map each face along unit profile to central line + for nUnit in range(unitsCountAlong): + for nAlongUnit in range(elementsCountAlongUnit+1): + n2 = nUnit*elementsCountAlongUnit + nAlongUnit + if nUnit == 0 or (nUnit > 0 and nAlongUnit > 0): # Take first face from first element along, take 2nd to last face for subsequent elements along + unitMid = [0.0, 0.0, unitLength/elementsCountAlongUnit* nAlongUnit] + unitTangent = normalise(sd1[n2]) + cp = crossproduct3(unitAxis, unitTangent) + if magnitude(cp)> 0.0: + axisRot = normalise(cp) + thetaRot = math.acos(dotproduct(unitAxis, unitTangent)) + rotFrame = rotationMatrixAboutAxis(axisRot, thetaRot) + midRot = [rotFrame[j][0]*unitMid[0] + rotFrame[j][1]*unitMid[1] + rotFrame[j][2]*unitMid[2] for j in range(3)] + else: + midRot = unitMid + + for n1 in range(elementsCountAround): + n = nAlongUnit*elementsCountAround + n1 + x = xOuter[n] + d1 = d1Outer[n] + d2 = d2Outer[n] + if magnitude(cp)> 0.0: + xRot = [rotFrame[j][0]*x[0] + rotFrame[j][1]*x[1] + rotFrame[j][2]*x[2] for j in range(3)] + d1Rot = [rotFrame[j][0]*d1[0] + rotFrame[j][1]*d1[1] + rotFrame[j][2]*d1[2] for j in range(3)] + d2Rot = [rotFrame[j][0]*d2[0] + rotFrame[j][1]*d2[1] + rotFrame[j][2]*d2[2] for j in range(3)] + else: + xRot = x + d1Rot = d1 + d2Rot = d2 + + translateMatrix = [sx[n2][j] - midRot[j] for j in range(3)] + xTranslate = [xRot[j] + translateMatrix[j] for j in range(3)] + xOuterList.append(xTranslate) + d1OuterList.append(d1Rot) + d2OuterList.append(d2Rot) + d3Unit = normalise(crossproduct3(normalise(d2Rot), normalise(d1Rot))) + d3OuterUnitList.append(d3Unit) + + # Pre-calculate node locations and derivatives on outer boundary + xInnerList, d1Inner, curvatureOuter = getInnerCoordinatesAndDerivativesFromOuter(xOuterList, d1OuterList, d3OuterUnitList, wallThickness, elementsCountAlong, elementsCountAround) + + # Interpolate to get nodes through wall + for n3 in range(elementsCountThroughWall + 1): + xi3 = 1/elementsCountThroughWall * n3 + x, dx_ds1, dx_ds2, dx_ds3 = interpolatefromInnerAndOuter( xInnerList, xOuterList, wallThickness, xi3, curvatureOuter, d1OuterList, d2OuterList, d3OuterUnitList, elementsCountAround, elementsCountAlong, elementsCountThroughWall) + xList = xList + x + dx_ds1List = dx_ds1List + dx_ds1 + dx_ds2List = dx_ds2List + dx_ds2 + dx_ds3List = dx_ds3List + dx_ds3 + + for n in range(len(xList)): + node = nodes.createNode(nodeIdentifier, nodetemplate) + cache.setNode(node) + coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_VALUE, 1, xList[n]) + coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS1, 1, dx_ds1List[n]) + coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS2, 1, dx_ds2List[n]) + coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS3, 1, dx_ds3List[n]) + if useCrossDerivatives: + coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D2_DS1DS2, 1, zero) + coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D2_DS1DS3, 1, zero) + coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D2_DS2DS3, 1, zero) + coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D3_DS1DS2DS3, 1, zero) + nodeIdentifier = nodeIdentifier + 1 + + # # For debugging - Nodes along central line + # for pt in range(len(sx)): + # node = nodes.createNode(nodeIdentifier, nodetemplate) + # cache.setNode(node) + # coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_VALUE, 1, sx[pt]) + # coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS1, 1, sd1[pt]) + # coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS2, 1, sNormal[pt]) + # coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS3, 1, sBinormal[pt]) + # nodeIdentifier = nodeIdentifier + 1 + + # create elements + elementIdentifier = nextElementIdentifier + now = (elementsCountAlong + 1)*elementsCountAround + for e3 in range(elementsCountThroughWall): + for e2 in range(elementsCountAlong): + for e1 in range(elementsCountAround): + element = mesh.createElement(elementIdentifier, elementtemplate) + bni11 = e3*now + e2*elementsCountAround + e1 + 1 + bni12 = e3*now + e2*elementsCountAround + (e1 + 1)%elementsCountAround + 1 + bni21 = e3*now + (e2 + 1)*elementsCountAround + e1 + 1 + bni22 = e3*now + (e2 + 1)*elementsCountAround + (e1 + 1)%elementsCountAround + 1 + nodeIdentifiers = [ bni11, bni12, bni21, bni22, bni11 + now, bni12 + now, bni21 + now, bni22 + now ] + result = element.setNodesByIdentifier(eft, nodeIdentifiers) + elementIdentifier = elementIdentifier + 1 + + fm.endChange() + + return nodeIdentifier, elementIdentifier + +def getInnerCoordinatesAndDerivativesFromOuter(xOuter, d1Outer, d3Outer, wallThickness, elementsCountAlong, elementsCountAround): + """ + Generates coordinates and derivatives of inner surface from + coordinates and derivatives of outer surface using wall thickness + and normals. + param xOuter: Coordinates on outer surface + param d1Outer: Derivatives on outer surface around tube + param d3Outer: Derivatives on outer surface through wall + param wallThickness: Thickness of wall + param elementsCountAlong: Number of elements along tube + param elementsCountAround: Number of elements around tube + return xInner: Coordinates on inner surface + return nd1: Derivatives on inner surface around tube + return curvatureOuter: Curvature of coordinates on outer surface + """ + xInner = [] + nd1 = [] + dWall = [] + curvatureOuter = [] + for n2 in range(elementsCountAlong + 1): + for n1 in range(elementsCountAround): + n = n2*elementsCountAround + n1 + x = [xOuter[n][i] + d3Outer[n][i]*wallThickness for i in range(3)] + prevIdx = n-1 if (n1 != 0) else (n2+1)*elementsCountAround - 1 + nextIdx = n+1 if (n1 < elementsCountAround-1) else n2*elementsCountAround + norm = [-1.0*c for c in d3Outer[n]] + curvatureAround = 0.5*( + getCubicHermiteCurvature(xOuter[prevIdx], d1Outer[prevIdx], xOuter[n], d1Outer[n], norm, 1.0) + + getCubicHermiteCurvature(xOuter[n], d1Outer[n], xOuter[nextIdx], d1Outer[nextIdx], norm, 0.0)) + factor = 1.0 - curvatureAround*wallThickness + nd1Inner = [ factor*c for c in d1Outer[n]] + xInner.append(x) + nd1.append(nd1Inner) + curvatureOuter.append(curvatureAround) + + return xInner, nd1, curvatureOuter + +def interpolatefromInnerAndOuter( xInner, xOuter, thickness, xi3, curvatureOuter, d1Outer, d2Outer, d3OuterUnit, + elementsCountAround, elementsCountAlong, elementsCountThroughWall): + """ + Generate coordinates and derivatives at xi3 by interpolating with + inner and outer coordinates and derivatives. + param xInner: Coordinates on inner surface + param xOuter: Coordinates on outer surface + param thickness: Thickness of wall + param curvatureOuter: Curvature of coordinates on outer surface + param d1Outer: Derivatives on outer surface around tube + param d2outer: Derivatives on outer surface along tube + param d3OuterUnit: Unit derivatives on inner surface through wall + param elementsCountAround: Number of elements around tube + param elementsCountAlong: Number of elements along tube + param elementsCountThroughWall: Number of elements through wall + return xList, dx_ds1List, dx_ds2List, dx_ds3List: Coordinates and derivatives on xi3 + """ + xList = [] + dx_ds1List = [] + dx_ds2List = [] + dx_ds3List =[] + + for n2 in range(elementsCountAlong+1): + for n1 in range(elementsCountAround): + n = n2*elementsCountAround + n1 + norm = [-1* c for c in d3OuterUnit[n]] + # x + innerx = xInner[n] + outerx = xOuter[n] + dWall = [thickness*c for c in norm] + x = interpolateCubicHermite(innerx, dWall, outerx, dWall, xi3) + xList.append(x) + # dx_ds1 + factor = 1.0 - curvatureOuter[n]*thickness*xi3 + dx_ds1 = [ factor*c for c in d1Outer[n]] + dx_ds1List.append(dx_ds1) + # dx_ds2 + if n2 > 0 and n2 < elementsCountAlong: + prevIdx = (n2-1)*elementsCountAround + n1 + nextIdx = (n2+1)*elementsCountAround + n1 + curvatureAround = 0.5*( + getCubicHermiteCurvature(xOuter[prevIdx], d2Outer[prevIdx], xOuter[n], d2Outer[n], norm, 1.0)+ + getCubicHermiteCurvature(xOuter[n], d2Outer[n], xOuter[nextIdx], d2Outer[nextIdx], norm, 0.0)) + elif n2 == 0: + nextIdx = (n2+1)*elementsCountAround + n1 + curvatureAround = getCubicHermiteCurvature(xOuter[n], d2Outer[n], xOuter[nextIdx], d2Outer[nextIdx], norm, 0.0) + else: + prevIdx = (n2-1)*elementsCountAround + n1 + curvatureAround = getCubicHermiteCurvature(xOuter[prevIdx], d2Outer[prevIdx], xOuter[n], d2Outer[n], norm, 1.0) + + factor = 1.0 - curvatureAround*thickness*xi3 + dx_ds2 = [ factor*c for c in d2Outer[n]] + dx_ds2List.append(dx_ds2) + #dx_ds3 + dx_ds3 = [c * thickness/elementsCountThroughWall for c in norm] + dx_ds3List.append(dx_ds3) + + return xList, dx_ds1List, dx_ds2List, dx_ds3List + +def rotationMatrixAboutAxis(rotAxis, theta): + """ + Generate the rotation matrix for rotation about an axis. + :param rotAxis: axis of rotation + :param theta: angle of rotation + :return: rotation matrix + """ + cosTheta = math.cos(theta) + sinTheta = math.sin(theta) + C = 1 - cosTheta + rotMatrix = ([[rotAxis[0]*rotAxis[0]*C + cosTheta, rotAxis[0]*rotAxis[1]*C - rotAxis[2]*sinTheta, rotAxis[0]*rotAxis[2]*C + rotAxis[1]*sinTheta], + [rotAxis[1]*rotAxis[0]*C + rotAxis[2]*sinTheta, rotAxis[1]*rotAxis[1]*C + cosTheta, rotAxis[1]*rotAxis[2]*C - rotAxis[0]*sinTheta], + [rotAxis[2]*rotAxis[0]*C - rotAxis[1]*sinTheta, rotAxis[2]*rotAxis[1]*C + rotAxis[0]*sinTheta, rotAxis[2]*rotAxis[2]*C + cosTheta]]) + return rotMatrix From 7ab6261d870cdaffa260d2fe7724e6e2514fa161 Mon Sep 17 00:00:00 2001 From: Mabelle Lin Date: Mon, 18 Feb 2019 17:10:40 +1300 Subject: [PATCH 06/44] Change default number of haustra and remove comments --- scaffoldmaker/meshtypes/meshtype_3d_haustra1.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/scaffoldmaker/meshtypes/meshtype_3d_haustra1.py b/scaffoldmaker/meshtypes/meshtype_3d_haustra1.py index 74106db3..0009d6ac 100644 --- a/scaffoldmaker/meshtypes/meshtype_3d_haustra1.py +++ b/scaffoldmaker/meshtypes/meshtype_3d_haustra1.py @@ -27,7 +27,7 @@ def getDefaultOptions(): 'Inner radius': 0.5, 'Corner radius fraction': 0.5, 'Wall thickness': 0.01, - 'Number of haustra': 1, + 'Number of haustra': 2, 'Haustra radius fraction': 0.5, 'Haustra length': 1.0, 'Interhaustra fold factor': 0.5, @@ -266,9 +266,6 @@ def generateBaseMesh(region, options): sx, sd1, se, sxi, _ = sampleCubicHermiteCurves(nx, nd1, elementsCountAlongHaustra) xInnerRaw.append(sx) dx_ds2InnerRaw.append(sd1) - # if n1 == 28: # troubleshoot issue when samples suddenly go back to start - # print('nx = ', nx) - # print(sx) # Re-arrange sample order & calculate dx_ds1 and dx_ds3 from dx_ds2 dx_ds1InnerList = dx_ds1InnerList + d1Inner From 0047e3b78526261448f6fa5b37f2e7d758eb7ae6 Mon Sep 17 00:00:00 2001 From: Mabelle Lin Date: Mon, 18 Feb 2019 17:12:55 +1300 Subject: [PATCH 07/44] Add new central line for human colon --- .../meshtypes/meshtype_3d_centrallinetube1.py | 35 +++++++++++++------ 1 file changed, 24 insertions(+), 11 deletions(-) diff --git a/scaffoldmaker/meshtypes/meshtype_3d_centrallinetube1.py b/scaffoldmaker/meshtypes/meshtype_3d_centrallinetube1.py index 6b987244..d05978ca 100644 --- a/scaffoldmaker/meshtypes/meshtype_3d_centrallinetube1.py +++ b/scaffoldmaker/meshtypes/meshtype_3d_centrallinetube1.py @@ -61,7 +61,7 @@ def checkOptions(options): options[key] = 1 if (options['Number of elements around'] < 2) : options['Number of elements around'] = 2 - if (options['Tube type'] < 1 or options['Tube type'] > 3 ) : + if (options['Tube type'] < 1 or options['Tube type'] > 4 ) : options['Tube type'] = 1 @staticmethod @@ -83,11 +83,11 @@ def generateBaseMesh(region, options): # Straight tube cx = [[1.0, 3.0, 0.0], [ 2.0, 0.0, 4.0 ] ] cd1 = [[ 1.0, -3.0, 4.0 ], [ 1.0, -3.0, 4.0 ]] - cd2 = [ [ 0.0, 0.3, 0.0 ], [ 0.0, 0.3, 0.0 ]] + cd2 = [ [ 0.0, 0.2, 0.0 ], [ 0.0, 0.2, 0.0 ]] cd3 = [ [ 0.0, 0.0, 0.5 ], [ 0.0, 0.0, 0.5 ]] # thickness in cd2 and cd3 directions and derivatives (rate of change) - t2 = [ 0.5, 0.5 ] + t2 = [ 0.1, 0.5 ] t2d = [ 0.0, 0.0 ] t3 = [ 0.5, 0.5 ] t3d = [ 0.0, 0.0 ] @@ -107,16 +107,29 @@ def generateBaseMesh(region, options): elif tubeType == 3: # Curved tube 2 - cx = [ [ 0.0, 0.0, 1.0], [1.0, 1.0, 2.0], [ 2.0, 0.0, 4.0 ] ] - cd1 = [ [ 1.0, 1.0, 1.0 ], [ 1.0, 0.0, 2.0 ], [1.0, -1.0, 2.0] ] - cd2 = [ [ 0.0, 0.2, 0.0 ], [ 0.0, 0.2, 0.0 ], [ 0.0, 0.2, 0.0 ] ] - cd3 = [ [ 0.0, 0.0, 0.2 ], [ 0.0, 0.0, 0.2 ], [ 0.0, 0.0, 0.2] ] + cx = [ [ 0.0, 0.0, 1.0], [1.5, 1.0, 0.0], [ 3.0, -1.0, 0.0 ], [ 5.0, 1.5, 1.0]] + cd1 = [ [ 4.0, 0.0, 0.0 ], [ 2.0, 0.0, 0.0 ], [3.0, 0.0, 0.0], [ 3.0, 0.0, 0.0 ]] + cd2 = [ [ 0.0, 0.2, 0.0 ], [ 0.0, 0.2, 0.0 ], [ 0.0, 0.2, 0.0 ], [ 0.0, 0.2, 0.0] ] + cd3 = [ [ 0.0, 0.0, 0.2 ], [ 0.0, 0.0, 0.2 ], [ 0.0, 0.0, 0.2], [ 0.0, 0.0, 0.2 ]] # thickness in cd2 and cd3 directions and derivatives (rate of change) - t2 = [ 0.1, 0.1, 0.1 ] - t2d = [ 0.0, 0.0, 0.0 ] - t3 = [ 0.1, 0.1, 0.1 ] - t3d = [ 0.0, 0.0, 0.0 ] + t2 = [ 0.1, 0.1, 0.1, 0.1 ] + t2d = [ 0.0, 0.0, 0.0, 0.0] + t3 = [ 0.1, 0.1, 0.1, 0.1] + t3d = [ 0.0, 0.0, 0.0, 0.0] + + elif tubeType == 4: + # Colon + cx = [ [ 0.0, 0.0, 0.0], [0.0, 10.0, 0.0], [5.0, 9.0, 0.0], [ 10.0, 10.0, 0.0 ], [ 10.0, -2.0, 0.0], [ 7.0, -4.0, 0.0] ] + cd1 = [ [ 0.0, 10.0, 0.0 ], [ 5.0, 5.0, 0.0 ], [5.0, 0.0, 0.0], [ 5.0, -5.0, 0.0 ], [ -3.0, -5.0, 0.0 ], [ -3.0, 0.0, 0.0 ]] + cd2 = [ [ 0.0, 0.5, 0.0 ], [ 0.0, 0.5, 0.0 ], [ 0.0, 0.5, 0.0 ], [ 0.0, 0.5, 0.0], [ 0.0, 0.5, 0.0 ], [ 0.0, 0.5, 0.0 ]] + cd3 = [ [ 0.0, 0.0, 0.5 ], [ 0.0, 0.0, 0.5 ], [ 0.0, 0.0, 0.5], [ 0.0, 0.0, 0.5 ], [ 0.0, 0.0, 0.5], [ 0.0, 0.0, 0.5]] + + # thickness in cd2 and cd3 directions and derivatives (rate of change) + t2 = [ 0.1, 0.1, 0.1, 0.1, 0.1, 0.1 ] + t2d = [ 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 ] + t3 = [ 0.1, 0.1, 0.1, 0.1, 0.1, 0.1] + t3d = [ 0.0, 0.0, 0.0, 0.0, 0.0, 0.0] nextNodeIdentifier, nextElementIdentifier = generatetubemesh(region, elementsCountAlong, elementsCountAround, elementsCountThroughWall, cx, cd1, cd2, cd3, t2, t2d, t3, t3d, useCrossDerivatives, useCubicHermiteThroughWall) From 59f2c287afaeaf9432dc22357cf7cf4081e0c504 Mon Sep 17 00:00:00 2001 From: Mabelle Lin Date: Wed, 20 Feb 2019 12:39:20 +1300 Subject: [PATCH 08/44] Rotate faces along colon to align with binormal axes --- scaffoldmaker/utils/tubemesh2.py | 56 ++++++++++++++++++++------------ 1 file changed, 35 insertions(+), 21 deletions(-) diff --git a/scaffoldmaker/utils/tubemesh2.py b/scaffoldmaker/utils/tubemesh2.py index b1e168a7..c9c52e8f 100644 --- a/scaffoldmaker/utils/tubemesh2.py +++ b/scaffoldmaker/utils/tubemesh2.py @@ -141,6 +141,7 @@ def generatetubemesh(region, for nAlongUnit in range(elementsCountAlongUnit+1): n2 = nUnit*elementsCountAlongUnit + nAlongUnit if nUnit == 0 or (nUnit > 0 and nAlongUnit > 0): # Take first face from first element along, take 2nd to last face for subsequent elements along + # Rotate to align unit axis with tangent of central line unitMid = [0.0, 0.0, unitLength/elementsCountAlongUnit* nAlongUnit] unitTangent = normalise(sd1[n2]) cp = crossproduct3(unitAxis, unitTangent) @@ -149,6 +150,7 @@ def generatetubemesh(region, thetaRot = math.acos(dotproduct(unitAxis, unitTangent)) rotFrame = rotationMatrixAboutAxis(axisRot, thetaRot) midRot = [rotFrame[j][0]*unitMid[0] + rotFrame[j][1]*unitMid[1] + rotFrame[j][2]*unitMid[2] for j in range(3)] + translateMatrix = [sx[n2][j] - midRot[j] for j in range(3)] else: midRot = unitMid @@ -158,20 +160,31 @@ def generatetubemesh(region, d1 = d1Outer[n] d2 = d2Outer[n] if magnitude(cp)> 0.0: - xRot = [rotFrame[j][0]*x[0] + rotFrame[j][1]*x[1] + rotFrame[j][2]*x[2] for j in range(3)] - d1Rot = [rotFrame[j][0]*d1[0] + rotFrame[j][1]*d1[1] + rotFrame[j][2]*d1[2] for j in range(3)] - d2Rot = [rotFrame[j][0]*d2[0] + rotFrame[j][1]*d2[1] + rotFrame[j][2]*d2[2] for j in range(3)] + xRot1 = [rotFrame[j][0]*x[0] + rotFrame[j][1]*x[1] + rotFrame[j][2]*x[2] for j in range(3)] + d1Rot1 = [rotFrame[j][0]*d1[0] + rotFrame[j][1]*d1[1] + rotFrame[j][2]*d1[2] for j in range(3)] + d2Rot1 = [rotFrame[j][0]*d2[0] + rotFrame[j][1]*d2[1] + rotFrame[j][2]*d2[2] for j in range(3)] + # Rotate to align first vector on face with binormal axis + if n1 == 0: + firstVector = normalise([xRot1[j] - midRot[j] for j in range(3)]) + thetaRot2 = math.acos(dotproduct(normalise(sBinormal[n2]), firstVector)) + cp2 = normalise(crossproduct3(normalise(sBinormal[n2]), firstVector)) + signThetaRot2 = dotproduct(unitTangent, cp2) + axisRot2 = unitTangent + rotFrame2 = rotationMatrixAboutAxis(axisRot2, -signThetaRot2*thetaRot2) + xRot2 = [rotFrame2[j][0]*xRot1[0] + rotFrame2[j][1]*xRot1[1] + rotFrame2[j][2]*xRot1[2] for j in range(3)] + d1Rot2 = [rotFrame2[j][0]*d1Rot1[0] + rotFrame2[j][1]*d1Rot1[1] + rotFrame2[j][2]*d1Rot1[2] for j in range(3)] + d2Rot2 = [rotFrame2[j][0]*d2Rot1[0] + rotFrame2[j][1]*d2Rot1[1] + rotFrame2[j][2]*d2Rot1[2] for j in range(3)] else: - xRot = x - d1Rot = d1 - d2Rot = d2 + xRot2 = x + d1Rot2 = d1 + d2Rot2 = d2 + + xTranslate = [xRot2[j] + translateMatrix[j] for j in range(3)] - translateMatrix = [sx[n2][j] - midRot[j] for j in range(3)] - xTranslate = [xRot[j] + translateMatrix[j] for j in range(3)] xOuterList.append(xTranslate) - d1OuterList.append(d1Rot) - d2OuterList.append(d2Rot) - d3Unit = normalise(crossproduct3(normalise(d2Rot), normalise(d1Rot))) + d1OuterList.append(d1Rot2) + d2OuterList.append(d2Rot2) + d3Unit = normalise(crossproduct3(normalise(d2Rot2), normalise(d1Rot2))) d3OuterUnitList.append(d3Unit) # Pre-calculate node locations and derivatives on outer boundary @@ -198,17 +211,18 @@ def generatetubemesh(region, coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D2_DS1DS3, 1, zero) coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D2_DS2DS3, 1, zero) coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D3_DS1DS2DS3, 1, zero) + # print('NodeIdentifier = ', nodeIdentifier, xList[n]) + nodeIdentifier = nodeIdentifier + 1 + + # For debugging - Nodes along central line + for pt in range(len(sx)): + node = nodes.createNode(nodeIdentifier, nodetemplate) + cache.setNode(node) + coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_VALUE, 1, sx[pt]) + coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS1, 1, sd1[pt]) + coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS2, 1, sNormal[pt]) + coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS3, 1, sBinormal[pt]) nodeIdentifier = nodeIdentifier + 1 - - # # For debugging - Nodes along central line - # for pt in range(len(sx)): - # node = nodes.createNode(nodeIdentifier, nodetemplate) - # cache.setNode(node) - # coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_VALUE, 1, sx[pt]) - # coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS1, 1, sd1[pt]) - # coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS2, 1, sNormal[pt]) - # coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS3, 1, sBinormal[pt]) - # nodeIdentifier = nodeIdentifier + 1 # create elements elementIdentifier = nextElementIdentifier From cb172aef5d7c3476dbb37dc5668bc9aedb5e0057 Mon Sep 17 00:00:00 2001 From: Mabelle Lin Date: Wed, 20 Feb 2019 12:41:53 +1300 Subject: [PATCH 09/44] Add new central line to describe 3D colon --- .../meshtypes/meshtype_3d_centrallinetube1.py | 21 +++++++++++++++---- scaffoldmaker/meshtypes/meshtype_3d_colon1.py | 8 +++++-- 2 files changed, 23 insertions(+), 6 deletions(-) diff --git a/scaffoldmaker/meshtypes/meshtype_3d_centrallinetube1.py b/scaffoldmaker/meshtypes/meshtype_3d_centrallinetube1.py index 8783429e..273ac0bd 100644 --- a/scaffoldmaker/meshtypes/meshtype_3d_centrallinetube1.py +++ b/scaffoldmaker/meshtypes/meshtype_3d_centrallinetube1.py @@ -62,7 +62,7 @@ def checkOptions(options): options[key] = 1 if (options['Number of elements around'] < 2) : options['Number of elements around'] = 2 - if (options['Tube type'] < 1 or options['Tube type'] > 4 ) : + if (options['Tube type'] < 1 or options['Tube type'] > 5 ) : options['Tube type'] = 1 @staticmethod @@ -120,18 +120,31 @@ def generateBaseMesh(region, options): t3d = [ 0.0, 0.0, 0.0, 0.0] elif tubeType == 4: - # Colon + # Colon in x-y plane cx = [ [ 0.0, 0.0, 0.0], [0.0, 10.0, 0.0], [5.0, 9.0, 0.0], [ 10.0, 10.0, 0.0 ], [ 10.0, -2.0, 0.0], [ 7.0, -4.0, 0.0] ] cd1 = [ [ 0.0, 10.0, 0.0 ], [ 5.0, 5.0, 0.0 ], [5.0, 0.0, 0.0], [ 5.0, -5.0, 0.0 ], [ -3.0, -5.0, 0.0 ], [ -3.0, 0.0, 0.0 ]] cd2 = [ [ 0.0, 0.5, 0.0 ], [ 0.0, 0.5, 0.0 ], [ 0.0, 0.5, 0.0 ], [ 0.0, 0.5, 0.0], [ 0.0, 0.5, 0.0 ], [ 0.0, 0.5, 0.0 ]] cd3 = [ [ 0.0, 0.0, 0.5 ], [ 0.0, 0.0, 0.5 ], [ 0.0, 0.0, 0.5], [ 0.0, 0.0, 0.5 ], [ 0.0, 0.0, 0.5], [ 0.0, 0.0, 0.5]] # thickness in cd2 and cd3 directions and derivatives (rate of change) - t2 = [ 0.1, 0.1, 0.1, 0.1, 0.1, 0.1 ] + t2 = [ 0.5, 0.5, 0.5, 0.5, 0.5, 0.5 ] t2d = [ 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 ] - t3 = [ 0.1, 0.1, 0.1, 0.1, 0.1, 0.1] + t3 = [ 0.5, 0.5, 0.5, 0.5, 0.5, 0.5] t3d = [ 0.0, 0.0, 0.0, 0.0, 0.0, 0.0] + elif tubeType == 5: + # Colon in 3D + cx = [ [ 0.0, 0.0, 0.0], [0.0, 10.0, 3.0], [5.0, 9.0, 0.0], [ 10.0, 10.0, 2.0 ], [15.0, 15.0, 7.0], [ 20.0, -2.0, 0.0], [ 10.0, -4.0, -0.0] ] + cd1 = [ [ 0.0, 10.0, 3.0 ], [ 5.0, 5.0, 0.0 ], [5.0, 0.0, 0.0], [ 10.0, -5.0, 0.0 ], [12.0, 12.0, 0.0], [ 5.0, -12.0, -5.0 ], [ -8.0, 0.0, 0.0 ]] + cd2 = [ [ 0.0, 0.5, 0.0 ], [ 0.0, 0.5, 0.0 ], [ 0.0, 0.5, 0.0 ], [ 0.0, 0.5, 0.0 ], [ 0.0, 0.5, 0.0 ], [ 0.0, 0.5, 0.0 ], [ 0.0, 0.5, 0.0 ]] + cd3 = [ [ 0.0, 0.0, 0.5 ], [ 0.0, 0.0, 0.5 ], [ 0.0, 0.0, 0.5], [ 0.0, 0.0, 0.5], [ 0.0, 0.0, 0.5], [ 0.0, 0.0, 0.5 ], [ 0.0, 0.0, 0.5]] + + # thickness in cd2 and cd3 directions and derivatives (rate of change) + t2 = [ 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5 ] + t2d = [ 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 ] + t3 = [ 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5] + t3d = [ 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0] + nextNodeIdentifier, nextElementIdentifier = generatetubemesh(region, elementsCountAlong, elementsCountAround, elementsCountThroughWall, cx, cd1, cd2, cd3, t2, t2d, t3, t3d, useCrossDerivatives, useCubicHermiteThroughWall) diff --git a/scaffoldmaker/meshtypes/meshtype_3d_colon1.py b/scaffoldmaker/meshtypes/meshtype_3d_colon1.py index ed6eaa77..729266b8 100644 --- a/scaffoldmaker/meshtypes/meshtype_3d_colon1.py +++ b/scaffoldmaker/meshtypes/meshtype_3d_colon1.py @@ -99,7 +99,7 @@ def checkOptions(options): 'Interhaustra fold factor']: if options[key] > 1.0: options[key] = 1.0 - if (options['Tube type'] < 1 or options['Tube type'] > 2 ) : + if (options['Tube type'] < 1 or options['Tube type'] > 3 ) : options['Tube type'] = 1 @staticmethod @@ -128,9 +128,13 @@ def generateBaseMesh(region, options): if tubeType == 1: # Straight tube cx = [[-4.0, 1.0, 3.0], [ 1.0, 2.0, 0.0 ] ] cd1 = [[ 5.0, 1.0, -3.0 ], [ 5.0, 1.0, -3.0 ]] - elif tubeType == 2: # Human colon + elif tubeType == 2: # Human colon in x-y plane cx = [ [ 0.0, 0.0, 0.0], [0.0, 10.0, 0.0], [5.0, 9.0, 0.0], [ 10.0, 10.0, 0.0 ], [ 10.0, -2.0, 0.0], [ 7.0, -4.0, 0.0] ] cd1 = [ [ 0.0, 10.0, 0.0 ], [ 5.0, 5.0, 0.0 ], [5.0, 0.0, 0.0], [ 5.0, -5.0, 0.0 ], [ -3.0, -5.0, 0.0 ], [ -3.0, 0.0, 0.0 ]] + elif tubeType == 3: # Human colon in 3D + cx = [ [ 0.0, 0.0, 0.0], [0.0, 10.0, 3.0], [5.0, 9.0, 0.0], [ 10.0, 10.0, 2.0 ], [15.0, 15.0, 7.0], [ 20.0, -2.0, 0.0], [ 10.0, -4.0, -0.0] ] + cd1 = [ [ 0.0, 10.0, 3.0 ], [ 5.0, 5.0, 0.0 ], [5.0, 0.0, 0.0], [ 10.0, -5.0, 0.0 ], [12.0, 12.0, 0.0], [ 5.0, -12.0, -5.0 ], [ -8.0, 0.0, 0.0 ]] + length = 0.0 # find arclength of colon From c2e85cab6dd2f0b7d12978cde36e274ef0fa9b11 Mon Sep 17 00:00:00 2001 From: Mabelle Lin Date: Mon, 25 Feb 2019 10:31:12 +1300 Subject: [PATCH 10/44] Set default options to human colon --- scaffoldmaker/meshtypes/meshtype_3d_colon1.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/scaffoldmaker/meshtypes/meshtype_3d_colon1.py b/scaffoldmaker/meshtypes/meshtype_3d_colon1.py index 729266b8..9463ebe7 100644 --- a/scaffoldmaker/meshtypes/meshtype_3d_colon1.py +++ b/scaffoldmaker/meshtypes/meshtype_3d_colon1.py @@ -30,17 +30,17 @@ def getName(): @staticmethod def getDefaultOptions(): return { - 'Number of elements around' : 9, - 'Number of elements along haustra' : 3, + 'Number of elements around' : 15, + 'Number of elements along haustra' : 4, 'Number of elements through wall' : 1, - 'Number of haustra': 2, - 'Inner radius': 0.5, + 'Number of haustra': 30, + 'Inner radius': 1.0, 'Corner radius fraction': 0.5, 'Haustra radius fraction': 0.5, 'Interhaustra fold factor': 0.5, - 'Haustra curvature factor': 1.0, - 'Wall thickness': 0.01, - 'Tube type': 1, + 'Haustra curvature factor': 2.0, + 'Wall thickness': 0.05, + 'Tube type': 2, 'Use cross derivatives' : False, 'Use linear through wall' : False, 'Refine' : False, From 6a0bb3170154bf566a65a9d236526a0a1b060b2b Mon Sep 17 00:00:00 2001 From: Mabelle Lin Date: Mon, 4 Mar 2019 13:54:50 +1300 Subject: [PATCH 11/44] Remove import of unnecessary files --- scaffoldmaker/meshtypes/meshtype_3d_colon1.py | 9 --------- 1 file changed, 9 deletions(-) diff --git a/scaffoldmaker/meshtypes/meshtype_3d_colon1.py b/scaffoldmaker/meshtypes/meshtype_3d_colon1.py index 9463ebe7..7fccef5b 100644 --- a/scaffoldmaker/meshtypes/meshtype_3d_colon1.py +++ b/scaffoldmaker/meshtypes/meshtype_3d_colon1.py @@ -4,17 +4,8 @@ variable radius and thickness along. """ -from scaffoldmaker.utils.eftfactory_bicubichermitelinear import eftfactory_bicubichermitelinear -from scaffoldmaker.utils.eftfactory_tricubichermite import eftfactory_tricubichermite from scaffoldmaker.utils.meshrefinement import MeshRefinement from scaffoldmaker.utils.tubemesh2 import * -from scaffoldmaker.utils.zinc_utils import * -from scaffoldmaker.utils.geometry import * -from scaffoldmaker.utils.interpolation import * -from scaffoldmaker.utils.vector import * -from opencmiss.zinc.element import Element, Elementbasis -from opencmiss.zinc.field import Field -from opencmiss.zinc.node import Node class MeshType_3d_colon1(object): ''' From 618ac0fa81e7a2e7b7e915e257a24437f0127968 Mon Sep 17 00:00:00 2001 From: Mabelle Lin Date: Mon, 4 Mar 2019 14:20:29 +1300 Subject: [PATCH 12/44] Clean up and update comments --- scaffoldmaker/meshtypes/meshtype_3d_colon1.py | 39 +++++++++---------- scaffoldmaker/utils/tubemesh2.py | 27 ++++++------- 2 files changed, 33 insertions(+), 33 deletions(-) diff --git a/scaffoldmaker/meshtypes/meshtype_3d_colon1.py b/scaffoldmaker/meshtypes/meshtype_3d_colon1.py index 7fccef5b..a892fba6 100644 --- a/scaffoldmaker/meshtypes/meshtype_3d_colon1.py +++ b/scaffoldmaker/meshtypes/meshtype_3d_colon1.py @@ -11,8 +11,8 @@ class MeshType_3d_colon1(object): ''' Generates a 3-D colon mesh with variable numbers of elements around, along the central line, and through wall. - The colon is created by a function that deforms haustras along - the central line based on the curvature of the line. + The colon is created by a function that generates haustra units + and uses tubemesh to map the units along a central line profile. ''' @staticmethod def getName(): @@ -21,9 +21,9 @@ def getName(): @staticmethod def getDefaultOptions(): return { - 'Number of elements around' : 15, - 'Number of elements along haustra' : 4, - 'Number of elements through wall' : 1, + 'Number of elements around': 15, + 'Number of elements along haustra': 4, + 'Number of elements through wall': 1, 'Number of haustra': 30, 'Inner radius': 1.0, 'Corner radius fraction': 0.5, @@ -32,12 +32,12 @@ def getDefaultOptions(): 'Haustra curvature factor': 2.0, 'Wall thickness': 0.05, 'Tube type': 2, - 'Use cross derivatives' : False, - 'Use linear through wall' : False, - 'Refine' : False, - 'Refine number of elements around' : 1, - 'Refine number of elements along' : 1, - 'Refine number of elements through wall' : 1 + 'Use cross derivatives': False, + 'Use linear through wall': False, + 'Refine': False, + 'Refine number of elements around': 1, + 'Refine number of elements along': 1, + 'Refine number of elements through wall': 1 } @staticmethod @@ -125,16 +125,15 @@ def generateBaseMesh(region, options): elif tubeType == 3: # Human colon in 3D cx = [ [ 0.0, 0.0, 0.0], [0.0, 10.0, 3.0], [5.0, 9.0, 0.0], [ 10.0, 10.0, 2.0 ], [15.0, 15.0, 7.0], [ 20.0, -2.0, 0.0], [ 10.0, -4.0, -0.0] ] cd1 = [ [ 0.0, 10.0, 3.0 ], [ 5.0, 5.0, 0.0 ], [5.0, 0.0, 0.0], [ 10.0, -5.0, 0.0 ], [12.0, 12.0, 0.0], [ 5.0, -12.0, -5.0 ], [ -8.0, 0.0, 0.0 ]] - - length = 0.0 # find arclength of colon + length = 0.0 elementsCountIn = len(cx) - 1 for e in range(elementsCountIn): arcLength = computeCubicHermiteArcLength(cx[e], cd1[e], cx[e + 1], cd1[e + 1], rescaleDerivatives = True) length += arcLength haustraLength = length / haustraCount - + # Generate outer surface of a haustra unit xHaustraOuter, d1HaustraOuter, d2HaustraOuter, haustraAxis = getUnitHaustraOuter(elementsCountAround, elementsCountAlongHaustra, radius, cornerRadiusFraction, haustraRadiusFraction, foldFactor, haustraCurvatureFactor, wallThickness, haustraLength) @@ -167,7 +166,7 @@ def generateMesh(cls, region, options): def getUnitHaustraOuter(elementsCountAround, elementsCountAlongHaustra, radius, cornerRadiusFraction, haustraRadiusFraction, foldFactor, haustraCurvatureFactor, wallThickness, haustraLength): """ - Generates a 3-D haustra mesh with variable numbers + Generates a 3-D haustra unit mesh with variable numbers of elements around, along the central line, and through wall. The haustra has a triangular profile with rounded corners at the interhaustral septa, and a clover profile outside the septa. @@ -179,14 +178,14 @@ def getUnitHaustraOuter(elementsCountAround, elementsCountAlongHaustra, radius, to get a radius of curvature at the corners. :param haustraRadiusFraction: Factor is multiplied by inner haustra radius to obtain radius of intersecting circles in the middle cross-section - of haustra. + along a haustra. :param foldFactor: Factor is multiplied by haustra length to scale derivative along the haustra at the interhaustral septa end. - :param haustraCurvatureFactor: Fator is multiplied by haustra length to + :param haustraCurvatureFactor: Factor is multiplied by haustra length to scale derivative along the haustra at the middle section of the haustra. :param wallThickness: Thickness of haustra through wall. :param haustraLength: Length of a haustra unit. - :return: coordinates, derivatives on outer surface + :return: coordinates, derivatives on outer surface of haustra unit. """ # create nodes @@ -311,7 +310,6 @@ def getUnitHaustraOuter(elementsCountAround, elementsCountAlongHaustra, radius, xInnerList.append(x) dx_ds2 = dx_ds2InnerRaw[n1][n2] dx_ds2InnerList.append(dx_ds2) - unitTangent = normalise(dx_ds2) # Interhaustra if n2 == 0 or n2 > elementsCountAlongHaustra - 1: @@ -367,7 +365,7 @@ def getUnitHaustraOuter(elementsCountAround, elementsCountAlongHaustra, radius, def getOuterCoordinatesAndDerivativesFromInner(xInner, d1Inner, d2Inner, d3Inner, wallThickness, elementsCountAlongHaustra, elementsCountAround): """ - Generates coordinates and derivatives of outer surface from + Generates coordinates and derivatives of outer surface from coordinates and derivatives of inner surface using wall thickness and normals. param xInner: Coordinates on inner surface @@ -433,4 +431,5 @@ def rotationMatrixAboutAxis(rotAxis, theta): rotMatrix = ([[rotAxis[0]*rotAxis[0]*C + cosTheta, rotAxis[0]*rotAxis[1]*C - rotAxis[2]*sinTheta, rotAxis[0]*rotAxis[2]*C + rotAxis[1]*sinTheta], [rotAxis[1]*rotAxis[0]*C + rotAxis[2]*sinTheta, rotAxis[1]*rotAxis[1]*C + cosTheta, rotAxis[1]*rotAxis[2]*C - rotAxis[0]*sinTheta], [rotAxis[2]*rotAxis[0]*C - rotAxis[1]*sinTheta, rotAxis[2]*rotAxis[1]*C + rotAxis[0]*sinTheta, rotAxis[2]*rotAxis[2]*C + cosTheta]]) + return rotMatrix \ No newline at end of file diff --git a/scaffoldmaker/utils/tubemesh2.py b/scaffoldmaker/utils/tubemesh2.py index c9c52e8f..a57eb058 100644 --- a/scaffoldmaker/utils/tubemesh2.py +++ b/scaffoldmaker/utils/tubemesh2.py @@ -32,14 +32,14 @@ def generatetubemesh(region, ''' Generates a 3-D tubular mesh with variable numbers of elements around, along the central axis, and radially through wall. The - tubular mesh is created from a unit profile which is mapped onto + tubular mesh is created from a unit profile which is mapped onto the central line and lateral axes data :param elementsCountAround: number of elements around tube :param elementsCountAlongUnit: number of elements along unit profile :param elementsCountThroughWall: number of elements through wall thickness :param unitsCountAlong: number of units along the tube :param cx: coordinates on central line - :param cd1: derivative 1 along central line + :param cd1: derivative along central line :param xOuter: coordinates on outer surface of unit profile :param d1Outer: derivatives around outer surface of unit profile :param d2Outer: derivatives along outer surface of unit profile @@ -140,7 +140,7 @@ def generatetubemesh(region, for nUnit in range(unitsCountAlong): for nAlongUnit in range(elementsCountAlongUnit+1): n2 = nUnit*elementsCountAlongUnit + nAlongUnit - if nUnit == 0 or (nUnit > 0 and nAlongUnit > 0): # Take first face from first element along, take 2nd to last face for subsequent elements along + if nUnit == 0 or (nUnit > 0 and nAlongUnit > 0): # Rotate to align unit axis with tangent of central line unitMid = [0.0, 0.0, unitLength/elementsCountAlongUnit* nAlongUnit] unitTangent = normalise(sd1[n2]) @@ -213,16 +213,16 @@ def generatetubemesh(region, coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D3_DS1DS2DS3, 1, zero) # print('NodeIdentifier = ', nodeIdentifier, xList[n]) nodeIdentifier = nodeIdentifier + 1 - - # For debugging - Nodes along central line - for pt in range(len(sx)): - node = nodes.createNode(nodeIdentifier, nodetemplate) - cache.setNode(node) - coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_VALUE, 1, sx[pt]) - coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS1, 1, sd1[pt]) - coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS2, 1, sNormal[pt]) - coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS3, 1, sBinormal[pt]) - nodeIdentifier = nodeIdentifier + 1 + + # # For debugging - Nodes along central line + # for pt in range(len(sx)): + # node = nodes.createNode(nodeIdentifier, nodetemplate) + # cache.setNode(node) + # coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_VALUE, 1, sx[pt]) + # coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS1, 1, sd1[pt]) + # coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS2, 1, sNormal[pt]) + # coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS3, 1, sBinormal[pt]) + # nodeIdentifier = nodeIdentifier + 1 # create elements elementIdentifier = nextElementIdentifier @@ -352,4 +352,5 @@ def rotationMatrixAboutAxis(rotAxis, theta): rotMatrix = ([[rotAxis[0]*rotAxis[0]*C + cosTheta, rotAxis[0]*rotAxis[1]*C - rotAxis[2]*sinTheta, rotAxis[0]*rotAxis[2]*C + rotAxis[1]*sinTheta], [rotAxis[1]*rotAxis[0]*C + rotAxis[2]*sinTheta, rotAxis[1]*rotAxis[1]*C + cosTheta, rotAxis[1]*rotAxis[2]*C - rotAxis[0]*sinTheta], [rotAxis[2]*rotAxis[0]*C - rotAxis[1]*sinTheta, rotAxis[2]*rotAxis[1]*C + rotAxis[0]*sinTheta, rotAxis[2]*rotAxis[2]*C + cosTheta]]) + return rotMatrix From 4626e9770602a33b0032b06dbfdf04ee92d3117a Mon Sep 17 00:00:00 2001 From: Mabelle Lin Date: Mon, 4 Mar 2019 15:20:07 +1300 Subject: [PATCH 13/44] Support multiple parameter sets --- scaffoldmaker/meshtypes/meshtype_3d_colon1.py | 21 +++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/scaffoldmaker/meshtypes/meshtype_3d_colon1.py b/scaffoldmaker/meshtypes/meshtype_3d_colon1.py index a892fba6..bdca3ed6 100644 --- a/scaffoldmaker/meshtypes/meshtype_3d_colon1.py +++ b/scaffoldmaker/meshtypes/meshtype_3d_colon1.py @@ -3,11 +3,11 @@ numbers of elements around, along and through wall, with variable radius and thickness along. """ - +from scaffoldmaker.meshtypes.scaffold_base import Scaffold_base from scaffoldmaker.utils.meshrefinement import MeshRefinement from scaffoldmaker.utils.tubemesh2 import * -class MeshType_3d_colon1(object): +class MeshType_3d_colon1(Scaffold_base): ''' Generates a 3-D colon mesh with variable numbers of elements around, along the central line, and through wall. @@ -19,8 +19,15 @@ def getName(): return '3D Colon 1' @staticmethod - def getDefaultOptions(): - return { + def getParameterSetNames(): + return [ + 'Default', + 'Human 1', + 'Section 1'] + + @staticmethod + def getDefaultOptions(parameterSetName='Default'): + options = { 'Number of elements around': 15, 'Number of elements along haustra': 4, 'Number of elements through wall': 1, @@ -39,6 +46,12 @@ def getDefaultOptions(): 'Refine number of elements along': 1, 'Refine number of elements through wall': 1 } + if 'Human 1' in parameterSetName: + options['Tube type'] = 3 + elif 'Section 1' in parameterSetName: + options['Tube type'] = 1 + options['Number of haustra'] = 3 + return options @staticmethod def getOrderedOptionNames(): From 64059f6dc2ed0262bd754a40f15e69e8cafc74f1 Mon Sep 17 00:00:00 2001 From: Mabelle Lin Date: Wed, 6 Mar 2019 09:58:03 +1300 Subject: [PATCH 14/44] Correct haustra to singular form when referring to elements along haustrum --- scaffoldmaker/meshtypes/meshtype_3d_colon1.py | 46 +++++++++---------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/scaffoldmaker/meshtypes/meshtype_3d_colon1.py b/scaffoldmaker/meshtypes/meshtype_3d_colon1.py index bdca3ed6..e31d5e1f 100644 --- a/scaffoldmaker/meshtypes/meshtype_3d_colon1.py +++ b/scaffoldmaker/meshtypes/meshtype_3d_colon1.py @@ -29,7 +29,7 @@ def getParameterSetNames(): def getDefaultOptions(parameterSetName='Default'): options = { 'Number of elements around': 15, - 'Number of elements along haustra': 4, + 'Number of elements along haustrum': 4, 'Number of elements through wall': 1, 'Number of haustra': 30, 'Inner radius': 1.0, @@ -57,7 +57,7 @@ def getDefaultOptions(parameterSetName='Default'): def getOrderedOptionNames(): return [ 'Number of elements around', - 'Number of elements along haustra', + 'Number of elements along haustrum', 'Number of elements through wall', 'Number of haustra', 'Inner radius', @@ -115,7 +115,7 @@ def generateBaseMesh(region, options): :return: None """ elementsCountAround = options['Number of elements around'] - elementsCountAlongHaustra = options['Number of elements along haustra'] + elementsCountAlongHaustrum = options['Number of elements along haustrum'] elementsCountThroughWall = options['Number of elements through wall'] haustraCount = options['Number of haustra'] radius = options['Inner radius'] @@ -127,7 +127,7 @@ def generateBaseMesh(region, options): tubeType = options['Tube type'] useCrossDerivatives = options['Use cross derivatives'] useCubicHermiteThroughWall = not(options['Use linear through wall']) - elementsCountAlong = int(elementsCountAlongHaustra*haustraCount) + elementsCountAlong = int(elementsCountAlongHaustrum*haustraCount) if tubeType == 1: # Straight tube cx = [[-4.0, 1.0, 3.0], [ 1.0, 2.0, 0.0 ] ] @@ -148,11 +148,11 @@ def generateBaseMesh(region, options): haustraLength = length / haustraCount # Generate outer surface of a haustra unit - xHaustraOuter, d1HaustraOuter, d2HaustraOuter, haustraAxis = getUnitHaustraOuter(elementsCountAround, elementsCountAlongHaustra, radius, cornerRadiusFraction, + xHaustraOuter, d1HaustraOuter, d2HaustraOuter, haustraAxis = getUnitHaustraOuter(elementsCountAround, elementsCountAlongHaustrum, radius, cornerRadiusFraction, haustraRadiusFraction, foldFactor, haustraCurvatureFactor, wallThickness, haustraLength) # Generate tube mesh - nextNodeIdentifier, nextElementIdentifier = generatetubemesh(region, elementsCountAround, elementsCountAlongHaustra, elementsCountThroughWall, haustraCount, + nextNodeIdentifier, nextElementIdentifier = generatetubemesh(region, elementsCountAround, elementsCountAlongHaustrum, elementsCountThroughWall, haustraCount, cx, cd1, xHaustraOuter, d1HaustraOuter, d2HaustraOuter, wallThickness, haustraAxis, haustraLength, useCrossDerivatives, useCubicHermiteThroughWall) @classmethod @@ -176,7 +176,7 @@ def generateMesh(cls, region, options): meshrefinement = MeshRefinement(baseRegion, region) meshrefinement.refineAllElementsCubeStandard3d(refineElementsCountAround, refineElementsCountAlong, refineElementsCountThroughWall) -def getUnitHaustraOuter(elementsCountAround, elementsCountAlongHaustra, radius, cornerRadiusFraction, +def getUnitHaustraOuter(elementsCountAround, elementsCountAlongHaustrum, radius, cornerRadiusFraction, haustraRadiusFraction, foldFactor, haustraCurvatureFactor, wallThickness, haustraLength): """ Generates a 3-D haustra unit mesh with variable numbers @@ -184,7 +184,7 @@ def getUnitHaustraOuter(elementsCountAround, elementsCountAlongHaustra, radius, The haustra has a triangular profile with rounded corners at the interhaustral septa, and a clover profile outside the septa. :param elementsCountAround: Number of elements around haustra. - :param elementsCountAlongHaustra: Number of elements along haustra. + :param elementsCountAlongHaustrum: Number of elements along haustrum. :param radius: Inner radius of haustra. :param cornerRadiusFraction: Roundness of triangular corners of interhaustral septa. Factor is multiplied by inner haustra radius @@ -309,13 +309,13 @@ def getUnitHaustraOuter(elementsCountAround, elementsCountAlongHaustra, radius, d3 = d2 = d1 = [c* haustraLength/3 for c in unitZ] nx = [v1, v2, v3] nd1 = [d1, d2, d3] - sx, sd1, se, sxi, _ = sampleCubicHermiteCurves(nx, nd1, elementsCountAlongHaustra) + sx, sd1, se, sxi, _ = sampleCubicHermiteCurves(nx, nd1, elementsCountAlongHaustrum) xInnerRaw.append(sx) dx_ds2InnerRaw.append(sd1) # Re-arrange sample order & calculate dx_ds1 and dx_ds3 from dx_ds2 dx_ds1InnerList = dx_ds1InnerList + d1Inner - for n2 in range(elementsCountAlongHaustra + 1): + for n2 in range(elementsCountAlongHaustrum + 1): xAround = [] unitdx_ds1Around = [] for n1 in range(elementsCountAround): @@ -325,23 +325,23 @@ def getUnitHaustraOuter(elementsCountAround, elementsCountAlongHaustra, radius, dx_ds2InnerList.append(dx_ds2) unitTangent = normalise(dx_ds2) # Interhaustra - if n2 == 0 or n2 > elementsCountAlongHaustra - 1: + if n2 == 0 or n2 > elementsCountAlongHaustrum - 1: dx_ds1 = d1Inner[n1] unitdx_ds3 = crossproduct3(normalise(dx_ds1), unitTangent) else: # Haustra - if elementsCountAlongHaustra == 2: + if elementsCountAlongHaustrum == 2: unitdx_ds1 = normalise(d1InnerHaustra[n1]) else: if n1%(elementsCountAround/3) == 0: # intersection points unitdx_ds1 = normalise(d1InnerHaustra[n1]) else: # points on clover - if elementsCountAlongHaustra > 3: - if n2 < int(elementsCountAlongHaustra/2): # first half of haustraLength + if elementsCountAlongHaustrum > 3: + if n2 < int(elementsCountAlongHaustrum/2): # first half of haustraLength axisRot = crossproduct3(unitZ, unitTangent) - elif n2 > int(elementsCountAlongHaustra/2): # second half of haustraLength + elif n2 > int(elementsCountAlongHaustrum/2): # second half of haustraLength axisRot = crossproduct3(unitTangent, unitZ) - elif elementsCountAlongHaustra == 3: # 3 elementsAlongHaustra + elif elementsCountAlongHaustrum == 3: # 3 elementsAlongHaustrum axisRot = crossproduct3(unitTangent, unitZ) rotFrame = rotationMatrixAboutAxis(axisRot, math.pi/2) @@ -351,7 +351,7 @@ def getUnitHaustraOuter(elementsCountAround, elementsCountAlongHaustra, radius, xAround.append(x) unitdx_ds1Around.append(unitdx_ds1) - if n2 > 0 and n2 < elementsCountAlongHaustra: + if n2 > 0 and n2 < elementsCountAlongHaustrum: dx_ds1InnerAroundList = [] for n1 in range(elementsCountAround): v1 = xAround[n1] @@ -372,11 +372,11 @@ def getUnitHaustraOuter(elementsCountAround, elementsCountAlongHaustra, radius, dx_ds3InnerUnitList.append(unitdx_ds3) # Pre-calculate node locations and derivatives on outer boundary - xOuter, d1Outer, d2Outer = getOuterCoordinatesAndDerivativesFromInner(xInnerList, dx_ds1InnerList, dx_ds2InnerList, dx_ds3InnerUnitList, wallThickness, elementsCountAlongHaustra, elementsCountAround) + xOuter, d1Outer, d2Outer = getOuterCoordinatesAndDerivativesFromInner(xInnerList, dx_ds1InnerList, dx_ds2InnerList, dx_ds3InnerUnitList, wallThickness, elementsCountAlongHaustrum, elementsCountAround) return xOuter, d1Outer, d2Outer, unitZ -def getOuterCoordinatesAndDerivativesFromInner(xInner, d1Inner, d2Inner, d3Inner, wallThickness, elementsCountAlongHaustra, elementsCountAround): +def getOuterCoordinatesAndDerivativesFromInner(xInner, d1Inner, d2Inner, d3Inner, wallThickness, elementsCountAlongHaustrum, elementsCountAround): """ Generates coordinates and derivatives of outer surface from coordinates and derivatives of inner surface using wall thickness @@ -386,7 +386,7 @@ def getOuterCoordinatesAndDerivativesFromInner(xInner, d1Inner, d2Inner, d3Inner param d2Inner: Derivatives on inner surface along haustra param d3Inner: Derivatives on inner surface through wall param thickness: Thickness of wall - param elementsCountAlongHaustra: Number of elements along haustra + param elementsCountAlongHaustrum: Number of elements along haustrum param elementsCountAround: Number of elements around haustra return xOuter: Coordinates on outer surface return nd1Outer: Derivatives on outer surface around haustra @@ -396,7 +396,7 @@ def getOuterCoordinatesAndDerivativesFromInner(xInner, d1Inner, d2Inner, d3Inner nd1Outer = [] nd2Outer = [] - for n2 in range(elementsCountAlongHaustra + 1): + for n2 in range(elementsCountAlongHaustrum + 1): for n1 in range(elementsCountAround): n = n2*elementsCountAround + n1 x = [xInner[n][i] + d3Inner[n][i]*wallThickness for i in range(3)] @@ -410,11 +410,11 @@ def getOuterCoordinatesAndDerivativesFromInner(xInner, d1Inner, d2Inner, d3Inner factor = 1.0 - curvatureAround*wallThickness nd1 = [ factor*c for c in d1Inner[n]] # d2 - if n2 > 0 and n2 < elementsCountAlongHaustra: + if n2 > 0 and n2 < elementsCountAlongHaustrum: prevIdx = (n2-1)*elementsCountAround + n1 nextIdx = (n2+1)*elementsCountAround + n1 elif n2 == 0: - prevIdx = (elementsCountAlongHaustra-1)*elementsCountAround + n1 + prevIdx = (elementsCountAlongHaustrum-1)*elementsCountAround + n1 nextIdx = (n2+1)*elementsCountAround + n1 else: prevIdx = (n2-1)*elementsCountAround + n1 From e42c2a35779e5177a75b249846ac2ed1d073e5b3 Mon Sep 17 00:00:00 2001 From: Mabelle Lin Date: Wed, 6 Mar 2019 10:14:39 +1300 Subject: [PATCH 15/44] Change default to central line profile for colon --- scaffoldmaker/meshtypes/meshtype_3d_centrallinetube1.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/scaffoldmaker/meshtypes/meshtype_3d_centrallinetube1.py b/scaffoldmaker/meshtypes/meshtype_3d_centrallinetube1.py index 273ac0bd..ce6e34a8 100644 --- a/scaffoldmaker/meshtypes/meshtype_3d_centrallinetube1.py +++ b/scaffoldmaker/meshtypes/meshtype_3d_centrallinetube1.py @@ -24,9 +24,9 @@ def getName(): def getDefaultOptions(parameterSetName='Default'): return { 'Number of elements around' : 8, - 'Number of elements along' : 6, + 'Number of elements along' : 30, #6 'Number of elements through wall' : 1, - 'Tube type' : 1, + 'Tube type' : 4, 'Use cross derivatives' : False, 'Use linear through wall' : False, 'Refine' : False, @@ -123,8 +123,8 @@ def generateBaseMesh(region, options): # Colon in x-y plane cx = [ [ 0.0, 0.0, 0.0], [0.0, 10.0, 0.0], [5.0, 9.0, 0.0], [ 10.0, 10.0, 0.0 ], [ 10.0, -2.0, 0.0], [ 7.0, -4.0, 0.0] ] cd1 = [ [ 0.0, 10.0, 0.0 ], [ 5.0, 5.0, 0.0 ], [5.0, 0.0, 0.0], [ 5.0, -5.0, 0.0 ], [ -3.0, -5.0, 0.0 ], [ -3.0, 0.0, 0.0 ]] - cd2 = [ [ 0.0, 0.5, 0.0 ], [ 0.0, 0.5, 0.0 ], [ 0.0, 0.5, 0.0 ], [ 0.0, 0.5, 0.0], [ 0.0, 0.5, 0.0 ], [ 0.0, 0.5, 0.0 ]] - cd3 = [ [ 0.0, 0.0, 0.5 ], [ 0.0, 0.0, 0.5 ], [ 0.0, 0.0, 0.5], [ 0.0, 0.0, 0.5 ], [ 0.0, 0.0, 0.5], [ 0.0, 0.0, 0.5]] + cd2 = [ [ 0.0, 1.0, 0.0 ], [ 0.0, 1.0, 0.0 ], [ 0.0, 1.0, 0.0 ], [ 0.0, 1.0, 0.0], [ 0.0, 1.0, 0.0 ], [ 0.0, 1.0, 0.0 ]] + cd3 = [ [ 0.0, 0.0, 1.0 ], [ 0.0, 0.0, 1.0 ], [ 0.0, 0.0, 1.0], [ 0.0, 0.0, 1.0 ], [ 0.0, 0.0, 1.0], [ 0.0, 0.0, 1.0]] # thickness in cd2 and cd3 directions and derivatives (rate of change) t2 = [ 0.5, 0.5, 0.5, 0.5, 0.5, 0.5 ] From 94222386567c815f245217aadb3a70494e51d69b Mon Sep 17 00:00:00 2001 From: Mabelle Lin Date: Wed, 6 Mar 2019 10:17:15 +1300 Subject: [PATCH 16/44] Change tube type options for input outside range --- scaffoldmaker/meshtypes/meshtype_3d_centrallinetube1.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/scaffoldmaker/meshtypes/meshtype_3d_centrallinetube1.py b/scaffoldmaker/meshtypes/meshtype_3d_centrallinetube1.py index ce6e34a8..16af63e4 100644 --- a/scaffoldmaker/meshtypes/meshtype_3d_centrallinetube1.py +++ b/scaffoldmaker/meshtypes/meshtype_3d_centrallinetube1.py @@ -62,8 +62,10 @@ def checkOptions(options): options[key] = 1 if (options['Number of elements around'] < 2) : options['Number of elements around'] = 2 - if (options['Tube type'] < 1 or options['Tube type'] > 5 ) : + if (options['Tube type'] < 1): options['Tube type'] = 1 + if (options['Tube type'] > 5): + options['Tube type'] = 5 @staticmethod def generateBaseMesh(region, options): From b05320fa3e8ac0db647e9c9e0bfc536ca2832ced Mon Sep 17 00:00:00 2001 From: Mabelle Lin Date: Wed, 6 Mar 2019 10:21:56 +1300 Subject: [PATCH 17/44] Change tube type options for inputs outside range --- scaffoldmaker/meshtypes/meshtype_3d_colon1.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/scaffoldmaker/meshtypes/meshtype_3d_colon1.py b/scaffoldmaker/meshtypes/meshtype_3d_colon1.py index e31d5e1f..5ae614a5 100644 --- a/scaffoldmaker/meshtypes/meshtype_3d_colon1.py +++ b/scaffoldmaker/meshtypes/meshtype_3d_colon1.py @@ -103,8 +103,10 @@ def checkOptions(options): 'Interhaustra fold factor']: if options[key] > 1.0: options[key] = 1.0 - if (options['Tube type'] < 1 or options['Tube type'] > 3 ) : + if options['Tube type'] < 1: options['Tube type'] = 1 + if options['Tube type'] > 3: + options['Tube type'] = 3 @staticmethod def generateBaseMesh(region, options): From fc111e062b6622a45192a30044618c3f92fc2dd5 Mon Sep 17 00:00:00 2001 From: Mabelle Lin Date: Wed, 6 Mar 2019 10:25:06 +1300 Subject: [PATCH 18/44] Add newline to end of script --- scaffoldmaker/meshtypes/meshtype_3d_colon1.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scaffoldmaker/meshtypes/meshtype_3d_colon1.py b/scaffoldmaker/meshtypes/meshtype_3d_colon1.py index 5ae614a5..a4a11bd0 100644 --- a/scaffoldmaker/meshtypes/meshtype_3d_colon1.py +++ b/scaffoldmaker/meshtypes/meshtype_3d_colon1.py @@ -447,4 +447,4 @@ def rotationMatrixAboutAxis(rotAxis, theta): [rotAxis[1]*rotAxis[0]*C + rotAxis[2]*sinTheta, rotAxis[1]*rotAxis[1]*C + cosTheta, rotAxis[1]*rotAxis[2]*C - rotAxis[0]*sinTheta], [rotAxis[2]*rotAxis[0]*C - rotAxis[1]*sinTheta, rotAxis[2]*rotAxis[1]*C + rotAxis[0]*sinTheta, rotAxis[2]*rotAxis[2]*C + cosTheta]]) - return rotMatrix \ No newline at end of file + return rotMatrix From 30c8e3f9ec8bcea1f69e2617a03fe08474fcff77 Mon Sep 17 00:00:00 2001 From: Mabelle Lin Date: Wed, 6 Mar 2019 10:59:07 +1300 Subject: [PATCH 19/44] Rename and move function for getting rotation matrix to utils --- scaffoldmaker/meshtypes/meshtype_3d_colon1.py | 19 ++------------ scaffoldmaker/utils/matrix.py | 21 ++++++++++++++++ scaffoldmaker/utils/tubemesh.py | 20 +++------------ scaffoldmaker/utils/tubemesh2.py | 25 ++++--------------- 4 files changed, 31 insertions(+), 54 deletions(-) create mode 100644 scaffoldmaker/utils/matrix.py diff --git a/scaffoldmaker/meshtypes/meshtype_3d_colon1.py b/scaffoldmaker/meshtypes/meshtype_3d_colon1.py index a4a11bd0..2831c8b2 100644 --- a/scaffoldmaker/meshtypes/meshtype_3d_colon1.py +++ b/scaffoldmaker/meshtypes/meshtype_3d_colon1.py @@ -4,6 +4,7 @@ variable radius and thickness along. """ from scaffoldmaker.meshtypes.scaffold_base import Scaffold_base +from scaffoldmaker.utils.matrix import * from scaffoldmaker.utils.meshrefinement import MeshRefinement from scaffoldmaker.utils.tubemesh2 import * @@ -346,7 +347,7 @@ def getUnitHaustraOuter(elementsCountAround, elementsCountAlongHaustrum, radius, elif elementsCountAlongHaustrum == 3: # 3 elementsAlongHaustrum axisRot = crossproduct3(unitTangent, unitZ) - rotFrame = rotationMatrixAboutAxis(axisRot, math.pi/2) + rotFrame = getRotationMatrixFromAxisAngle(axisRot, math.pi/2) rotNormal = [rotFrame[j][0]*unitTangent[0] + rotFrame[j][1]*unitTangent[1] + rotFrame[j][2]*unitTangent[2] for j in range(3)] unitdx_ds3 = normalise(rotNormal) unitdx_ds1 = crossproduct3(unitTangent, unitdx_ds3) @@ -432,19 +433,3 @@ def getOuterCoordinatesAndDerivativesFromInner(xInner, d1Inner, d2Inner, d3Inner nd2Outer.append(nd2) return xOuter, nd1Outer, nd2Outer - -def rotationMatrixAboutAxis(rotAxis, theta): - """ - Generate the rotation matrix for rotation about an axis. - :param rotAxis: axis of rotation - :param theta: angle of rotation - :return: rotation matrix - """ - cosTheta = math.cos(theta) - sinTheta = math.sin(theta) - C = 1 - cosTheta - rotMatrix = ([[rotAxis[0]*rotAxis[0]*C + cosTheta, rotAxis[0]*rotAxis[1]*C - rotAxis[2]*sinTheta, rotAxis[0]*rotAxis[2]*C + rotAxis[1]*sinTheta], - [rotAxis[1]*rotAxis[0]*C + rotAxis[2]*sinTheta, rotAxis[1]*rotAxis[1]*C + cosTheta, rotAxis[1]*rotAxis[2]*C - rotAxis[0]*sinTheta], - [rotAxis[2]*rotAxis[0]*C - rotAxis[1]*sinTheta, rotAxis[2]*rotAxis[1]*C + rotAxis[0]*sinTheta, rotAxis[2]*rotAxis[2]*C + cosTheta]]) - - return rotMatrix diff --git a/scaffoldmaker/utils/matrix.py b/scaffoldmaker/utils/matrix.py new file mode 100644 index 00000000..fdb88ad5 --- /dev/null +++ b/scaffoldmaker/utils/matrix.py @@ -0,0 +1,21 @@ +''' +Utility functions for matrix. +''' + +import math + +def getRotationMatrixFromAxisAngle(rotAxis, theta): + """ + Generate the rotation matrix for rotation about an axis. + :param rotAxis: axis of rotation + :param theta: angle of rotation + :return: rotation matrix + """ + cosTheta = math.cos(theta) + sinTheta = math.sin(theta) + C = 1 - cosTheta + rotMatrix = ([[rotAxis[0]*rotAxis[0]*C + cosTheta, rotAxis[0]*rotAxis[1]*C - rotAxis[2]*sinTheta, rotAxis[0]*rotAxis[2]*C + rotAxis[1]*sinTheta], + [rotAxis[1]*rotAxis[0]*C + rotAxis[2]*sinTheta, rotAxis[1]*rotAxis[1]*C + cosTheta, rotAxis[1]*rotAxis[2]*C - rotAxis[0]*sinTheta], + [rotAxis[2]*rotAxis[0]*C - rotAxis[1]*sinTheta, rotAxis[2]*rotAxis[1]*C + rotAxis[0]*sinTheta, rotAxis[2]*rotAxis[2]*C + cosTheta]]) + + return rotMatrix diff --git a/scaffoldmaker/utils/tubemesh.py b/scaffoldmaker/utils/tubemesh.py index b6472322..77424bf7 100644 --- a/scaffoldmaker/utils/tubemesh.py +++ b/scaffoldmaker/utils/tubemesh.py @@ -8,10 +8,11 @@ import math from scaffoldmaker.utils.eftfactory_bicubichermitelinear import eftfactory_bicubichermitelinear from scaffoldmaker.utils.eftfactory_tricubichermite import eftfactory_tricubichermite -from scaffoldmaker.utils.zinc_utils import * from scaffoldmaker.utils.geometry import * from scaffoldmaker.utils.interpolation import * +from scaffoldmaker.utils.matrix import * from scaffoldmaker.utils.vector import * +from scaffoldmaker.utils.zinc_utils import * from opencmiss.zinc.element import Element, Elementbasis from opencmiss.zinc.field import Field from opencmiss.zinc.node import Node @@ -83,7 +84,7 @@ def generatetubemesh(region, if magnitude(cp)> 0.0: axisRot = normalise(cp) thetaRot = math.acos(dotproduct(prevUnitTangent, unitTangent)) - rotFrame = rotationMatrixAboutAxis(axisRot, thetaRot) + rotFrame = getRotationMatrixFromAxisAngle(axisRot, thetaRot) rotNormal = [rotFrame[j][0]*prevUnitNormal[0] + rotFrame[j][1]*prevUnitNormal[1] + rotFrame[j][2]*prevUnitNormal[2] for j in range(3)] unitNormal = normalise(rotNormal) unitBinormal = crossproduct3(unitTangent, unitNormal) @@ -273,18 +274,3 @@ def generatetubemesh(region, fm.endChange() return nodeIdentifier, elementIdentifier - -def rotationMatrixAboutAxis(rotAxis, theta): - """ - Generate the rotation matrix for rotation about an axis. - :param rotAxis: axis of rotation - :param theta: angle of rotation - :return: rotation matrix - """ - cosTheta = math.cos(theta) - sinTheta = math.sin(theta) - C = 1 - cosTheta - rotMatrix = ([[rotAxis[0]*rotAxis[0]*C + cosTheta, rotAxis[0]*rotAxis[1]*C - rotAxis[2]*sinTheta, rotAxis[0]*rotAxis[2]*C + rotAxis[1]*sinTheta], - [rotAxis[1]*rotAxis[0]*C + rotAxis[2]*sinTheta, rotAxis[1]*rotAxis[1]*C + cosTheta, rotAxis[1]*rotAxis[2]*C - rotAxis[0]*sinTheta], - [rotAxis[2]*rotAxis[0]*C - rotAxis[1]*sinTheta, rotAxis[2]*rotAxis[1]*C + rotAxis[0]*sinTheta, rotAxis[2]*rotAxis[2]*C + cosTheta]]) - return rotMatrix diff --git a/scaffoldmaker/utils/tubemesh2.py b/scaffoldmaker/utils/tubemesh2.py index a57eb058..28504ea4 100644 --- a/scaffoldmaker/utils/tubemesh2.py +++ b/scaffoldmaker/utils/tubemesh2.py @@ -9,10 +9,11 @@ import math from scaffoldmaker.utils.eftfactory_bicubichermitelinear import eftfactory_bicubichermitelinear from scaffoldmaker.utils.eftfactory_tricubichermite import eftfactory_tricubichermite -from scaffoldmaker.utils.zinc_utils import * from scaffoldmaker.utils.geometry import * from scaffoldmaker.utils.interpolation import * +from scaffoldmaker.utils.matrix import * from scaffoldmaker.utils.vector import * +from scaffoldmaker.utils.zinc_utils import * from opencmiss.zinc.element import Element, Elementbasis from opencmiss.zinc.field import Field from opencmiss.zinc.node import Node @@ -77,7 +78,7 @@ def generatetubemesh(region, if magnitude(cp)> 0.0: axisRot = normalise(cp) thetaRot = math.acos(dotproduct(prevUnitTangent, unitTangent)) - rotFrame = rotationMatrixAboutAxis(axisRot, thetaRot) + rotFrame = getRotationMatrixFromAxisAngle(axisRot, thetaRot) rotNormal = [rotFrame[j][0]*prevUnitNormal[0] + rotFrame[j][1]*prevUnitNormal[1] + rotFrame[j][2]*prevUnitNormal[2] for j in range(3)] unitNormal = normalise(rotNormal) unitBinormal = crossproduct3(unitTangent, unitNormal) @@ -148,7 +149,7 @@ def generatetubemesh(region, if magnitude(cp)> 0.0: axisRot = normalise(cp) thetaRot = math.acos(dotproduct(unitAxis, unitTangent)) - rotFrame = rotationMatrixAboutAxis(axisRot, thetaRot) + rotFrame = getRotationMatrixFromAxisAngle(axisRot, thetaRot) midRot = [rotFrame[j][0]*unitMid[0] + rotFrame[j][1]*unitMid[1] + rotFrame[j][2]*unitMid[2] for j in range(3)] translateMatrix = [sx[n2][j] - midRot[j] for j in range(3)] else: @@ -170,7 +171,7 @@ def generatetubemesh(region, cp2 = normalise(crossproduct3(normalise(sBinormal[n2]), firstVector)) signThetaRot2 = dotproduct(unitTangent, cp2) axisRot2 = unitTangent - rotFrame2 = rotationMatrixAboutAxis(axisRot2, -signThetaRot2*thetaRot2) + rotFrame2 = getRotationMatrixFromAxisAngle(axisRot2, -signThetaRot2*thetaRot2) xRot2 = [rotFrame2[j][0]*xRot1[0] + rotFrame2[j][1]*xRot1[1] + rotFrame2[j][2]*xRot1[2] for j in range(3)] d1Rot2 = [rotFrame2[j][0]*d1Rot1[0] + rotFrame2[j][1]*d1Rot1[1] + rotFrame2[j][2]*d1Rot1[2] for j in range(3)] d2Rot2 = [rotFrame2[j][0]*d2Rot1[0] + rotFrame2[j][1]*d2Rot1[1] + rotFrame2[j][2]*d2Rot1[2] for j in range(3)] @@ -338,19 +339,3 @@ def interpolatefromInnerAndOuter( xInner, xOuter, thickness, xi3, curvatureOuter dx_ds3List.append(dx_ds3) return xList, dx_ds1List, dx_ds2List, dx_ds3List - -def rotationMatrixAboutAxis(rotAxis, theta): - """ - Generate the rotation matrix for rotation about an axis. - :param rotAxis: axis of rotation - :param theta: angle of rotation - :return: rotation matrix - """ - cosTheta = math.cos(theta) - sinTheta = math.sin(theta) - C = 1 - cosTheta - rotMatrix = ([[rotAxis[0]*rotAxis[0]*C + cosTheta, rotAxis[0]*rotAxis[1]*C - rotAxis[2]*sinTheta, rotAxis[0]*rotAxis[2]*C + rotAxis[1]*sinTheta], - [rotAxis[1]*rotAxis[0]*C + rotAxis[2]*sinTheta, rotAxis[1]*rotAxis[1]*C + cosTheta, rotAxis[1]*rotAxis[2]*C - rotAxis[0]*sinTheta], - [rotAxis[2]*rotAxis[0]*C - rotAxis[1]*sinTheta, rotAxis[2]*rotAxis[1]*C + rotAxis[0]*sinTheta, rotAxis[2]*rotAxis[2]*C + cosTheta]]) - - return rotMatrix From 773bc77471b6217b0d7a423d59cde66d1788979c Mon Sep 17 00:00:00 2001 From: Mabelle Lin Date: Wed, 6 Mar 2019 11:42:05 +1300 Subject: [PATCH 20/44] Update names of radius/fold/curvature parameters --- scaffoldmaker/meshtypes/meshtype_3d_colon1.py | 74 ++++++++++--------- 1 file changed, 38 insertions(+), 36 deletions(-) diff --git a/scaffoldmaker/meshtypes/meshtype_3d_colon1.py b/scaffoldmaker/meshtypes/meshtype_3d_colon1.py index 2831c8b2..943f7868 100644 --- a/scaffoldmaker/meshtypes/meshtype_3d_colon1.py +++ b/scaffoldmaker/meshtypes/meshtype_3d_colon1.py @@ -34,10 +34,10 @@ def getDefaultOptions(parameterSetName='Default'): 'Number of elements through wall': 1, 'Number of haustra': 30, 'Inner radius': 1.0, - 'Corner radius fraction': 0.5, - 'Haustra radius fraction': 0.5, - 'Interhaustra fold factor': 0.5, - 'Haustra curvature factor': 2.0, + 'Corner inner radius factor': 0.5, + 'Haustra inner radius factor': 0.5, + 'Haustrum segment end-derivative factor': 0.5, + 'Haustrum segment mid-derivative factor': 2.0, 'Wall thickness': 0.05, 'Tube type': 2, 'Use cross derivatives': False, @@ -62,10 +62,10 @@ def getOrderedOptionNames(): 'Number of elements through wall', 'Number of haustra', 'Inner radius', - 'Corner radius fraction', - 'Haustra radius fraction', - 'Interhaustra fold factor', - 'Haustra curvature factor', + 'Corner inner radius factor', + 'Haustra inner radius factor', + 'Haustrum segment end-derivative factor', + 'Haustrum segment mid-derivative factor', 'Wall thickness', 'Tube type', 'Use cross derivatives', @@ -91,17 +91,17 @@ def checkOptions(options): options['Number of elements around'] = (options['Number of elements around']//3)*3 for key in [ 'Inner radius', - 'Haustra radius fraction', - 'Interhaustra fold factor', - 'Haustra curvature factor', + 'Haustra inner radius factor', + 'Haustrum segment end-derivative factor', + 'Haustrum segment mid-derivative factor', 'Wall thickness']: if options[key] < 0.0: options[key] = 0.0 - if options['Corner radius fraction'] < 0.1: - options['Corner radius fraction'] = 0.1 + if options['Corner inner radius factor'] < 0.1: + options['Corner inner radius factor'] = 0.1 for key in [ - 'Corner radius fraction', - 'Interhaustra fold factor']: + 'Corner inner radius factor', + 'Haustrum segment end-derivative factor']: if options[key] > 1.0: options[key] = 1.0 if options['Tube type'] < 1: @@ -122,10 +122,10 @@ def generateBaseMesh(region, options): elementsCountThroughWall = options['Number of elements through wall'] haustraCount = options['Number of haustra'] radius = options['Inner radius'] - cornerRadiusFraction = options['Corner radius fraction'] - haustraRadiusFraction = options['Haustra radius fraction'] - foldFactor = options['Interhaustra fold factor'] - haustraCurvatureFactor = options['Haustra curvature factor'] + cornerInnerRadiusFactor = options['Corner inner radius factor'] + haustraInnerRadiusFactor = options['Haustra inner radius factor'] + haustrumSegmentEndDerivativeFactor = options['Haustrum segment end-derivative factor'] + haustrumSegmentMidDerivativeFactor = options['Haustrum segment mid-derivative factor'] wallThickness = options['Wall thickness'] tubeType = options['Tube type'] useCrossDerivatives = options['Use cross derivatives'] @@ -151,8 +151,8 @@ def generateBaseMesh(region, options): haustraLength = length / haustraCount # Generate outer surface of a haustra unit - xHaustraOuter, d1HaustraOuter, d2HaustraOuter, haustraAxis = getUnitHaustraOuter(elementsCountAround, elementsCountAlongHaustrum, radius, cornerRadiusFraction, - haustraRadiusFraction, foldFactor, haustraCurvatureFactor, wallThickness, haustraLength) + xHaustraOuter, d1HaustraOuter, d2HaustraOuter, haustraAxis = getUnitHaustraOuter(elementsCountAround, elementsCountAlongHaustrum, radius, cornerInnerRadiusFactor, + haustraInnerRadiusFactor, haustrumSegmentEndDerivativeFactor, haustrumSegmentMidDerivativeFactor, wallThickness, haustraLength) # Generate tube mesh nextNodeIdentifier, nextElementIdentifier = generatetubemesh(region, elementsCountAround, elementsCountAlongHaustrum, elementsCountThroughWall, haustraCount, @@ -179,8 +179,8 @@ def generateMesh(cls, region, options): meshrefinement = MeshRefinement(baseRegion, region) meshrefinement.refineAllElementsCubeStandard3d(refineElementsCountAround, refineElementsCountAlong, refineElementsCountThroughWall) -def getUnitHaustraOuter(elementsCountAround, elementsCountAlongHaustrum, radius, cornerRadiusFraction, - haustraRadiusFraction, foldFactor, haustraCurvatureFactor, wallThickness, haustraLength): +def getUnitHaustraOuter(elementsCountAround, elementsCountAlongHaustrum, radius, cornerInnerRadiusFactor, + haustraInnerRadiusFactor, haustrumSegmentEndDerivativeFactor, haustrumSegmentMidDerivativeFactor, wallThickness, haustraLength): """ Generates a 3-D haustra unit mesh with variable numbers of elements around, along the central line, and through wall. @@ -188,17 +188,19 @@ def getUnitHaustraOuter(elementsCountAround, elementsCountAlongHaustrum, radius, interhaustral septa, and a clover profile outside the septa. :param elementsCountAround: Number of elements around haustra. :param elementsCountAlongHaustrum: Number of elements along haustrum. - :param radius: Inner radius of haustra. - :param cornerRadiusFraction: Roundness of triangular corners of - interhaustral septa. Factor is multiplied by inner haustra radius + :param radius: Inner radius defined from center of triangular + profile to vertex of the triangle. + :param cornerInnerRadiusFactor: Roundness of triangular corners of + interhaustral septa. Factor is multiplied by inner radius to get a radius of curvature at the corners. - :param haustraRadiusFraction: Factor is multiplied by inner haustra + :param haustraInnerRadiusFactor: Factor is multiplied by inner radius to obtain radius of intersecting circles in the middle cross-section - along a haustra. - :param foldFactor: Factor is multiplied by haustra length to scale derivative - along the haustra at the interhaustral septa end. - :param haustraCurvatureFactor: Factor is multiplied by haustra length to - scale derivative along the haustra at the middle section of the haustra. + along a haustrum. + :param haustrumSegmentEndDerivativeFactor: Factor is multiplied by haustra + length to scale derivative along the haustra at the interhaustral septa end. + :param haustrumSegmentMidDerivativeFactor: Factor is multiplied by haustra + length to scale derivative along the haustra at the middle section of the + haustra. :param wallThickness: Thickness of haustra through wall. :param haustraLength: Length of a haustra unit. :return: coordinates, derivatives on outer surface of haustra unit. @@ -210,9 +212,9 @@ def getUnitHaustraOuter(elementsCountAround, elementsCountAlongHaustrum, radius, dx_ds2 = [ 0.0, 0.0, 0.0 ] dx_ds3 = [ 0.0, 0.0, 0.0 ] radiansRangeRC = [7*math.pi/4, 0.0, math.pi/4] - cornerRC = cornerRadiusFraction*radius + cornerRC = cornerInnerRadiusFactor*radius unitZ = [0.0, 0.0, 1.0] - haustraRadius = (haustraRadiusFraction + 1)*radius + haustraRadius = (haustraInnerRadiusFactor + 1)*radius xAround = [] d1Around = [] xInner = [] @@ -298,10 +300,10 @@ def getUnitHaustraOuter(elementsCountAround, elementsCountAlongHaustrum, radius, for n1 in range(elementsCountAround): if n1%(elementsCountAround/3) > 0.0: v1 = [xInner[n1][0], xInner[n1][1], 0.0] - startArcLength = foldFactor * haustraLength + startArcLength = haustrumSegmentEndDerivativeFactor * haustraLength d1 = [ c*startArcLength for c in unitZ] v2 = [xHaustraInner[n1][0], xHaustraInner[n1][1], haustraLength/2] - midArcLength = haustraCurvatureFactor * haustraLength + midArcLength = haustrumSegmentMidDerivativeFactor * haustraLength d2 = [ c*midArcLength for c in unitZ] v3 = [xInner[n1][0], xInner[n1][1], haustraLength] d3 = [ c*startArcLength for c in unitZ] From 9e1c0945230e6cde2858706f0bc8879c7e989e1d Mon Sep 17 00:00:00 2001 From: Mabelle Lin Date: Wed, 6 Mar 2019 12:02:40 +1300 Subject: [PATCH 21/44] Create and return list of annotation groups --- scaffoldmaker/meshtypes/meshtype_3d_colon1.py | 12 ++++++++---- scaffoldmaker/utils/tubemesh2.py | 4 +++- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/scaffoldmaker/meshtypes/meshtype_3d_colon1.py b/scaffoldmaker/meshtypes/meshtype_3d_colon1.py index 943f7868..920a217f 100644 --- a/scaffoldmaker/meshtypes/meshtype_3d_colon1.py +++ b/scaffoldmaker/meshtypes/meshtype_3d_colon1.py @@ -115,7 +115,7 @@ def generateBaseMesh(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: None + :return: annotationGroups """ elementsCountAround = options['Number of elements around'] elementsCountAlongHaustrum = options['Number of elements along haustrum'] @@ -155,15 +155,18 @@ def generateBaseMesh(region, options): haustraInnerRadiusFactor, haustrumSegmentEndDerivativeFactor, haustrumSegmentMidDerivativeFactor, wallThickness, haustraLength) # Generate tube mesh - nextNodeIdentifier, nextElementIdentifier = generatetubemesh(region, elementsCountAround, elementsCountAlongHaustrum, elementsCountThroughWall, haustraCount, + annotationGroups, nextNodeIdentifier, nextElementIdentifier = generatetubemesh(region, elementsCountAround, elementsCountAlongHaustrum, elementsCountThroughWall, haustraCount, cx, cd1, xHaustraOuter, d1HaustraOuter, d2HaustraOuter, wallThickness, haustraAxis, haustraLength, useCrossDerivatives, useCubicHermiteThroughWall) + return annotationGroups + @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']: cls.generateBaseMesh(region, options) @@ -174,10 +177,11 @@ def generateMesh(cls, region, options): refineElementsCountThroughWall = options['Refine number of elements through wall'] baseRegion = region.createRegion() - cls.generateBaseMesh(baseRegion, options) + baseAnnotationGroups = cls.generateBaseMesh(baseRegion, options) - meshrefinement = MeshRefinement(baseRegion, region) + meshrefinement = MeshRefinement(baseRegion, region, baseAnnotationGroups) meshrefinement.refineAllElementsCubeStandard3d(refineElementsCountAround, refineElementsCountAlong, refineElementsCountThroughWall) + return meshrefinement.getAnnotationGroups() def getUnitHaustraOuter(elementsCountAround, elementsCountAlongHaustrum, radius, cornerInnerRadiusFactor, haustraInnerRadiusFactor, haustrumSegmentEndDerivativeFactor, haustrumSegmentMidDerivativeFactor, wallThickness, haustraLength): diff --git a/scaffoldmaker/utils/tubemesh2.py b/scaffoldmaker/utils/tubemesh2.py index 28504ea4..f44f955a 100644 --- a/scaffoldmaker/utils/tubemesh2.py +++ b/scaffoldmaker/utils/tubemesh2.py @@ -48,8 +48,10 @@ def generatetubemesh(region, :param unitAxis: axis of unit profile :param unitLength: length of unit profile :param useCubicHermiteThroughWall: use linear when false + :return: annotationGroups, nextNodeIdentifier, nextElementIdentifier ''' zero = [0.0, 0.0, 0.0] + annotationGroups = [] elementsCountAlong = elementsCountAlongUnit*unitsCountAlong # Sample central line to get same number of elements as elementsCountAlong @@ -242,7 +244,7 @@ def generatetubemesh(region, fm.endChange() - return nodeIdentifier, elementIdentifier + return annotationGroups, nodeIdentifier, elementIdentifier def getInnerCoordinatesAndDerivativesFromOuter(xOuter, d1Outer, d3Outer, wallThickness, elementsCountAlong, elementsCountAround): """ From 50f33ac48d5df821075e72875f0a8d6bd650e740 Mon Sep 17 00:00:00 2001 From: Mabelle Lin Date: Wed, 6 Mar 2019 12:06:36 +1300 Subject: [PATCH 22/44] Remove @author from code --- scaffoldmaker/utils/tubemesh2.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/scaffoldmaker/utils/tubemesh2.py b/scaffoldmaker/utils/tubemesh2.py index f44f955a..9d0baf55 100644 --- a/scaffoldmaker/utils/tubemesh2.py +++ b/scaffoldmaker/utils/tubemesh2.py @@ -1,9 +1,6 @@ ''' Utility function for generating tubular mesh from a central line using a unit profile. -Created on 18 Feb, 2019 - -@author: Mabelle Lin ''' from __future__ import division import math From 795b5e3cc9d06f91a082503224597aa72b4057ea Mon Sep 17 00:00:00 2001 From: Mabelle Lin Date: Wed, 6 Mar 2019 15:15:31 +1300 Subject: [PATCH 23/44] Update terminologies for haustra segments --- scaffoldmaker/meshtypes/meshtype_3d_colon1.py | 85 ++++++++++--------- scaffoldmaker/utils/tubemesh2.py | 50 +++++------ 2 files changed, 68 insertions(+), 67 deletions(-) diff --git a/scaffoldmaker/meshtypes/meshtype_3d_colon1.py b/scaffoldmaker/meshtypes/meshtype_3d_colon1.py index 920a217f..0aa1c58b 100644 --- a/scaffoldmaker/meshtypes/meshtype_3d_colon1.py +++ b/scaffoldmaker/meshtypes/meshtype_3d_colon1.py @@ -12,8 +12,9 @@ class MeshType_3d_colon1(Scaffold_base): ''' Generates a 3-D colon mesh with variable numbers of elements around, along the central line, and through wall. - The colon is created by a function that generates haustra units - and uses tubemesh to map the units along a central line profile. + The colon is created by a function that generates a haustra + segment and uses tubemesh to map the segment along a central + line profile. ''' @staticmethod def getName(): @@ -32,7 +33,7 @@ def getDefaultOptions(parameterSetName='Default'): 'Number of elements around': 15, 'Number of elements along haustrum': 4, 'Number of elements through wall': 1, - 'Number of haustra': 30, + 'Number of haustra segments': 30, 'Inner radius': 1.0, 'Corner inner radius factor': 0.5, 'Haustra inner radius factor': 0.5, @@ -51,7 +52,7 @@ def getDefaultOptions(parameterSetName='Default'): options['Tube type'] = 3 elif 'Section 1' in parameterSetName: options['Tube type'] = 1 - options['Number of haustra'] = 3 + options['Number of haustra segments'] = 3 return options @staticmethod @@ -60,7 +61,7 @@ def getOrderedOptionNames(): 'Number of elements around', 'Number of elements along haustrum', 'Number of elements through wall', - 'Number of haustra', + 'Number of haustra segments', 'Inner radius', 'Corner inner radius factor', 'Haustra inner radius factor', @@ -79,7 +80,7 @@ def getOrderedOptionNames(): def checkOptions(options): for key in [ 'Number of elements through wall', - 'Number of haustra', + 'Number of haustra segments', 'Refine number of elements around', 'Refine number of elements along', 'Refine number of elements through wall']: @@ -120,7 +121,7 @@ def generateBaseMesh(region, options): elementsCountAround = options['Number of elements around'] elementsCountAlongHaustrum = options['Number of elements along haustrum'] elementsCountThroughWall = options['Number of elements through wall'] - haustraCount = options['Number of haustra'] + haustraSegmentCount = options['Number of haustra segments'] radius = options['Inner radius'] cornerInnerRadiusFactor = options['Corner inner radius factor'] haustraInnerRadiusFactor = options['Haustra inner radius factor'] @@ -130,7 +131,7 @@ def generateBaseMesh(region, options): tubeType = options['Tube type'] useCrossDerivatives = options['Use cross derivatives'] useCubicHermiteThroughWall = not(options['Use linear through wall']) - elementsCountAlong = int(elementsCountAlongHaustrum*haustraCount) + elementsCountAlong = int(elementsCountAlongHaustrum*haustraSegmentCount) if tubeType == 1: # Straight tube cx = [[-4.0, 1.0, 3.0], [ 1.0, 2.0, 0.0 ] ] @@ -148,15 +149,15 @@ def generateBaseMesh(region, options): for e in range(elementsCountIn): arcLength = computeCubicHermiteArcLength(cx[e], cd1[e], cx[e + 1], cd1[e + 1], rescaleDerivatives = True) length += arcLength - haustraLength = length / haustraCount + haustraSegmentLength = length / haustraSegmentCount - # Generate outer surface of a haustra unit - xHaustraOuter, d1HaustraOuter, d2HaustraOuter, haustraAxis = getUnitHaustraOuter(elementsCountAround, elementsCountAlongHaustrum, radius, cornerInnerRadiusFactor, - haustraInnerRadiusFactor, haustrumSegmentEndDerivativeFactor, haustrumSegmentMidDerivativeFactor, wallThickness, haustraLength) + # Generate outer surface of a haustra segment + xHaustraOuter, d1HaustraOuter, d2HaustraOuter, haustraSegmentAxis = getColonHaustraSegmentOuterPoints(elementsCountAround, elementsCountAlongHaustrum, radius, cornerInnerRadiusFactor, + haustraInnerRadiusFactor, haustrumSegmentEndDerivativeFactor, haustrumSegmentMidDerivativeFactor, wallThickness, haustraSegmentLength) # Generate tube mesh - annotationGroups, nextNodeIdentifier, nextElementIdentifier = generatetubemesh(region, elementsCountAround, elementsCountAlongHaustrum, elementsCountThroughWall, haustraCount, - cx, cd1, xHaustraOuter, d1HaustraOuter, d2HaustraOuter, wallThickness, haustraAxis, haustraLength, useCrossDerivatives, useCubicHermiteThroughWall) + annotationGroups, nextNodeIdentifier, nextElementIdentifier = generatetubemesh(region, elementsCountAround, elementsCountAlongHaustrum, elementsCountThroughWall, haustraSegmentCount, + cx, cd1, xHaustraOuter, d1HaustraOuter, d2HaustraOuter, wallThickness, haustraSegmentAxis, haustraSegmentLength, useCrossDerivatives, useCubicHermiteThroughWall) return annotationGroups @@ -183,13 +184,14 @@ def generateMesh(cls, region, options): meshrefinement.refineAllElementsCubeStandard3d(refineElementsCountAround, refineElementsCountAlong, refineElementsCountThroughWall) return meshrefinement.getAnnotationGroups() -def getUnitHaustraOuter(elementsCountAround, elementsCountAlongHaustrum, radius, cornerInnerRadiusFactor, - haustraInnerRadiusFactor, haustrumSegmentEndDerivativeFactor, haustrumSegmentMidDerivativeFactor, wallThickness, haustraLength): +def getColonHaustraSegmentOuterPoints(elementsCountAround, elementsCountAlongHaustrum, radius, cornerInnerRadiusFactor, + haustraInnerRadiusFactor, haustrumSegmentEndDerivativeFactor, haustrumSegmentMidDerivativeFactor, wallThickness, haustraSegmentLength): """ - Generates a 3-D haustra unit mesh with variable numbers + Generates a 3-D haustra segment mesh with variable numbers of elements around, along the central line, and through wall. - The haustra has a triangular profile with rounded corners at the - interhaustral septa, and a clover profile outside the septa. + The haustra segment has a triangular profile with rounded corners + at the inter-haustral septa, and a clover profile in the intra-haustral + region. :param elementsCountAround: Number of elements around haustra. :param elementsCountAlongHaustrum: Number of elements along haustrum. :param radius: Inner radius defined from center of triangular @@ -199,15 +201,14 @@ def getUnitHaustraOuter(elementsCountAround, elementsCountAlongHaustrum, radius, to get a radius of curvature at the corners. :param haustraInnerRadiusFactor: Factor is multiplied by inner radius to obtain radius of intersecting circles in the middle cross-section - along a haustrum. + along a haustra segment. :param haustrumSegmentEndDerivativeFactor: Factor is multiplied by haustra - length to scale derivative along the haustra at the interhaustral septa end. + length to scale derivative along the end of a haustra segment length. :param haustrumSegmentMidDerivativeFactor: Factor is multiplied by haustra - length to scale derivative along the haustra at the middle section of the - haustra. + length to scale derivative along the mid length of the haustra segment. :param wallThickness: Thickness of haustra through wall. - :param haustraLength: Length of a haustra unit. - :return: coordinates, derivatives on outer surface of haustra unit. + :param haustraSegmentLength: Length of a haustra segment. + :return: coordinates, derivatives on outer surface of haustra segment. """ # create nodes @@ -300,22 +301,22 @@ def getUnitHaustraOuter(elementsCountAround, elementsCountAlongHaustrum, radius, d1InnerHaustraRaw.append(dx_ds1) d1InnerHaustra = smoothCubicHermiteDerivativesLoop(xHaustraInner, d1InnerHaustraRaw) - # Sample arclength of haustra + # Sample arclength of haustra segment for n1 in range(elementsCountAround): if n1%(elementsCountAround/3) > 0.0: v1 = [xInner[n1][0], xInner[n1][1], 0.0] - startArcLength = haustrumSegmentEndDerivativeFactor * haustraLength + startArcLength = haustrumSegmentEndDerivativeFactor * haustraSegmentLength d1 = [ c*startArcLength for c in unitZ] - v2 = [xHaustraInner[n1][0], xHaustraInner[n1][1], haustraLength/2] - midArcLength = haustrumSegmentMidDerivativeFactor * haustraLength + v2 = [xHaustraInner[n1][0], xHaustraInner[n1][1], haustraSegmentLength/2] + midArcLength = haustrumSegmentMidDerivativeFactor * haustraSegmentLength d2 = [ c*midArcLength for c in unitZ] - v3 = [xInner[n1][0], xInner[n1][1], haustraLength] + v3 = [xInner[n1][0], xInner[n1][1], haustraSegmentLength] d3 = [ c*startArcLength for c in unitZ] else: v1 = [xInner[n1][0], xInner[n1][1], 0.0] - v2 = [xInner[n1][0], xInner[n1][1], haustraLength/2] - v3 = [xInner[n1][0], xInner[n1][1], haustraLength] - d3 = d2 = d1 = [c* haustraLength/3 for c in unitZ] + v2 = [xInner[n1][0], xInner[n1][1], haustraSegmentLength/2] + v3 = [xInner[n1][0], xInner[n1][1], haustraSegmentLength] + d3 = d2 = d1 = [c* haustraSegmentLength/3 for c in unitZ] nx = [v1, v2, v3] nd1 = [d1, d2, d3] sx, sd1, se, sxi, _ = sampleCubicHermiteCurves(nx, nd1, elementsCountAlongHaustrum) @@ -333,12 +334,12 @@ def getUnitHaustraOuter(elementsCountAround, elementsCountAlongHaustrum, radius, dx_ds2 = dx_ds2InnerRaw[n1][n2] dx_ds2InnerList.append(dx_ds2) unitTangent = normalise(dx_ds2) - # Interhaustra + # Inter-haustra if n2 == 0 or n2 > elementsCountAlongHaustrum - 1: dx_ds1 = d1Inner[n1] unitdx_ds3 = crossproduct3(normalise(dx_ds1), unitTangent) else: - # Haustra + # Intra-Haustra if elementsCountAlongHaustrum == 2: unitdx_ds1 = normalise(d1InnerHaustra[n1]) else: @@ -346,9 +347,9 @@ def getUnitHaustraOuter(elementsCountAround, elementsCountAlongHaustrum, radius, unitdx_ds1 = normalise(d1InnerHaustra[n1]) else: # points on clover if elementsCountAlongHaustrum > 3: - if n2 < int(elementsCountAlongHaustrum/2): # first half of haustraLength + if n2 < int(elementsCountAlongHaustrum/2): # first half of haustraSegmentLength axisRot = crossproduct3(unitZ, unitTangent) - elif n2 > int(elementsCountAlongHaustrum/2): # second half of haustraLength + elif n2 > int(elementsCountAlongHaustrum/2): # second half of haustraSegmentLength axisRot = crossproduct3(unitTangent, unitZ) elif elementsCountAlongHaustrum == 3: # 3 elementsAlongHaustrum axisRot = crossproduct3(unitTangent, unitZ) @@ -391,15 +392,15 @@ def getOuterCoordinatesAndDerivativesFromInner(xInner, d1Inner, d2Inner, d3Inner coordinates and derivatives of inner surface using wall thickness and normals. param xInner: Coordinates on inner surface - param d1Inner: Derivatives on inner surface around haustra - param d2Inner: Derivatives on inner surface along haustra + param d1Inner: Derivatives on inner surface around haustra segment + param d2Inner: Derivatives on inner surface along haustra segment param d3Inner: Derivatives on inner surface through wall param thickness: Thickness of wall param elementsCountAlongHaustrum: Number of elements along haustrum - param elementsCountAround: Number of elements around haustra + param elementsCountAround: Number of elements around haustra segment return xOuter: Coordinates on outer surface - return nd1Outer: Derivatives on outer surface around haustra - return nd2Outer: Derivatives on outer surface along haustra + return nd1Outer: Derivatives on outer surface around haustra segment + return nd2Outer: Derivatives on outer surface along haustra segment """ xOuter = [] nd1Outer = [] diff --git a/scaffoldmaker/utils/tubemesh2.py b/scaffoldmaker/utils/tubemesh2.py index 9d0baf55..4f7358e9 100644 --- a/scaffoldmaker/utils/tubemesh2.py +++ b/scaffoldmaker/utils/tubemesh2.py @@ -1,6 +1,6 @@ ''' Utility function for generating tubular mesh from a central line -using a unit profile. +using a segment profile. ''' from __future__ import division import math @@ -17,12 +17,12 @@ def generatetubemesh(region, elementsCountAround, - elementsCountAlongUnit, + elementsCountAlongSegment, elementsCountThroughWall, - unitsCountAlong, + segmentCountAlong, cx, cd1, xOuter, d1Outer, d2Outer, wallThickness, - unitAxis, unitLength, + segmentAxis, segmentLength, useCrossDerivatives, useCubicHermiteThroughWall, # or Zinc Elementbasis.FUNCTION_TYPE_LINEAR_LAGRANGE etc. nextNodeIdentifier = 1, nextElementIdentifier = 1 @@ -30,26 +30,26 @@ def generatetubemesh(region, ''' Generates a 3-D tubular mesh with variable numbers of elements around, along the central axis, and radially through wall. The - tubular mesh is created from a unit profile which is mapped onto + tubular mesh is created from a segment profile which is mapped onto the central line and lateral axes data :param elementsCountAround: number of elements around tube - :param elementsCountAlongUnit: number of elements along unit profile + :param elementsCountAlongSegment: number of elements along segment profile :param elementsCountThroughWall: number of elements through wall thickness - :param unitsCountAlong: number of units along the tube + :param segmentCountAlong: number of segments along the tube :param cx: coordinates on central line :param cd1: derivative along central line - :param xOuter: coordinates on outer surface of unit profile - :param d1Outer: derivatives around outer surface of unit profile - :param d2Outer: derivatives along outer surface of unit profile + :param xOuter: coordinates on outer surface of segment profile + :param d1Outer: derivatives around outer surface of segment profile + :param d2Outer: derivatives along outer surface of segment profile :param wallThickness: thickness of wall - :param unitAxis: axis of unit profile - :param unitLength: length of unit profile + :param segmentAxis: axis of segment profile + :param segmentLength: length of segment profile :param useCubicHermiteThroughWall: use linear when false :return: annotationGroups, nextNodeIdentifier, nextElementIdentifier ''' zero = [0.0, 0.0, 0.0] annotationGroups = [] - elementsCountAlong = elementsCountAlongUnit*unitsCountAlong + elementsCountAlong = elementsCountAlongSegment*segmentCountAlong # Sample central line to get same number of elements as elementsCountAlong sx, sd1, se, sxi, _ = sampleCubicHermiteCurves(cx, cd1, elementsCountAlong) @@ -136,26 +136,26 @@ def generatetubemesh(region, dx_ds2List = [] dx_ds3List = [] - # Map each face along unit profile to central line - for nUnit in range(unitsCountAlong): - for nAlongUnit in range(elementsCountAlongUnit+1): - n2 = nUnit*elementsCountAlongUnit + nAlongUnit - if nUnit == 0 or (nUnit > 0 and nAlongUnit > 0): - # Rotate to align unit axis with tangent of central line - unitMid = [0.0, 0.0, unitLength/elementsCountAlongUnit* nAlongUnit] + # Map each face along segment profile to central line + for nSegment in range(segmentCountAlong): + for nAlongSegment in range(elementsCountAlongSegment+1): + n2 = nSegment*elementsCountAlongSegment + nAlongSegment + if nSegment == 0 or (nSegment > 0 and nAlongSegment > 0): + # Rotate to align segment axis with tangent of central line + segmentMid = [0.0, 0.0, segmentLength/elementsCountAlongSegment* nAlongSegment] unitTangent = normalise(sd1[n2]) - cp = crossproduct3(unitAxis, unitTangent) + cp = crossproduct3(segmentAxis, unitTangent) if magnitude(cp)> 0.0: axisRot = normalise(cp) - thetaRot = math.acos(dotproduct(unitAxis, unitTangent)) + thetaRot = math.acos(dotproduct(segmentAxis, unitTangent)) rotFrame = getRotationMatrixFromAxisAngle(axisRot, thetaRot) - midRot = [rotFrame[j][0]*unitMid[0] + rotFrame[j][1]*unitMid[1] + rotFrame[j][2]*unitMid[2] for j in range(3)] + midRot = [rotFrame[j][0]*segmentMid[0] + rotFrame[j][1]*segmentMid[1] + rotFrame[j][2]*segmentMid[2] for j in range(3)] translateMatrix = [sx[n2][j] - midRot[j] for j in range(3)] else: - midRot = unitMid + midRot = segmentMid for n1 in range(elementsCountAround): - n = nAlongUnit*elementsCountAround + n1 + n = nAlongSegment*elementsCountAround + n1 x = xOuter[n] d1 = d1Outer[n] d2 = d2Outer[n] From bc1c7a8a4ba992fabdf7372ec9b16ac0595af159 Mon Sep 17 00:00:00 2001 From: Mabelle Lin Date: Wed, 6 Mar 2019 15:31:17 +1300 Subject: [PATCH 24/44] Support multiple parameter sets, add scaffold base class --- scaffoldmaker/meshtypes/meshtype_3d_haustra1.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/scaffoldmaker/meshtypes/meshtype_3d_haustra1.py b/scaffoldmaker/meshtypes/meshtype_3d_haustra1.py index 0009d6ac..67f28bda 100644 --- a/scaffoldmaker/meshtypes/meshtype_3d_haustra1.py +++ b/scaffoldmaker/meshtypes/meshtype_3d_haustra1.py @@ -3,11 +3,11 @@ numbers of elements around, along and through wall, with variable radius and thickness along. """ - +from scaffoldmaker.meshtypes.scaffold_base import Scaffold_base from scaffoldmaker.utils.meshrefinement import MeshRefinement from scaffoldmaker.utils.tubemesh import * -class MeshType_3d_haustra1(object): +class MeshType_3d_haustra1(Scaffold_base): ''' Generates a 3-D haustra mesh with variable numbers of elements around, along the central line, and through wall. @@ -19,7 +19,7 @@ def getName(): return '3D Haustra 1' @staticmethod - def getDefaultOptions(): + def getDefaultOptions(parameterSetName='Default'): return { 'Number of elements around' : 9, 'Number of elements along haustra' : 3, From 6d611f2ed53b19b5d4d29f675517e5a0168c7a38 Mon Sep 17 00:00:00 2001 From: Mabelle Lin Date: Wed, 6 Mar 2019 15:36:42 +1300 Subject: [PATCH 25/44] Change default number of haustra segments --- scaffoldmaker/meshtypes/meshtype_3d_haustra1.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scaffoldmaker/meshtypes/meshtype_3d_haustra1.py b/scaffoldmaker/meshtypes/meshtype_3d_haustra1.py index 67f28bda..bb913f2d 100644 --- a/scaffoldmaker/meshtypes/meshtype_3d_haustra1.py +++ b/scaffoldmaker/meshtypes/meshtype_3d_haustra1.py @@ -27,7 +27,7 @@ def getDefaultOptions(parameterSetName='Default'): 'Inner radius': 0.5, 'Corner radius fraction': 0.5, 'Wall thickness': 0.01, - 'Number of haustra': 2, + 'Number of haustra': 1, 'Haustra radius fraction': 0.5, 'Haustra length': 1.0, 'Interhaustra fold factor': 0.5, From 3be4634542344c859c8770b536d16aac23931a70 Mon Sep 17 00:00:00 2001 From: Mabelle Lin Date: Wed, 6 Mar 2019 15:52:22 +1300 Subject: [PATCH 26/44] Check option for number of elements along haustrum --- scaffoldmaker/meshtypes/meshtype_3d_colon1.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/scaffoldmaker/meshtypes/meshtype_3d_colon1.py b/scaffoldmaker/meshtypes/meshtype_3d_colon1.py index 0aa1c58b..77c29e91 100644 --- a/scaffoldmaker/meshtypes/meshtype_3d_colon1.py +++ b/scaffoldmaker/meshtypes/meshtype_3d_colon1.py @@ -90,6 +90,8 @@ def checkOptions(options): options['Number of elements around'] = 9 if (options['Number of elements around'] % 3 != 0) : options['Number of elements around'] = (options['Number of elements around']//3)*3 + if (options['Number of elements along haustrum'] < 2) : + options['Number of elements along haustrum'] = 2 for key in [ 'Inner radius', 'Haustra inner radius factor', From e497a69eb1e4b3fecceb3d6dc0fc141ca2edf44b Mon Sep 17 00:00:00 2001 From: Mabelle Lin Date: Wed, 6 Mar 2019 15:57:19 +1300 Subject: [PATCH 27/44] Update option name for refine number of elements along haustrum --- scaffoldmaker/meshtypes/meshtype_3d_colon1.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/scaffoldmaker/meshtypes/meshtype_3d_colon1.py b/scaffoldmaker/meshtypes/meshtype_3d_colon1.py index 77c29e91..5afd3a15 100644 --- a/scaffoldmaker/meshtypes/meshtype_3d_colon1.py +++ b/scaffoldmaker/meshtypes/meshtype_3d_colon1.py @@ -45,7 +45,7 @@ def getDefaultOptions(parameterSetName='Default'): 'Use linear through wall': False, 'Refine': False, 'Refine number of elements around': 1, - 'Refine number of elements along': 1, + 'Refine number of elements along haustrum': 1, 'Refine number of elements through wall': 1 } if 'Human 1' in parameterSetName: @@ -73,7 +73,7 @@ def getOrderedOptionNames(): 'Use linear through wall', 'Refine', 'Refine number of elements around', - 'Refine number of elements along', + 'Refine number of elements along haustrum', 'Refine number of elements through wall' ] @@ -82,7 +82,7 @@ def checkOptions(options): 'Number of elements through wall', 'Number of haustra segments', 'Refine number of elements around', - 'Refine number of elements along', + 'Refine number of elements along haustrum', 'Refine number of elements through wall']: if options[key] < 1: options[key] = 1 @@ -176,7 +176,7 @@ def generateMesh(cls, region, options): return refineElementsCountAround = options['Refine number of elements around'] - refineElementsCountAlong = options['Refine number of elements along'] + refineElementsCountAlong = options['Refine number of elements along haustrum'] refineElementsCountThroughWall = options['Refine number of elements through wall'] baseRegion = region.createRegion() From c5a10a022c5430b5c3c5f5740db93a247bddd2c7 Mon Sep 17 00:00:00 2001 From: Mabelle Lin Date: Thu, 7 Mar 2019 12:34:54 +1300 Subject: [PATCH 28/44] Generate mesh from inner surface points --- scaffoldmaker/meshtypes/meshtype_3d_colon1.py | 74 +----------- scaffoldmaker/utils/tubemesh2.py | 114 +++++++++--------- 2 files changed, 64 insertions(+), 124 deletions(-) diff --git a/scaffoldmaker/meshtypes/meshtype_3d_colon1.py b/scaffoldmaker/meshtypes/meshtype_3d_colon1.py index 5afd3a15..77cdd898 100644 --- a/scaffoldmaker/meshtypes/meshtype_3d_colon1.py +++ b/scaffoldmaker/meshtypes/meshtype_3d_colon1.py @@ -154,12 +154,12 @@ def generateBaseMesh(region, options): haustraSegmentLength = length / haustraSegmentCount # Generate outer surface of a haustra segment - xHaustraOuter, d1HaustraOuter, d2HaustraOuter, haustraSegmentAxis = getColonHaustraSegmentOuterPoints(elementsCountAround, elementsCountAlongHaustrum, radius, cornerInnerRadiusFactor, + xHaustraInner, d1HaustraInner, d2HaustraInner, haustraSegmentAxis = getColonHaustraSegmentInnerPoints(elementsCountAround, elementsCountAlongHaustrum, radius, cornerInnerRadiusFactor, haustraInnerRadiusFactor, haustrumSegmentEndDerivativeFactor, haustrumSegmentMidDerivativeFactor, wallThickness, haustraSegmentLength) # Generate tube mesh annotationGroups, nextNodeIdentifier, nextElementIdentifier = generatetubemesh(region, elementsCountAround, elementsCountAlongHaustrum, elementsCountThroughWall, haustraSegmentCount, - cx, cd1, xHaustraOuter, d1HaustraOuter, d2HaustraOuter, wallThickness, haustraSegmentAxis, haustraSegmentLength, useCrossDerivatives, useCubicHermiteThroughWall) + cx, cd1, xHaustraInner, d1HaustraInner, d2HaustraInner, wallThickness, haustraSegmentAxis, haustraSegmentLength, useCrossDerivatives, useCubicHermiteThroughWall) return annotationGroups @@ -186,7 +186,7 @@ def generateMesh(cls, region, options): meshrefinement.refineAllElementsCubeStandard3d(refineElementsCountAround, refineElementsCountAlong, refineElementsCountThroughWall) return meshrefinement.getAnnotationGroups() -def getColonHaustraSegmentOuterPoints(elementsCountAround, elementsCountAlongHaustrum, radius, cornerInnerRadiusFactor, +def getColonHaustraSegmentInnerPoints(elementsCountAround, elementsCountAlongHaustrum, radius, cornerInnerRadiusFactor, haustraInnerRadiusFactor, haustrumSegmentEndDerivativeFactor, haustrumSegmentMidDerivativeFactor, wallThickness, haustraSegmentLength): """ Generates a 3-D haustra segment mesh with variable numbers @@ -210,7 +210,7 @@ def getColonHaustraSegmentOuterPoints(elementsCountAround, elementsCountAlongHau length to scale derivative along the mid length of the haustra segment. :param wallThickness: Thickness of haustra through wall. :param haustraSegmentLength: Length of a haustra segment. - :return: coordinates, derivatives on outer surface of haustra segment. + :return: coordinates, derivatives on inner surface of haustra segment. """ # create nodes @@ -233,7 +233,6 @@ def getColonHaustraSegmentOuterPoints(elementsCountAround, elementsCountAlongHau dx_ds2InnerRaw = [] xInnerList = [] dx_ds2InnerList = [] - dx_ds3InnerUnitList = [] # Pre-calculate node locations and derivatives on inner triangle for n1 in range(3): @@ -378,67 +377,4 @@ def getColonHaustraSegmentOuterPoints(elementsCountAround, elementsCountAlongHau dx_ds1InnerList = dx_ds1InnerList + d1Inner - for n in range(len(xInnerList)): - dx_ds3 = crossproduct3(normalise(dx_ds1InnerList[n]), normalise(dx_ds2InnerList[n])) - unitdx_ds3 = normalise(dx_ds3) - dx_ds3InnerUnitList.append(unitdx_ds3) - - # Pre-calculate node locations and derivatives on outer boundary - xOuter, d1Outer, d2Outer = getOuterCoordinatesAndDerivativesFromInner(xInnerList, dx_ds1InnerList, dx_ds2InnerList, dx_ds3InnerUnitList, wallThickness, elementsCountAlongHaustrum, elementsCountAround) - - return xOuter, d1Outer, d2Outer, unitZ - -def getOuterCoordinatesAndDerivativesFromInner(xInner, d1Inner, d2Inner, d3Inner, wallThickness, elementsCountAlongHaustrum, elementsCountAround): - """ - Generates coordinates and derivatives of outer surface from - coordinates and derivatives of inner surface using wall thickness - and normals. - param xInner: Coordinates on inner surface - param d1Inner: Derivatives on inner surface around haustra segment - param d2Inner: Derivatives on inner surface along haustra segment - param d3Inner: Derivatives on inner surface through wall - param thickness: Thickness of wall - param elementsCountAlongHaustrum: Number of elements along haustrum - param elementsCountAround: Number of elements around haustra segment - return xOuter: Coordinates on outer surface - return nd1Outer: Derivatives on outer surface around haustra segment - return nd2Outer: Derivatives on outer surface along haustra segment - """ - xOuter = [] - nd1Outer = [] - nd2Outer = [] - - for n2 in range(elementsCountAlongHaustrum + 1): - for n1 in range(elementsCountAround): - n = n2*elementsCountAround + n1 - x = [xInner[n][i] + d3Inner[n][i]*wallThickness for i in range(3)] - norm = d3Inner[n] - # d1 - prevIdx = n-1 if (n1 != 0) else (n2+1)*elementsCountAround - 1 - nextIdx = n+1 if (n1 < elementsCountAround-1) else n2*elementsCountAround - curvatureAround = 0.5*( - getCubicHermiteCurvature(xInner[prevIdx], d1Inner[prevIdx], xInner[n], d1Inner[n], norm, 1.0) + - getCubicHermiteCurvature(xInner[n], d1Inner[n], xInner[nextIdx], d1Inner[nextIdx], norm, 0.0)) - factor = 1.0 - curvatureAround*wallThickness - nd1 = [ factor*c for c in d1Inner[n]] - # d2 - if n2 > 0 and n2 < elementsCountAlongHaustrum: - prevIdx = (n2-1)*elementsCountAround + n1 - nextIdx = (n2+1)*elementsCountAround + n1 - elif n2 == 0: - prevIdx = (elementsCountAlongHaustrum-1)*elementsCountAround + n1 - nextIdx = (n2+1)*elementsCountAround + n1 - else: - prevIdx = (n2-1)*elementsCountAround + n1 - nextIdx = elementsCountAround + n1 - curvatureAlong = 0.5*( - getCubicHermiteCurvature(xInner[prevIdx], d2Inner[prevIdx], xInner[n], d2Inner[n], norm, 1.0)+ - getCubicHermiteCurvature(xInner[n], d2Inner[n], xInner[nextIdx], d2Inner[nextIdx], norm, 0.0)) - factor = 1.0 - curvatureAlong*wallThickness - nd2 = [ factor*c for c in d2Inner[n]] - - xOuter.append(x) - nd1Outer.append(nd1) - nd2Outer.append(nd2) - - return xOuter, nd1Outer, nd2Outer + return xInnerList, dx_ds1InnerList, dx_ds2InnerList, unitZ diff --git a/scaffoldmaker/utils/tubemesh2.py b/scaffoldmaker/utils/tubemesh2.py index 4f7358e9..2a17963f 100644 --- a/scaffoldmaker/utils/tubemesh2.py +++ b/scaffoldmaker/utils/tubemesh2.py @@ -21,7 +21,7 @@ def generatetubemesh(region, elementsCountThroughWall, segmentCountAlong, cx, cd1, - xOuter, d1Outer, d2Outer, wallThickness, + xInner, d1Inner, d2Inner, wallThickness, segmentAxis, segmentLength, useCrossDerivatives, useCubicHermiteThroughWall, # or Zinc Elementbasis.FUNCTION_TYPE_LINEAR_LAGRANGE etc. @@ -38,9 +38,9 @@ def generatetubemesh(region, :param segmentCountAlong: number of segments along the tube :param cx: coordinates on central line :param cd1: derivative along central line - :param xOuter: coordinates on outer surface of segment profile - :param d1Outer: derivatives around outer surface of segment profile - :param d2Outer: derivatives along outer surface of segment profile + :param xInner: coordinates on inner surface of segment profile + :param d1Inner: derivatives around inner surface of segment profile + :param d2Inner: derivatives along inner surface of segment profile :param wallThickness: thickness of wall :param segmentAxis: axis of segment profile :param segmentLength: length of segment profile @@ -127,10 +127,10 @@ def generatetubemesh(region, dx_ds1 = [ 0.0, 0.0, 0.0 ] dx_ds2 = [ 0.0, 0.0, 0.0 ] dx_ds3 = [ 0.0, 0.0, 0.0 ] - xOuterList = [] - d1OuterList = [] - d2OuterList = [] - d3OuterUnitList = [] + xInnerList = [] + d1InnerList = [] + d2InnerList = [] + d3InnerUnitList = [] xList = [] dx_ds1List = [] dx_ds2List = [] @@ -156,9 +156,9 @@ def generatetubemesh(region, for n1 in range(elementsCountAround): n = nAlongSegment*elementsCountAround + n1 - x = xOuter[n] - d1 = d1Outer[n] - d2 = d2Outer[n] + x = xInner[n] + d1 = d1Inner[n] + d2 = d2Inner[n] if magnitude(cp)> 0.0: xRot1 = [rotFrame[j][0]*x[0] + rotFrame[j][1]*x[1] + rotFrame[j][2]*x[2] for j in range(3)] d1Rot1 = [rotFrame[j][0]*d1[0] + rotFrame[j][1]*d1[1] + rotFrame[j][2]*d1[2] for j in range(3)] @@ -167,10 +167,14 @@ def generatetubemesh(region, if n1 == 0: firstVector = normalise([xRot1[j] - midRot[j] for j in range(3)]) thetaRot2 = math.acos(dotproduct(normalise(sBinormal[n2]), firstVector)) - cp2 = normalise(crossproduct3(normalise(sBinormal[n2]), firstVector)) - signThetaRot2 = dotproduct(unitTangent, cp2) - axisRot2 = unitTangent - rotFrame2 = getRotationMatrixFromAxisAngle(axisRot2, -signThetaRot2*thetaRot2) + cp2 = crossproduct3(normalise(sBinormal[n2]), firstVector) + if magnitude(cp2) > 0.0: + cp2 = normalise(cp2) + signThetaRot2 = dotproduct(unitTangent, cp2) + axisRot2 = unitTangent + rotFrame2 = getRotationMatrixFromAxisAngle(axisRot2, -signThetaRot2*thetaRot2) + else: + rotFrame2 = [ [1, 0, 0], [0, 1, 0], [0, 0, 1]] xRot2 = [rotFrame2[j][0]*xRot1[0] + rotFrame2[j][1]*xRot1[1] + rotFrame2[j][2]*xRot1[2] for j in range(3)] d1Rot2 = [rotFrame2[j][0]*d1Rot1[0] + rotFrame2[j][1]*d1Rot1[1] + rotFrame2[j][2]*d1Rot1[2] for j in range(3)] d2Rot2 = [rotFrame2[j][0]*d2Rot1[0] + rotFrame2[j][1]*d2Rot1[1] + rotFrame2[j][2]*d2Rot1[2] for j in range(3)] @@ -181,19 +185,19 @@ def generatetubemesh(region, xTranslate = [xRot2[j] + translateMatrix[j] for j in range(3)] - xOuterList.append(xTranslate) - d1OuterList.append(d1Rot2) - d2OuterList.append(d2Rot2) - d3Unit = normalise(crossproduct3(normalise(d2Rot2), normalise(d1Rot2))) - d3OuterUnitList.append(d3Unit) + xInnerList.append(xTranslate) + d1InnerList.append(d1Rot2) + d2InnerList.append(d2Rot2) + d3Unit = normalise(crossproduct3(normalise(d1Rot2), normalise(d2Rot2))) + d3InnerUnitList.append(d3Unit) # Pre-calculate node locations and derivatives on outer boundary - xInnerList, d1Inner, curvatureOuter = getInnerCoordinatesAndDerivativesFromOuter(xOuterList, d1OuterList, d3OuterUnitList, wallThickness, elementsCountAlong, elementsCountAround) + xOuterList, d1Outer, curvatureInner = getOuterCoordinatesAndDerivativesFromInner(xInnerList, d1InnerList, d3InnerUnitList, wallThickness, elementsCountAlong, elementsCountAround) # Interpolate to get nodes through wall for n3 in range(elementsCountThroughWall + 1): xi3 = 1/elementsCountThroughWall * n3 - x, dx_ds1, dx_ds2, dx_ds3 = interpolatefromInnerAndOuter( xInnerList, xOuterList, wallThickness, xi3, curvatureOuter, d1OuterList, d2OuterList, d3OuterUnitList, elementsCountAround, elementsCountAlong, elementsCountThroughWall) + x, dx_ds1, dx_ds2, dx_ds3 = interpolatefromInnerAndOuter( xInnerList, xOuterList, wallThickness, xi3, curvatureInner, d1InnerList, d2InnerList, d3InnerUnitList, elementsCountAround, elementsCountAlong, elementsCountThroughWall) xList = xList + x dx_ds1List = dx_ds1List + dx_ds1 dx_ds2List = dx_ds2List + dx_ds2 @@ -243,44 +247,44 @@ def generatetubemesh(region, return annotationGroups, nodeIdentifier, elementIdentifier -def getInnerCoordinatesAndDerivativesFromOuter(xOuter, d1Outer, d3Outer, wallThickness, elementsCountAlong, elementsCountAround): +def getOuterCoordinatesAndDerivativesFromInner(xInner, d1Inner, d3Inner, wallThickness, elementsCountAlong, elementsCountAround): """ - Generates coordinates and derivatives of inner surface from - coordinates and derivatives of outer surface using wall thickness + Generates coordinates and derivatives of outer surface from + coordinates and derivatives of inner surface using wall thickness and normals. - param xOuter: Coordinates on outer surface - param d1Outer: Derivatives on outer surface around tube - param d3Outer: Derivatives on outer surface through wall + param xInner: Coordinates on inner surface + param d1Inner: Derivatives on inner surface around tube + param d3Inner: Derivatives on inner surface through wall param wallThickness: Thickness of wall param elementsCountAlong: Number of elements along tube param elementsCountAround: Number of elements around tube - return xInner: Coordinates on inner surface - return nd1: Derivatives on inner surface around tube - return curvatureOuter: Curvature of coordinates on outer surface + return xOuter: Coordinates on outer surface + return nd1: Derivatives on outer surface around tube + return curvatureInner: Curvature of coordinates on inner surface """ - xInner = [] + xOuter = [] nd1 = [] dWall = [] - curvatureOuter = [] + curvatureInner = [] for n2 in range(elementsCountAlong + 1): for n1 in range(elementsCountAround): n = n2*elementsCountAround + n1 - x = [xOuter[n][i] + d3Outer[n][i]*wallThickness for i in range(3)] + x = [xInner[n][i] + d3Inner[n][i]*wallThickness for i in range(3)] prevIdx = n-1 if (n1 != 0) else (n2+1)*elementsCountAround - 1 nextIdx = n+1 if (n1 < elementsCountAround-1) else n2*elementsCountAround - norm = [-1.0*c for c in d3Outer[n]] + norm = d3Inner[n] curvatureAround = 0.5*( - getCubicHermiteCurvature(xOuter[prevIdx], d1Outer[prevIdx], xOuter[n], d1Outer[n], norm, 1.0) + - getCubicHermiteCurvature(xOuter[n], d1Outer[n], xOuter[nextIdx], d1Outer[nextIdx], norm, 0.0)) + getCubicHermiteCurvature(xInner[prevIdx], d1Inner[prevIdx], xInner[n], d1Inner[n], norm, 1.0) + + getCubicHermiteCurvature(xInner[n], d1Inner[n], xInner[nextIdx], d1Inner[nextIdx], norm, 0.0)) factor = 1.0 - curvatureAround*wallThickness - nd1Inner = [ factor*c for c in d1Outer[n]] - xInner.append(x) - nd1.append(nd1Inner) - curvatureOuter.append(curvatureAround) + nd1Outer = [ factor*c for c in d1Inner[n]] + xOuter.append(x) + nd1.append(nd1Outer) + curvatureInner.append(curvatureAround) - return xInner, nd1, curvatureOuter + return xOuter, nd1, curvatureInner -def interpolatefromInnerAndOuter( xInner, xOuter, thickness, xi3, curvatureOuter, d1Outer, d2Outer, d3OuterUnit, +def interpolatefromInnerAndOuter( xInner, xOuter, thickness, xi3, curvatureInner, d1Inner, d2Inner, d3InnerUnit, elementsCountAround, elementsCountAlong, elementsCountThroughWall): """ Generate coordinates and derivatives at xi3 by interpolating with @@ -288,10 +292,10 @@ def interpolatefromInnerAndOuter( xInner, xOuter, thickness, xi3, curvatureOuter param xInner: Coordinates on inner surface param xOuter: Coordinates on outer surface param thickness: Thickness of wall - param curvatureOuter: Curvature of coordinates on outer surface - param d1Outer: Derivatives on outer surface around tube - param d2outer: Derivatives on outer surface along tube - param d3OuterUnit: Unit derivatives on inner surface through wall + param curvatureInner: Curvature of coordinates on inner surface + param d1Inner: Derivatives on inner surface around tube + param d2Inner: Derivatives on inner surface along tube + param d3InnerUnit: Unit derivatives on inner surface through wall param elementsCountAround: Number of elements around tube param elementsCountAlong: Number of elements along tube param elementsCountThroughWall: Number of elements through wall @@ -305,7 +309,7 @@ def interpolatefromInnerAndOuter( xInner, xOuter, thickness, xi3, curvatureOuter for n2 in range(elementsCountAlong+1): for n1 in range(elementsCountAround): n = n2*elementsCountAround + n1 - norm = [-1* c for c in d3OuterUnit[n]] + norm = d3InnerUnit[n] # x innerx = xInner[n] outerx = xOuter[n] @@ -313,25 +317,25 @@ def interpolatefromInnerAndOuter( xInner, xOuter, thickness, xi3, curvatureOuter x = interpolateCubicHermite(innerx, dWall, outerx, dWall, xi3) xList.append(x) # dx_ds1 - factor = 1.0 - curvatureOuter[n]*thickness*xi3 - dx_ds1 = [ factor*c for c in d1Outer[n]] + factor = 1.0 - curvatureInner[n]*thickness*xi3 + dx_ds1 = [ factor*c for c in d1Inner[n]] dx_ds1List.append(dx_ds1) # dx_ds2 if n2 > 0 and n2 < elementsCountAlong: prevIdx = (n2-1)*elementsCountAround + n1 nextIdx = (n2+1)*elementsCountAround + n1 curvatureAround = 0.5*( - getCubicHermiteCurvature(xOuter[prevIdx], d2Outer[prevIdx], xOuter[n], d2Outer[n], norm, 1.0)+ - getCubicHermiteCurvature(xOuter[n], d2Outer[n], xOuter[nextIdx], d2Outer[nextIdx], norm, 0.0)) + getCubicHermiteCurvature(xInner[prevIdx], d2Inner[prevIdx], xInner[n], d2Inner[n], norm, 1.0)+ + getCubicHermiteCurvature(xInner[n], d2Inner[n], xInner[nextIdx], d2Inner[nextIdx], norm, 0.0)) elif n2 == 0: nextIdx = (n2+1)*elementsCountAround + n1 - curvatureAround = getCubicHermiteCurvature(xOuter[n], d2Outer[n], xOuter[nextIdx], d2Outer[nextIdx], norm, 0.0) + curvatureAround = getCubicHermiteCurvature(xInner[n], d2Inner[n], xInner[nextIdx], d2Inner[nextIdx], norm, 0.0) else: prevIdx = (n2-1)*elementsCountAround + n1 - curvatureAround = getCubicHermiteCurvature(xOuter[prevIdx], d2Outer[prevIdx], xOuter[n], d2Outer[n], norm, 1.0) + curvatureAround = getCubicHermiteCurvature(xInner[prevIdx], d2Inner[prevIdx], xInner[n], d2Inner[n], norm, 1.0) factor = 1.0 - curvatureAround*thickness*xi3 - dx_ds2 = [ factor*c for c in d2Outer[n]] + dx_ds2 = [ factor*c for c in d2Inner[n]] dx_ds2List.append(dx_ds2) #dx_ds3 dx_ds3 = [c * thickness/elementsCountThroughWall for c in norm] From 0c361c53e17c103d11d1a315faae74e34f0afec5 Mon Sep 17 00:00:00 2001 From: Mabelle Lin Date: Thu, 7 Mar 2019 12:40:54 +1300 Subject: [PATCH 29/44] Remove wallThickness from list of function inputs --- scaffoldmaker/meshtypes/meshtype_3d_colon1.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/scaffoldmaker/meshtypes/meshtype_3d_colon1.py b/scaffoldmaker/meshtypes/meshtype_3d_colon1.py index 77cdd898..e7ba8e25 100644 --- a/scaffoldmaker/meshtypes/meshtype_3d_colon1.py +++ b/scaffoldmaker/meshtypes/meshtype_3d_colon1.py @@ -155,7 +155,7 @@ def generateBaseMesh(region, options): # Generate outer surface of a haustra segment xHaustraInner, d1HaustraInner, d2HaustraInner, haustraSegmentAxis = getColonHaustraSegmentInnerPoints(elementsCountAround, elementsCountAlongHaustrum, radius, cornerInnerRadiusFactor, - haustraInnerRadiusFactor, haustrumSegmentEndDerivativeFactor, haustrumSegmentMidDerivativeFactor, wallThickness, haustraSegmentLength) + haustraInnerRadiusFactor, haustrumSegmentEndDerivativeFactor, haustrumSegmentMidDerivativeFactor, haustraSegmentLength) # Generate tube mesh annotationGroups, nextNodeIdentifier, nextElementIdentifier = generatetubemesh(region, elementsCountAround, elementsCountAlongHaustrum, elementsCountThroughWall, haustraSegmentCount, @@ -187,7 +187,7 @@ def generateMesh(cls, region, options): return meshrefinement.getAnnotationGroups() def getColonHaustraSegmentInnerPoints(elementsCountAround, elementsCountAlongHaustrum, radius, cornerInnerRadiusFactor, - haustraInnerRadiusFactor, haustrumSegmentEndDerivativeFactor, haustrumSegmentMidDerivativeFactor, wallThickness, haustraSegmentLength): + haustraInnerRadiusFactor, haustrumSegmentEndDerivativeFactor, haustrumSegmentMidDerivativeFactor, haustraSegmentLength): """ Generates a 3-D haustra segment mesh with variable numbers of elements around, along the central line, and through wall. @@ -208,7 +208,6 @@ def getColonHaustraSegmentInnerPoints(elementsCountAround, elementsCountAlongHau length to scale derivative along the end of a haustra segment length. :param haustrumSegmentMidDerivativeFactor: Factor is multiplied by haustra length to scale derivative along the mid length of the haustra segment. - :param wallThickness: Thickness of haustra through wall. :param haustraSegmentLength: Length of a haustra segment. :return: coordinates, derivatives on inner surface of haustra segment. """ From fbde02b8e7fe59ffad733262f17028e3a84fb381 Mon Sep 17 00:00:00 2001 From: Mabelle Lin Date: Thu, 7 Mar 2019 12:47:44 +1300 Subject: [PATCH 30/44] Rename and clean up function for generating outer points --- scaffoldmaker/utils/tubemesh2.py | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/scaffoldmaker/utils/tubemesh2.py b/scaffoldmaker/utils/tubemesh2.py index 2a17963f..77b1a659 100644 --- a/scaffoldmaker/utils/tubemesh2.py +++ b/scaffoldmaker/utils/tubemesh2.py @@ -192,7 +192,7 @@ def generatetubemesh(region, d3InnerUnitList.append(d3Unit) # Pre-calculate node locations and derivatives on outer boundary - xOuterList, d1Outer, curvatureInner = getOuterCoordinatesAndDerivativesFromInner(xInnerList, d1InnerList, d3InnerUnitList, wallThickness, elementsCountAlong, elementsCountAround) + xOuterList, curvatureInner = getOuterCoordinatesAndCurvatureFromInner(xInnerList, d1InnerList, d3InnerUnitList, wallThickness, elementsCountAlong, elementsCountAround) # Interpolate to get nodes through wall for n3 in range(elementsCountThroughWall + 1): @@ -247,11 +247,11 @@ def generatetubemesh(region, return annotationGroups, nodeIdentifier, elementIdentifier -def getOuterCoordinatesAndDerivativesFromInner(xInner, d1Inner, d3Inner, wallThickness, elementsCountAlong, elementsCountAround): +def getOuterCoordinatesAndCurvatureFromInner(xInner, d1Inner, d3Inner, wallThickness, elementsCountAlong, elementsCountAround): """ - Generates coordinates and derivatives of outer surface from - coordinates and derivatives of inner surface using wall thickness - and normals. + Generates coordinates on outer surface and curvature of inner + surface from coordinates and derivatives of inner surface using + wall thickness and normals. param xInner: Coordinates on inner surface param d1Inner: Derivatives on inner surface around tube param d3Inner: Derivatives on inner surface through wall @@ -259,12 +259,9 @@ def getOuterCoordinatesAndDerivativesFromInner(xInner, d1Inner, d3Inner, wallThi param elementsCountAlong: Number of elements along tube param elementsCountAround: Number of elements around tube return xOuter: Coordinates on outer surface - return nd1: Derivatives on outer surface around tube return curvatureInner: Curvature of coordinates on inner surface """ xOuter = [] - nd1 = [] - dWall = [] curvatureInner = [] for n2 in range(elementsCountAlong + 1): for n1 in range(elementsCountAround): @@ -276,13 +273,10 @@ def getOuterCoordinatesAndDerivativesFromInner(xInner, d1Inner, d3Inner, wallThi curvatureAround = 0.5*( getCubicHermiteCurvature(xInner[prevIdx], d1Inner[prevIdx], xInner[n], d1Inner[n], norm, 1.0) + getCubicHermiteCurvature(xInner[n], d1Inner[n], xInner[nextIdx], d1Inner[nextIdx], norm, 0.0)) - factor = 1.0 - curvatureAround*wallThickness - nd1Outer = [ factor*c for c in d1Inner[n]] xOuter.append(x) - nd1.append(nd1Outer) curvatureInner.append(curvatureAround) - return xOuter, nd1, curvatureInner + return xOuter, curvatureInner def interpolatefromInnerAndOuter( xInner, xOuter, thickness, xi3, curvatureInner, d1Inner, d2Inner, d3InnerUnit, elementsCountAround, elementsCountAlong, elementsCountThroughWall): From 3da322b85b3b5e144c7ed01abfce5ae259e69a71 Mon Sep 17 00:00:00 2001 From: Mabelle Lin Date: Thu, 7 Mar 2019 12:55:09 +1300 Subject: [PATCH 31/44] Remove temporary parameter set Section 1 --- scaffoldmaker/meshtypes/meshtype_3d_colon1.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/scaffoldmaker/meshtypes/meshtype_3d_colon1.py b/scaffoldmaker/meshtypes/meshtype_3d_colon1.py index e7ba8e25..41f2ba1a 100644 --- a/scaffoldmaker/meshtypes/meshtype_3d_colon1.py +++ b/scaffoldmaker/meshtypes/meshtype_3d_colon1.py @@ -24,8 +24,7 @@ def getName(): def getParameterSetNames(): return [ 'Default', - 'Human 1', - 'Section 1'] + 'Human 1'] @staticmethod def getDefaultOptions(parameterSetName='Default'): @@ -50,9 +49,6 @@ def getDefaultOptions(parameterSetName='Default'): } if 'Human 1' in parameterSetName: options['Tube type'] = 3 - elif 'Section 1' in parameterSetName: - options['Tube type'] = 1 - options['Number of haustra segments'] = 3 return options @staticmethod From 7c00ad6caa3e99ee71323483ea05a35de13864c3 Mon Sep 17 00:00:00 2001 From: Mabelle Lin Date: Thu, 7 Mar 2019 13:18:20 +1300 Subject: [PATCH 32/44] Merge tubeMesh files into 1 and discard centrallinetube1 meshtype --- .../meshtypes/meshtype_3d_centrallinetube1.py | 172 --------- scaffoldmaker/meshtypes/meshtype_3d_colon1.py | 2 +- scaffoldmaker/scaffolds.py | 2 - scaffoldmaker/utils/tubemesh.py | 320 ++++++++++------- scaffoldmaker/utils/tubemesh2.py | 338 ------------------ 5 files changed, 192 insertions(+), 642 deletions(-) delete mode 100644 scaffoldmaker/meshtypes/meshtype_3d_centrallinetube1.py delete mode 100644 scaffoldmaker/utils/tubemesh2.py diff --git a/scaffoldmaker/meshtypes/meshtype_3d_centrallinetube1.py b/scaffoldmaker/meshtypes/meshtype_3d_centrallinetube1.py deleted file mode 100644 index 16af63e4..00000000 --- a/scaffoldmaker/meshtypes/meshtype_3d_centrallinetube1.py +++ /dev/null @@ -1,172 +0,0 @@ -""" -Generates a 3-D tubular mesh from a central line -with variable numbers of elements around, along and -through wall, with variable major and minor axis length and -wall thickness. -""" - -from scaffoldmaker.meshtypes.scaffold_base import Scaffold_base -from scaffoldmaker.utils.meshrefinement import MeshRefinement -from scaffoldmaker.utils.tubemesh import * - -class MeshType_3d_centrallinetube1(Scaffold_base): - ''' - Generates a 3-D tubular mesh with variable numbers - of elements around, along the central line, and through wall. - The ellipsoidal tube is created from a central line and - lateral axes data - ''' - @staticmethod - def getName(): - return '3D Central Line Tube 1' - - @staticmethod - def getDefaultOptions(parameterSetName='Default'): - return { - 'Number of elements around' : 8, - 'Number of elements along' : 30, #6 - 'Number of elements through wall' : 1, - 'Tube type' : 4, - 'Use cross derivatives' : False, - 'Use linear through wall' : False, - 'Refine' : False, - 'Refine number of elements around' : 1, - 'Refine number of elements along' : 1, - 'Refine number of elements through wall' : 1 - } - - @staticmethod - def getOrderedOptionNames(): - return [ - 'Number of elements around', - 'Number of elements along', - 'Number of elements through wall', - 'Tube type', - 'Use cross derivatives', - 'Use linear through wall', - 'Refine', - 'Refine number of elements around', - 'Refine number of elements along', - 'Refine number of elements through wall' - ] - - @staticmethod - def checkOptions(options): - for key in [ - 'Number of elements along', - 'Number of elements through wall', - 'Refine number of elements around', - 'Refine number of elements along', - 'Refine number of elements through wall']: - if options[key] < 1: - options[key] = 1 - if (options['Number of elements around'] < 2) : - options['Number of elements around'] = 2 - if (options['Tube type'] < 1): - options['Tube type'] = 1 - if (options['Tube type'] > 5): - options['Tube type'] = 5 - - @staticmethod - def generateBaseMesh(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: None - """ - elementsCountAround = options['Number of elements around'] - elementsCountAlong = options['Number of elements along'] - elementsCountThroughWall = options['Number of elements through wall'] - tubeType = options['Tube type'] - useCrossDerivatives = options['Use cross derivatives'] - useCubicHermiteThroughWall = not(options['Use linear through wall']) - - if tubeType == 1: - # Straight tube - cx = [[1.0, 3.0, 0.0], [ 2.0, 0.0, 4.0 ] ] - cd1 = [[ 1.0, -3.0, 4.0 ], [ 1.0, -3.0, 4.0 ]] - cd2 = [ [ 0.0, 0.2, 0.0 ], [ 0.0, 0.2, 0.0 ]] - cd3 = [ [ 0.0, 0.0, 0.5 ], [ 0.0, 0.0, 0.5 ]] - - # thickness in cd2 and cd3 directions and derivatives (rate of change) - t2 = [ 0.1, 0.5 ] - t2d = [ 0.0, 0.0 ] - t3 = [ 0.5, 0.5 ] - t3d = [ 0.0, 0.0 ] - - elif tubeType == 2: - # Curved tube 1 - cx = [ [ 0.0, 0.0, 0.0], [1.0, 1.0, 0.0], [ 2.0, 0.0, 0.0 ] ] - cd1 = [ [ 1.0, 1.0, 0.0 ], [ 1.0, 0.0, 0.0 ], [1.0, -1.0, 0.0] ] - cd2 = [ [ 0.0, 0.1, 0.0 ], [ 0.0, 0.1, 0.0 ], [ 0.0, 0.1, 0.0 ] ] - cd3 = [ [ 0.0, 0.0, 0.2 ], [ 0.0, 0.0, 0.2 ], [ 0.0, 0.0, 0.2] ] - - # thickness in cd2 and cd3 directions and derivatives (rate of change) - t2 = [ 0.1, 0.1, 0.1 ] - t2d = [ 0.0, 0.0, 0.0 ] - t3 = [ 0.1, 0.1, 0.1 ] - t3d = [ 0.0, 0.0, 0.0 ] - - elif tubeType == 3: - # Curved tube 2 - cx = [ [ 0.0, 0.0, 1.0], [1.5, 1.0, 0.0], [ 3.0, -1.0, 0.0 ], [ 5.0, 1.5, 1.0]] - cd1 = [ [ 4.0, 0.0, 0.0 ], [ 2.0, 0.0, 0.0 ], [3.0, 0.0, 0.0], [ 3.0, 0.0, 0.0 ]] - cd2 = [ [ 0.0, 0.2, 0.0 ], [ 0.0, 0.2, 0.0 ], [ 0.0, 0.2, 0.0 ], [ 0.0, 0.2, 0.0] ] - cd3 = [ [ 0.0, 0.0, 0.2 ], [ 0.0, 0.0, 0.2 ], [ 0.0, 0.0, 0.2], [ 0.0, 0.0, 0.2 ]] - - # thickness in cd2 and cd3 directions and derivatives (rate of change) - t2 = [ 0.1, 0.1, 0.1, 0.1 ] - t2d = [ 0.0, 0.0, 0.0, 0.0] - t3 = [ 0.1, 0.1, 0.1, 0.1] - t3d = [ 0.0, 0.0, 0.0, 0.0] - - elif tubeType == 4: - # Colon in x-y plane - cx = [ [ 0.0, 0.0, 0.0], [0.0, 10.0, 0.0], [5.0, 9.0, 0.0], [ 10.0, 10.0, 0.0 ], [ 10.0, -2.0, 0.0], [ 7.0, -4.0, 0.0] ] - cd1 = [ [ 0.0, 10.0, 0.0 ], [ 5.0, 5.0, 0.0 ], [5.0, 0.0, 0.0], [ 5.0, -5.0, 0.0 ], [ -3.0, -5.0, 0.0 ], [ -3.0, 0.0, 0.0 ]] - cd2 = [ [ 0.0, 1.0, 0.0 ], [ 0.0, 1.0, 0.0 ], [ 0.0, 1.0, 0.0 ], [ 0.0, 1.0, 0.0], [ 0.0, 1.0, 0.0 ], [ 0.0, 1.0, 0.0 ]] - cd3 = [ [ 0.0, 0.0, 1.0 ], [ 0.0, 0.0, 1.0 ], [ 0.0, 0.0, 1.0], [ 0.0, 0.0, 1.0 ], [ 0.0, 0.0, 1.0], [ 0.0, 0.0, 1.0]] - - # thickness in cd2 and cd3 directions and derivatives (rate of change) - t2 = [ 0.5, 0.5, 0.5, 0.5, 0.5, 0.5 ] - t2d = [ 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 ] - t3 = [ 0.5, 0.5, 0.5, 0.5, 0.5, 0.5] - t3d = [ 0.0, 0.0, 0.0, 0.0, 0.0, 0.0] - - elif tubeType == 5: - # Colon in 3D - cx = [ [ 0.0, 0.0, 0.0], [0.0, 10.0, 3.0], [5.0, 9.0, 0.0], [ 10.0, 10.0, 2.0 ], [15.0, 15.0, 7.0], [ 20.0, -2.0, 0.0], [ 10.0, -4.0, -0.0] ] - cd1 = [ [ 0.0, 10.0, 3.0 ], [ 5.0, 5.0, 0.0 ], [5.0, 0.0, 0.0], [ 10.0, -5.0, 0.0 ], [12.0, 12.0, 0.0], [ 5.0, -12.0, -5.0 ], [ -8.0, 0.0, 0.0 ]] - cd2 = [ [ 0.0, 0.5, 0.0 ], [ 0.0, 0.5, 0.0 ], [ 0.0, 0.5, 0.0 ], [ 0.0, 0.5, 0.0 ], [ 0.0, 0.5, 0.0 ], [ 0.0, 0.5, 0.0 ], [ 0.0, 0.5, 0.0 ]] - cd3 = [ [ 0.0, 0.0, 0.5 ], [ 0.0, 0.0, 0.5 ], [ 0.0, 0.0, 0.5], [ 0.0, 0.0, 0.5], [ 0.0, 0.0, 0.5], [ 0.0, 0.0, 0.5 ], [ 0.0, 0.0, 0.5]] - - # thickness in cd2 and cd3 directions and derivatives (rate of change) - t2 = [ 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5 ] - t2d = [ 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 ] - t3 = [ 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5] - t3d = [ 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0] - - nextNodeIdentifier, nextElementIdentifier = generatetubemesh(region, elementsCountAlong, elementsCountAround, elementsCountThroughWall, - cx, cd1, cd2, cd3, t2, t2d, t3, t3d, useCrossDerivatives, useCubicHermiteThroughWall) - - @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(). - """ - if not options['Refine']: - cls.generateBaseMesh(region, options) - return - - refineElementsCountAround = options['Refine number of elements around'] - refineElementsCountAlong = options['Refine number of elements along'] - refineElementsCountThroughWall = options['Refine number of elements through wall'] - - baseRegion = region.createRegion() - cls.generateBaseMesh(baseRegion, options) - - meshrefinement = MeshRefinement(baseRegion, region) - meshrefinement.refineAllElementsCubeStandard3d(refineElementsCountAround, refineElementsCountAlong, refineElementsCountThroughWall) diff --git a/scaffoldmaker/meshtypes/meshtype_3d_colon1.py b/scaffoldmaker/meshtypes/meshtype_3d_colon1.py index 41f2ba1a..f969ff74 100644 --- a/scaffoldmaker/meshtypes/meshtype_3d_colon1.py +++ b/scaffoldmaker/meshtypes/meshtype_3d_colon1.py @@ -6,7 +6,7 @@ from scaffoldmaker.meshtypes.scaffold_base import Scaffold_base from scaffoldmaker.utils.matrix import * from scaffoldmaker.utils.meshrefinement import MeshRefinement -from scaffoldmaker.utils.tubemesh2 import * +from scaffoldmaker.utils.tubemesh import * class MeshType_3d_colon1(Scaffold_base): ''' diff --git a/scaffoldmaker/scaffolds.py b/scaffoldmaker/scaffolds.py index 3b929d7d..4f6ae141 100644 --- a/scaffoldmaker/scaffolds.py +++ b/scaffoldmaker/scaffolds.py @@ -8,7 +8,6 @@ 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_centrallinetube1 import MeshType_3d_centrallinetube1 from scaffoldmaker.meshtypes.meshtype_3d_colon1 import MeshType_3d_colon1 from scaffoldmaker.meshtypes.meshtype_3d_haustra1 import MeshType_3d_haustra1 from scaffoldmaker.meshtypes.meshtype_3d_heart1 import MeshType_3d_heart1 @@ -39,7 +38,6 @@ def __init__(self): MeshType_2d_tube1, MeshType_3d_box1, MeshType_3d_boxhole1, - MeshType_3d_centrallinetube1, MeshType_3d_colon1, MeshType_3d_haustra1, MeshType_3d_heart1, diff --git a/scaffoldmaker/utils/tubemesh.py b/scaffoldmaker/utils/tubemesh.py index 77424bf7..77b1a659 100644 --- a/scaffoldmaker/utils/tubemesh.py +++ b/scaffoldmaker/utils/tubemesh.py @@ -1,8 +1,6 @@ ''' -Utility function for generating tubular mesh from a central line. -Created on Oct 9, 2018 - -@author: Mabelle Lin +Utility function for generating tubular mesh from a central line +using a segment profile. ''' from __future__ import division import math @@ -18,10 +16,13 @@ from opencmiss.zinc.node import Node def generatetubemesh(region, - elementsCountAlong, elementsCountAround, + elementsCountAlongSegment, elementsCountThroughWall, - cx, cd1, cd2, cd3, t2, t2d, t3, t3d, + segmentCountAlong, + cx, cd1, + xInner, d1Inner, d2Inner, wallThickness, + segmentAxis, segmentLength, useCrossDerivatives, useCubicHermiteThroughWall, # or Zinc Elementbasis.FUNCTION_TYPE_LINEAR_LAGRANGE etc. nextNodeIdentifier = 1, nextElementIdentifier = 1 @@ -29,37 +30,29 @@ def generatetubemesh(region, ''' Generates a 3-D tubular mesh with variable numbers of elements around, along the central axis, and radially through wall. The - ellipsoidal tubular mesh is created from central line and lateral - axes data - :param elementsCountAlong: number of elements along tube + tubular mesh is created from a segment profile which is mapped onto + the central line and lateral axes data :param elementsCountAround: number of elements around tube + :param elementsCountAlongSegment: number of elements along segment profile :param elementsCountThroughWall: number of elements through wall thickness + :param segmentCountAlong: number of segments along the tube :param cx: coordinates on central line - :param cd1: derivative 1 along central line - :param cd2: derivative 2 to the lateral axis - :param cd3: derivative 3 to the lateral axis perpendicular to direction of cd2 - :param t2: wall thickness in cd2 direction - :param t2d: wall thickness derivative in cd2 direction (rate of change) - :param t3: wall thickness in cd3 direction - :param t3d: wall thickness derivative in cd3 direction (rate of change) + :param cd1: derivative along central line + :param xInner: coordinates on inner surface of segment profile + :param d1Inner: derivatives around inner surface of segment profile + :param d2Inner: derivatives along inner surface of segment profile + :param wallThickness: thickness of wall + :param segmentAxis: axis of segment profile + :param segmentLength: length of segment profile :param useCubicHermiteThroughWall: use linear when false - :param nextNodeIdentifier, nextElementIdentifier: Next identifiers to use and increment. - :return: Final values of nextNodeIdentifier, nextElementIdentifier + :return: annotationGroups, nextNodeIdentifier, nextElementIdentifier ''' - - zero = [ 0.0, 0.0, 0.0 ] + zero = [0.0, 0.0, 0.0] + annotationGroups = [] + elementsCountAlong = elementsCountAlongSegment*segmentCountAlong # Sample central line to get same number of elements as elementsCountAlong sx, sd1, se, sxi, _ = sampleCubicHermiteCurves(cx, cd1, elementsCountAlong) - # sd1Smoothed = smoothCubicHermiteDerivativesLine(sx, sd1) - # sd1 = [] - # sd1 = sd1Smoothed - sd2 = interpolateSampleLinear(cd2, se, sxi) - sd3 = interpolateSampleLinear(cd3, se, sxi) - st2 = interpolateSampleLinear(t2, se, sxi) - st2d = interpolateSampleLinear(t2d, se, sxi) - st3 = interpolateSampleLinear(t3, se, sxi) - st3d = interpolateSampleLinear(t3d, se, sxi) # Find unit normals and binormals at each sample points sNormal = [] @@ -134,116 +127,95 @@ def generatetubemesh(region, dx_ds1 = [ 0.0, 0.0, 0.0 ] dx_ds2 = [ 0.0, 0.0, 0.0 ] dx_ds3 = [ 0.0, 0.0, 0.0 ] - innerx = [] - innerdx_ds1 = [] - outerx = [] - dWall = [] - unitNormalInner = [] - curvatureAlong = [] - ellipsex = [] - ellipsedx_ds1 = [] - tubex = [] - tubedx_ds1 = [] - tubedx_ds2 = [] - tubedx_ds3 = [] - - # Calculate node location and derivatives for inner and outer surface - for n2 in range(elementsCountAlong+1): - aInner = sd2[n2][1] - bInner = sd3[n2][2] - perimeterInner = getApproximateEllipsePerimeter(aInner, bInner) - arcLengthPerElementAroundInner = perimeterInner / elementsCountAround - prevRadiansAroundInner = updateEllipseAngleByArcLength(aInner, bInner, 0.0, arcLengthPerElementAroundInner) + xInnerList = [] + d1InnerList = [] + d2InnerList = [] + d3InnerUnitList = [] + xList = [] + dx_ds1List = [] + dx_ds2List = [] + dx_ds3List = [] - aOuter = sd2[n2][1] + st2[n2] - bOuter = sd3[n2][2] + st3[n2] - perimeterOuter = getApproximateEllipsePerimeter(aOuter, bOuter) - arcLengthPerElementAroundOuter = perimeterOuter / elementsCountAround - prevRadiansAroundOuter = updateEllipseAngleByArcLength(aOuter, bOuter, 0.0, arcLengthPerElementAroundOuter) + # Map each face along segment profile to central line + for nSegment in range(segmentCountAlong): + for nAlongSegment in range(elementsCountAlongSegment+1): + n2 = nSegment*elementsCountAlongSegment + nAlongSegment + if nSegment == 0 or (nSegment > 0 and nAlongSegment > 0): + # Rotate to align segment axis with tangent of central line + segmentMid = [0.0, 0.0, segmentLength/elementsCountAlongSegment* nAlongSegment] + unitTangent = normalise(sd1[n2]) + cp = crossproduct3(segmentAxis, unitTangent) + if magnitude(cp)> 0.0: + axisRot = normalise(cp) + thetaRot = math.acos(dotproduct(segmentAxis, unitTangent)) + rotFrame = getRotationMatrixFromAxisAngle(axisRot, thetaRot) + midRot = [rotFrame[j][0]*segmentMid[0] + rotFrame[j][1]*segmentMid[1] + rotFrame[j][2]*segmentMid[2] for j in range(3)] + translateMatrix = [sx[n2][j] - midRot[j] for j in range(3)] + else: + midRot = segmentMid - for n1 in range(elementsCountAround): - arcLengthAroundInner = n1*arcLengthPerElementAroundInner - radiansAroundInner = -1*updateEllipseAngleByArcLength(aInner, bInner, 0.0, arcLengthAroundInner) - cosRadiansAroundInner = math.cos(radiansAroundInner) - sinRadiansAroundInner = math.sin(radiansAroundInner) - xInner = [sx[n2][j] + aInner*cosRadiansAroundInner*sBinormal[n2][j] + bInner*sinRadiansAroundInner*sNormal[n2][j] for j in range(3)] - innerx.append(xInner) - dx_ds1Inner = [(radiansAroundInner - prevRadiansAroundInner)*(aInner*-sinRadiansAroundInner*sBinormal[n2][j] + bInner*cosRadiansAroundInner*sNormal[n2][j]) for j in range(3)] - innerdx_ds1.append(dx_ds1Inner) - prevRadiansAroundInner = radiansAroundInner - unitNormalInnerNode = normalise([aInner*cosRadiansAroundInner*sBinormal[n2][j] + bInner*sinRadiansAroundInner*sNormal[n2][j] for j in range(3)]) - unitNormalInner.append(unitNormalInnerNode) + for n1 in range(elementsCountAround): + n = nAlongSegment*elementsCountAround + n1 + x = xInner[n] + d1 = d1Inner[n] + d2 = d2Inner[n] + if magnitude(cp)> 0.0: + xRot1 = [rotFrame[j][0]*x[0] + rotFrame[j][1]*x[1] + rotFrame[j][2]*x[2] for j in range(3)] + d1Rot1 = [rotFrame[j][0]*d1[0] + rotFrame[j][1]*d1[1] + rotFrame[j][2]*d1[2] for j in range(3)] + d2Rot1 = [rotFrame[j][0]*d2[0] + rotFrame[j][1]*d2[1] + rotFrame[j][2]*d2[2] for j in range(3)] + # Rotate to align first vector on face with binormal axis + if n1 == 0: + firstVector = normalise([xRot1[j] - midRot[j] for j in range(3)]) + thetaRot2 = math.acos(dotproduct(normalise(sBinormal[n2]), firstVector)) + cp2 = crossproduct3(normalise(sBinormal[n2]), firstVector) + if magnitude(cp2) > 0.0: + cp2 = normalise(cp2) + signThetaRot2 = dotproduct(unitTangent, cp2) + axisRot2 = unitTangent + rotFrame2 = getRotationMatrixFromAxisAngle(axisRot2, -signThetaRot2*thetaRot2) + else: + rotFrame2 = [ [1, 0, 0], [0, 1, 0], [0, 0, 1]] + xRot2 = [rotFrame2[j][0]*xRot1[0] + rotFrame2[j][1]*xRot1[1] + rotFrame2[j][2]*xRot1[2] for j in range(3)] + d1Rot2 = [rotFrame2[j][0]*d1Rot1[0] + rotFrame2[j][1]*d1Rot1[1] + rotFrame2[j][2]*d1Rot1[2] for j in range(3)] + d2Rot2 = [rotFrame2[j][0]*d2Rot1[0] + rotFrame2[j][1]*d2Rot1[1] + rotFrame2[j][2]*d2Rot1[2] for j in range(3)] + else: + xRot2 = x + d1Rot2 = d1 + d2Rot2 = d2 - arcLengthAroundOuter = n1*arcLengthPerElementAroundOuter - radiansAroundOuter = -1*updateEllipseAngleByArcLength(aOuter, bOuter, 0.0, arcLengthAroundOuter) - cosRadiansAroundOuter = math.cos(radiansAroundOuter) - sinRadiansAroundOuter = math.sin(radiansAroundOuter) - xOuter = [sx[n2][j] + aOuter*cosRadiansAroundOuter*sBinormal[n2][j] + bOuter*sinRadiansAroundOuter*sNormal[n2][j] for j in range(3)] - outerx.append(xOuter) - prevRadiansAroundOuter = radiansAroundOuter + xTranslate = [xRot2[j] + translateMatrix[j] for j in range(3)] - d = [ xOuter[i] - xInner[i] for i in range(3)] - dWall.append(d) + xInnerList.append(xTranslate) + d1InnerList.append(d1Rot2) + d2InnerList.append(d2Rot2) + d3Unit = normalise(crossproduct3(normalise(d1Rot2), normalise(d2Rot2))) + d3InnerUnitList.append(d3Unit) - if n2 == 0: - curvature = getCubicHermiteCurvature(sx[n2], sd1[n2], sx[n2+1], sd1[n2+1], unitNormalInnerNode, 0.0) - elif n2 == elementsCountAlong: - curvature = getCubicHermiteCurvature(sx[n2-1], sd1[n2-1], sx[n2], sd1[n2], unitNormalInnerNode, 1.0) - else: - curvature = 0.5*( - getCubicHermiteCurvature(sx[n2-1], sd1[n2-1], sx[n2], sd1[n2], unitNormalInnerNode, 1.0) + - getCubicHermiteCurvature(sx[n2], sd1[n2], sx[n2+1], sd1[n2+1], unitNormalInnerNode, 0.0)) - curvatureAlong.append(curvature) + # Pre-calculate node locations and derivatives on outer boundary + xOuterList, curvatureInner = getOuterCoordinatesAndCurvatureFromInner(xInnerList, d1InnerList, d3InnerUnitList, wallThickness, elementsCountAlong, elementsCountAround) # Interpolate to get nodes through wall for n3 in range(elementsCountThroughWall + 1): - xi = 1/elementsCountThroughWall*n3 - - for n2 in range(elementsCountAlong+1): - for n1 in range(elementsCountAround): - n = n2*elementsCountAround + n1 - - # x - x = interpolateCubicHermite(innerx[n], dWall[n], outerx[n], dWall[n], xi) - tubex.append(x) - - # dx_ds1 - if n1 == 0: - prevIdx = (((n+1)//elementsCountAround + 1)*elementsCountAround)-1 - ellipsex = [] - ellipsedx_ds1 = [] - else: - prevIdx = n - 1 - nextIdx = ((n+1)//(elementsCountAround +1) * elementsCountAround + ((n+1)%elementsCountAround) + 1)-1 - curvatureAround = 0.5*( - getCubicHermiteCurvature(innerx[prevIdx], innerdx_ds1[prevIdx], innerx[n], innerdx_ds1[n], unitNormalInner[n], 1.0) + - getCubicHermiteCurvature(innerx[n], innerdx_ds1[n], innerx[nextIdx], innerdx_ds1[nextIdx], unitNormalInner[n], 0.0)) - wallDistance = magnitude(dWall[n])*xi - factor = 1.0 - curvatureAround*wallDistance - dx_ds1 = [ factor*c for c in innerdx_ds1[n]] - ellipsedx_ds1.append(dx_ds1) - ellipsex.append(x) - if n1 == elementsCountAround-1: - smoothdx_ds1 = smoothCubicHermiteDerivativesLoop(ellipsex, ellipsedx_ds1) - tubedx_ds1 = tubedx_ds1 + smoothdx_ds1 - - # dx_ds2 - factor = 1.0 - curvatureAlong[n]*wallDistance - d1AlongTube = [ factor*c for c in sd1[n2]] - tubedx_ds2.append(d1AlongTube) + xi3 = 1/elementsCountThroughWall * n3 + x, dx_ds1, dx_ds2, dx_ds3 = interpolatefromInnerAndOuter( xInnerList, xOuterList, wallThickness, xi3, curvatureInner, d1InnerList, d2InnerList, d3InnerUnitList, elementsCountAround, elementsCountAlong, elementsCountThroughWall) + xList = xList + x + dx_ds1List = dx_ds1List + dx_ds1 + dx_ds2List = dx_ds2List + dx_ds2 + dx_ds3List = dx_ds3List + dx_ds3 - #dx_ds3 - dx_ds3 = interpolateCubicHermiteDerivative(innerx[n], dWall[n], outerx[n], dWall[n], xi) - dx_ds3 = [c/elementsCountThroughWall for c in dx_ds3] - tubedx_ds3.append(dx_ds3) - - for n in range((elementsCountAround)*(elementsCountAlong+1)*(elementsCountThroughWall+1)): + for n in range(len(xList)): node = nodes.createNode(nodeIdentifier, nodetemplate) cache.setNode(node) - coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_VALUE, 1, tubex[n]) - coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS1, 1, tubedx_ds1[n]) - coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS2, 1, tubedx_ds2[n]) - coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS3, 1, tubedx_ds3[n]) + coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_VALUE, 1, xList[n]) + coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS1, 1, dx_ds1List[n]) + coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS2, 1, dx_ds2List[n]) + coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS3, 1, dx_ds3List[n]) + if useCrossDerivatives: + coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D2_DS1DS2, 1, zero) + coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D2_DS1DS3, 1, zero) + coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D2_DS2DS3, 1, zero) + coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D3_DS1DS2DS3, 1, zero) + # print('NodeIdentifier = ', nodeIdentifier, xList[n]) nodeIdentifier = nodeIdentifier + 1 # # For debugging - Nodes along central line @@ -273,4 +245,94 @@ def generatetubemesh(region, fm.endChange() - return nodeIdentifier, elementIdentifier + return annotationGroups, nodeIdentifier, elementIdentifier + +def getOuterCoordinatesAndCurvatureFromInner(xInner, d1Inner, d3Inner, wallThickness, elementsCountAlong, elementsCountAround): + """ + Generates coordinates on outer surface and curvature of inner + surface from coordinates and derivatives of inner surface using + wall thickness and normals. + param xInner: Coordinates on inner surface + param d1Inner: Derivatives on inner surface around tube + param d3Inner: Derivatives on inner surface through wall + param wallThickness: Thickness of wall + param elementsCountAlong: Number of elements along tube + param elementsCountAround: Number of elements around tube + return xOuter: Coordinates on outer surface + return curvatureInner: Curvature of coordinates on inner surface + """ + xOuter = [] + curvatureInner = [] + for n2 in range(elementsCountAlong + 1): + for n1 in range(elementsCountAround): + n = n2*elementsCountAround + n1 + x = [xInner[n][i] + d3Inner[n][i]*wallThickness for i in range(3)] + prevIdx = n-1 if (n1 != 0) else (n2+1)*elementsCountAround - 1 + nextIdx = n+1 if (n1 < elementsCountAround-1) else n2*elementsCountAround + norm = d3Inner[n] + curvatureAround = 0.5*( + getCubicHermiteCurvature(xInner[prevIdx], d1Inner[prevIdx], xInner[n], d1Inner[n], norm, 1.0) + + getCubicHermiteCurvature(xInner[n], d1Inner[n], xInner[nextIdx], d1Inner[nextIdx], norm, 0.0)) + xOuter.append(x) + curvatureInner.append(curvatureAround) + + return xOuter, curvatureInner + +def interpolatefromInnerAndOuter( xInner, xOuter, thickness, xi3, curvatureInner, d1Inner, d2Inner, d3InnerUnit, + elementsCountAround, elementsCountAlong, elementsCountThroughWall): + """ + Generate coordinates and derivatives at xi3 by interpolating with + inner and outer coordinates and derivatives. + param xInner: Coordinates on inner surface + param xOuter: Coordinates on outer surface + param thickness: Thickness of wall + param curvatureInner: Curvature of coordinates on inner surface + param d1Inner: Derivatives on inner surface around tube + param d2Inner: Derivatives on inner surface along tube + param d3InnerUnit: Unit derivatives on inner surface through wall + param elementsCountAround: Number of elements around tube + param elementsCountAlong: Number of elements along tube + param elementsCountThroughWall: Number of elements through wall + return xList, dx_ds1List, dx_ds2List, dx_ds3List: Coordinates and derivatives on xi3 + """ + xList = [] + dx_ds1List = [] + dx_ds2List = [] + dx_ds3List =[] + + for n2 in range(elementsCountAlong+1): + for n1 in range(elementsCountAround): + n = n2*elementsCountAround + n1 + norm = d3InnerUnit[n] + # x + innerx = xInner[n] + outerx = xOuter[n] + dWall = [thickness*c for c in norm] + x = interpolateCubicHermite(innerx, dWall, outerx, dWall, xi3) + xList.append(x) + # dx_ds1 + factor = 1.0 - curvatureInner[n]*thickness*xi3 + dx_ds1 = [ factor*c for c in d1Inner[n]] + dx_ds1List.append(dx_ds1) + # dx_ds2 + if n2 > 0 and n2 < elementsCountAlong: + prevIdx = (n2-1)*elementsCountAround + n1 + nextIdx = (n2+1)*elementsCountAround + n1 + curvatureAround = 0.5*( + getCubicHermiteCurvature(xInner[prevIdx], d2Inner[prevIdx], xInner[n], d2Inner[n], norm, 1.0)+ + getCubicHermiteCurvature(xInner[n], d2Inner[n], xInner[nextIdx], d2Inner[nextIdx], norm, 0.0)) + elif n2 == 0: + nextIdx = (n2+1)*elementsCountAround + n1 + curvatureAround = getCubicHermiteCurvature(xInner[n], d2Inner[n], xInner[nextIdx], d2Inner[nextIdx], norm, 0.0) + else: + prevIdx = (n2-1)*elementsCountAround + n1 + curvatureAround = getCubicHermiteCurvature(xInner[prevIdx], d2Inner[prevIdx], xInner[n], d2Inner[n], norm, 1.0) + + factor = 1.0 - curvatureAround*thickness*xi3 + dx_ds2 = [ factor*c for c in d2Inner[n]] + dx_ds2List.append(dx_ds2) + #dx_ds3 + dx_ds3 = [c * thickness/elementsCountThroughWall for c in norm] + dx_ds3List.append(dx_ds3) + + return xList, dx_ds1List, dx_ds2List, dx_ds3List diff --git a/scaffoldmaker/utils/tubemesh2.py b/scaffoldmaker/utils/tubemesh2.py deleted file mode 100644 index 77b1a659..00000000 --- a/scaffoldmaker/utils/tubemesh2.py +++ /dev/null @@ -1,338 +0,0 @@ -''' -Utility function for generating tubular mesh from a central line -using a segment profile. -''' -from __future__ import division -import math -from scaffoldmaker.utils.eftfactory_bicubichermitelinear import eftfactory_bicubichermitelinear -from scaffoldmaker.utils.eftfactory_tricubichermite import eftfactory_tricubichermite -from scaffoldmaker.utils.geometry import * -from scaffoldmaker.utils.interpolation import * -from scaffoldmaker.utils.matrix import * -from scaffoldmaker.utils.vector import * -from scaffoldmaker.utils.zinc_utils import * -from opencmiss.zinc.element import Element, Elementbasis -from opencmiss.zinc.field import Field -from opencmiss.zinc.node import Node - -def generatetubemesh(region, - elementsCountAround, - elementsCountAlongSegment, - elementsCountThroughWall, - segmentCountAlong, - cx, cd1, - xInner, d1Inner, d2Inner, wallThickness, - segmentAxis, segmentLength, - useCrossDerivatives, - useCubicHermiteThroughWall, # or Zinc Elementbasis.FUNCTION_TYPE_LINEAR_LAGRANGE etc. - nextNodeIdentifier = 1, nextElementIdentifier = 1 - ): - ''' - Generates a 3-D tubular mesh with variable numbers of elements - around, along the central axis, and radially through wall. The - tubular mesh is created from a segment profile which is mapped onto - the central line and lateral axes data - :param elementsCountAround: number of elements around tube - :param elementsCountAlongSegment: number of elements along segment profile - :param elementsCountThroughWall: number of elements through wall thickness - :param segmentCountAlong: number of segments along the tube - :param cx: coordinates on central line - :param cd1: derivative along central line - :param xInner: coordinates on inner surface of segment profile - :param d1Inner: derivatives around inner surface of segment profile - :param d2Inner: derivatives along inner surface of segment profile - :param wallThickness: thickness of wall - :param segmentAxis: axis of segment profile - :param segmentLength: length of segment profile - :param useCubicHermiteThroughWall: use linear when false - :return: annotationGroups, nextNodeIdentifier, nextElementIdentifier - ''' - zero = [0.0, 0.0, 0.0] - annotationGroups = [] - elementsCountAlong = elementsCountAlongSegment*segmentCountAlong - - # Sample central line to get same number of elements as elementsCountAlong - sx, sd1, se, sxi, _ = sampleCubicHermiteCurves(cx, cd1, elementsCountAlong) - - # Find unit normals and binormals at each sample points - sNormal = [] - sBinormal = [] - - # Set up normal and binormal for first frame - prevUnitTangent = normalise(sd1[0]) - if magnitude(crossproduct3(prevUnitTangent,[0.0, 0.0, 1.0])) > 0.0: - prevBinormal = crossproduct3(prevUnitTangent,[0.0, 0.0, 1.0]) - else: - prevBinormal = crossproduct3(prevUnitTangent,[0.0, -1.0, 0.0]) - prevUnitBinormal = normalise(prevBinormal) - prevUnitNormal = crossproduct3(prevUnitBinormal, prevUnitTangent) - sNormal.append(prevUnitNormal) - sBinormal.append(prevUnitBinormal) - - # Step through central line and rotate central line axes to align tangent - # to tangent from previous frame - for n in range(1, elementsCountAlong+1): - unitTangent = normalise(sd1[n]) - cp = crossproduct3(prevUnitTangent, unitTangent) - if magnitude(cp)> 0.0: - axisRot = normalise(cp) - thetaRot = math.acos(dotproduct(prevUnitTangent, unitTangent)) - rotFrame = getRotationMatrixFromAxisAngle(axisRot, thetaRot) - rotNormal = [rotFrame[j][0]*prevUnitNormal[0] + rotFrame[j][1]*prevUnitNormal[1] + rotFrame[j][2]*prevUnitNormal[2] for j in range(3)] - unitNormal = normalise(rotNormal) - unitBinormal = crossproduct3(unitTangent, unitNormal) - prevUnitTangent = unitTangent - prevUnitNormal = unitNormal - else: - unitBinormal = prevUnitBinormal - unitNormal = prevUnitNormal - sNormal.append(unitNormal) - sBinormal.append(unitBinormal) - - fm = region.getFieldmodule() - fm.beginChange() - cache = fm.createFieldcache() - coordinates = getOrCreateCoordinateField(fm) - - nodes = fm.findNodesetByFieldDomainType(Field.DOMAIN_TYPE_NODES) - nodetemplate = nodes.createNodetemplate() - nodetemplate.defineField(coordinates) - nodetemplate.setValueNumberOfVersions(coordinates, -1, Node.VALUE_LABEL_VALUE, 1) - nodetemplate.setValueNumberOfVersions(coordinates, -1, Node.VALUE_LABEL_D_DS1, 1) - nodetemplate.setValueNumberOfVersions(coordinates, -1, Node.VALUE_LABEL_D_DS2, 1) - if useCrossDerivatives: - nodetemplate.setValueNumberOfVersions(coordinates, -1, Node.VALUE_LABEL_D2_DS1DS2, 1) - if useCubicHermiteThroughWall: - nodetemplate.setValueNumberOfVersions(coordinates, -1, Node.VALUE_LABEL_D_DS3, 1) - if useCrossDerivatives: - nodetemplate.setValueNumberOfVersions(coordinates, -1, Node.VALUE_LABEL_D2_DS1DS3, 1) - nodetemplate.setValueNumberOfVersions(coordinates, -1, Node.VALUE_LABEL_D2_DS2DS3, 1) - nodetemplate.setValueNumberOfVersions(coordinates, -1, Node.VALUE_LABEL_D3_DS1DS2DS3, 1) - - mesh = fm.findMeshByDimension(3) - - if useCubicHermiteThroughWall: - eftfactory = eftfactory_tricubichermite(mesh, useCrossDerivatives) - else: - eftfactory = eftfactory_bicubichermitelinear(mesh, useCrossDerivatives) - eft = eftfactory.createEftBasic() - - elementtemplate = mesh.createElementtemplate() - elementtemplate.setElementShapeType(Element.SHAPE_TYPE_CUBE) - result = elementtemplate.defineField(coordinates, -1, eft) - - # create nodes - nodeIdentifier = nextNodeIdentifier - x = [ 0.0, 0.0, 0.0 ] - dx_ds1 = [ 0.0, 0.0, 0.0 ] - dx_ds2 = [ 0.0, 0.0, 0.0 ] - dx_ds3 = [ 0.0, 0.0, 0.0 ] - xInnerList = [] - d1InnerList = [] - d2InnerList = [] - d3InnerUnitList = [] - xList = [] - dx_ds1List = [] - dx_ds2List = [] - dx_ds3List = [] - - # Map each face along segment profile to central line - for nSegment in range(segmentCountAlong): - for nAlongSegment in range(elementsCountAlongSegment+1): - n2 = nSegment*elementsCountAlongSegment + nAlongSegment - if nSegment == 0 or (nSegment > 0 and nAlongSegment > 0): - # Rotate to align segment axis with tangent of central line - segmentMid = [0.0, 0.0, segmentLength/elementsCountAlongSegment* nAlongSegment] - unitTangent = normalise(sd1[n2]) - cp = crossproduct3(segmentAxis, unitTangent) - if magnitude(cp)> 0.0: - axisRot = normalise(cp) - thetaRot = math.acos(dotproduct(segmentAxis, unitTangent)) - rotFrame = getRotationMatrixFromAxisAngle(axisRot, thetaRot) - midRot = [rotFrame[j][0]*segmentMid[0] + rotFrame[j][1]*segmentMid[1] + rotFrame[j][2]*segmentMid[2] for j in range(3)] - translateMatrix = [sx[n2][j] - midRot[j] for j in range(3)] - else: - midRot = segmentMid - - for n1 in range(elementsCountAround): - n = nAlongSegment*elementsCountAround + n1 - x = xInner[n] - d1 = d1Inner[n] - d2 = d2Inner[n] - if magnitude(cp)> 0.0: - xRot1 = [rotFrame[j][0]*x[0] + rotFrame[j][1]*x[1] + rotFrame[j][2]*x[2] for j in range(3)] - d1Rot1 = [rotFrame[j][0]*d1[0] + rotFrame[j][1]*d1[1] + rotFrame[j][2]*d1[2] for j in range(3)] - d2Rot1 = [rotFrame[j][0]*d2[0] + rotFrame[j][1]*d2[1] + rotFrame[j][2]*d2[2] for j in range(3)] - # Rotate to align first vector on face with binormal axis - if n1 == 0: - firstVector = normalise([xRot1[j] - midRot[j] for j in range(3)]) - thetaRot2 = math.acos(dotproduct(normalise(sBinormal[n2]), firstVector)) - cp2 = crossproduct3(normalise(sBinormal[n2]), firstVector) - if magnitude(cp2) > 0.0: - cp2 = normalise(cp2) - signThetaRot2 = dotproduct(unitTangent, cp2) - axisRot2 = unitTangent - rotFrame2 = getRotationMatrixFromAxisAngle(axisRot2, -signThetaRot2*thetaRot2) - else: - rotFrame2 = [ [1, 0, 0], [0, 1, 0], [0, 0, 1]] - xRot2 = [rotFrame2[j][0]*xRot1[0] + rotFrame2[j][1]*xRot1[1] + rotFrame2[j][2]*xRot1[2] for j in range(3)] - d1Rot2 = [rotFrame2[j][0]*d1Rot1[0] + rotFrame2[j][1]*d1Rot1[1] + rotFrame2[j][2]*d1Rot1[2] for j in range(3)] - d2Rot2 = [rotFrame2[j][0]*d2Rot1[0] + rotFrame2[j][1]*d2Rot1[1] + rotFrame2[j][2]*d2Rot1[2] for j in range(3)] - else: - xRot2 = x - d1Rot2 = d1 - d2Rot2 = d2 - - xTranslate = [xRot2[j] + translateMatrix[j] for j in range(3)] - - xInnerList.append(xTranslate) - d1InnerList.append(d1Rot2) - d2InnerList.append(d2Rot2) - d3Unit = normalise(crossproduct3(normalise(d1Rot2), normalise(d2Rot2))) - d3InnerUnitList.append(d3Unit) - - # Pre-calculate node locations and derivatives on outer boundary - xOuterList, curvatureInner = getOuterCoordinatesAndCurvatureFromInner(xInnerList, d1InnerList, d3InnerUnitList, wallThickness, elementsCountAlong, elementsCountAround) - - # Interpolate to get nodes through wall - for n3 in range(elementsCountThroughWall + 1): - xi3 = 1/elementsCountThroughWall * n3 - x, dx_ds1, dx_ds2, dx_ds3 = interpolatefromInnerAndOuter( xInnerList, xOuterList, wallThickness, xi3, curvatureInner, d1InnerList, d2InnerList, d3InnerUnitList, elementsCountAround, elementsCountAlong, elementsCountThroughWall) - xList = xList + x - dx_ds1List = dx_ds1List + dx_ds1 - dx_ds2List = dx_ds2List + dx_ds2 - dx_ds3List = dx_ds3List + dx_ds3 - - for n in range(len(xList)): - node = nodes.createNode(nodeIdentifier, nodetemplate) - cache.setNode(node) - coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_VALUE, 1, xList[n]) - coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS1, 1, dx_ds1List[n]) - coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS2, 1, dx_ds2List[n]) - coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS3, 1, dx_ds3List[n]) - if useCrossDerivatives: - coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D2_DS1DS2, 1, zero) - coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D2_DS1DS3, 1, zero) - coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D2_DS2DS3, 1, zero) - coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D3_DS1DS2DS3, 1, zero) - # print('NodeIdentifier = ', nodeIdentifier, xList[n]) - nodeIdentifier = nodeIdentifier + 1 - - # # For debugging - Nodes along central line - # for pt in range(len(sx)): - # node = nodes.createNode(nodeIdentifier, nodetemplate) - # cache.setNode(node) - # coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_VALUE, 1, sx[pt]) - # coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS1, 1, sd1[pt]) - # coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS2, 1, sNormal[pt]) - # coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS3, 1, sBinormal[pt]) - # nodeIdentifier = nodeIdentifier + 1 - - # create elements - elementIdentifier = nextElementIdentifier - now = (elementsCountAlong + 1)*elementsCountAround - for e3 in range(elementsCountThroughWall): - for e2 in range(elementsCountAlong): - for e1 in range(elementsCountAround): - element = mesh.createElement(elementIdentifier, elementtemplate) - bni11 = e3*now + e2*elementsCountAround + e1 + 1 - bni12 = e3*now + e2*elementsCountAround + (e1 + 1)%elementsCountAround + 1 - bni21 = e3*now + (e2 + 1)*elementsCountAround + e1 + 1 - bni22 = e3*now + (e2 + 1)*elementsCountAround + (e1 + 1)%elementsCountAround + 1 - nodeIdentifiers = [ bni11, bni12, bni21, bni22, bni11 + now, bni12 + now, bni21 + now, bni22 + now ] - result = element.setNodesByIdentifier(eft, nodeIdentifiers) - elementIdentifier = elementIdentifier + 1 - - fm.endChange() - - return annotationGroups, nodeIdentifier, elementIdentifier - -def getOuterCoordinatesAndCurvatureFromInner(xInner, d1Inner, d3Inner, wallThickness, elementsCountAlong, elementsCountAround): - """ - Generates coordinates on outer surface and curvature of inner - surface from coordinates and derivatives of inner surface using - wall thickness and normals. - param xInner: Coordinates on inner surface - param d1Inner: Derivatives on inner surface around tube - param d3Inner: Derivatives on inner surface through wall - param wallThickness: Thickness of wall - param elementsCountAlong: Number of elements along tube - param elementsCountAround: Number of elements around tube - return xOuter: Coordinates on outer surface - return curvatureInner: Curvature of coordinates on inner surface - """ - xOuter = [] - curvatureInner = [] - for n2 in range(elementsCountAlong + 1): - for n1 in range(elementsCountAround): - n = n2*elementsCountAround + n1 - x = [xInner[n][i] + d3Inner[n][i]*wallThickness for i in range(3)] - prevIdx = n-1 if (n1 != 0) else (n2+1)*elementsCountAround - 1 - nextIdx = n+1 if (n1 < elementsCountAround-1) else n2*elementsCountAround - norm = d3Inner[n] - curvatureAround = 0.5*( - getCubicHermiteCurvature(xInner[prevIdx], d1Inner[prevIdx], xInner[n], d1Inner[n], norm, 1.0) + - getCubicHermiteCurvature(xInner[n], d1Inner[n], xInner[nextIdx], d1Inner[nextIdx], norm, 0.0)) - xOuter.append(x) - curvatureInner.append(curvatureAround) - - return xOuter, curvatureInner - -def interpolatefromInnerAndOuter( xInner, xOuter, thickness, xi3, curvatureInner, d1Inner, d2Inner, d3InnerUnit, - elementsCountAround, elementsCountAlong, elementsCountThroughWall): - """ - Generate coordinates and derivatives at xi3 by interpolating with - inner and outer coordinates and derivatives. - param xInner: Coordinates on inner surface - param xOuter: Coordinates on outer surface - param thickness: Thickness of wall - param curvatureInner: Curvature of coordinates on inner surface - param d1Inner: Derivatives on inner surface around tube - param d2Inner: Derivatives on inner surface along tube - param d3InnerUnit: Unit derivatives on inner surface through wall - param elementsCountAround: Number of elements around tube - param elementsCountAlong: Number of elements along tube - param elementsCountThroughWall: Number of elements through wall - return xList, dx_ds1List, dx_ds2List, dx_ds3List: Coordinates and derivatives on xi3 - """ - xList = [] - dx_ds1List = [] - dx_ds2List = [] - dx_ds3List =[] - - for n2 in range(elementsCountAlong+1): - for n1 in range(elementsCountAround): - n = n2*elementsCountAround + n1 - norm = d3InnerUnit[n] - # x - innerx = xInner[n] - outerx = xOuter[n] - dWall = [thickness*c for c in norm] - x = interpolateCubicHermite(innerx, dWall, outerx, dWall, xi3) - xList.append(x) - # dx_ds1 - factor = 1.0 - curvatureInner[n]*thickness*xi3 - dx_ds1 = [ factor*c for c in d1Inner[n]] - dx_ds1List.append(dx_ds1) - # dx_ds2 - if n2 > 0 and n2 < elementsCountAlong: - prevIdx = (n2-1)*elementsCountAround + n1 - nextIdx = (n2+1)*elementsCountAround + n1 - curvatureAround = 0.5*( - getCubicHermiteCurvature(xInner[prevIdx], d2Inner[prevIdx], xInner[n], d2Inner[n], norm, 1.0)+ - getCubicHermiteCurvature(xInner[n], d2Inner[n], xInner[nextIdx], d2Inner[nextIdx], norm, 0.0)) - elif n2 == 0: - nextIdx = (n2+1)*elementsCountAround + n1 - curvatureAround = getCubicHermiteCurvature(xInner[n], d2Inner[n], xInner[nextIdx], d2Inner[nextIdx], norm, 0.0) - else: - prevIdx = (n2-1)*elementsCountAround + n1 - curvatureAround = getCubicHermiteCurvature(xInner[prevIdx], d2Inner[prevIdx], xInner[n], d2Inner[n], norm, 1.0) - - factor = 1.0 - curvatureAround*thickness*xi3 - dx_ds2 = [ factor*c for c in d2Inner[n]] - dx_ds2List.append(dx_ds2) - #dx_ds3 - dx_ds3 = [c * thickness/elementsCountThroughWall for c in norm] - dx_ds3List.append(dx_ds3) - - return xList, dx_ds1List, dx_ds2List, dx_ds3List From bae844ce9688a6153e81535800c2231d5cf7480d Mon Sep 17 00:00:00 2001 From: Mabelle Lin Date: Thu, 7 Mar 2019 14:36:21 +1300 Subject: [PATCH 33/44] Update function call to getRotationMatrixFromAxisAngle --- scaffoldmaker/meshtypes/meshtype_3d_haustra1.py | 17 ++--------------- 1 file changed, 2 insertions(+), 15 deletions(-) diff --git a/scaffoldmaker/meshtypes/meshtype_3d_haustra1.py b/scaffoldmaker/meshtypes/meshtype_3d_haustra1.py index bb913f2d..4cfcb558 100644 --- a/scaffoldmaker/meshtypes/meshtype_3d_haustra1.py +++ b/scaffoldmaker/meshtypes/meshtype_3d_haustra1.py @@ -4,6 +4,7 @@ variable radius and thickness along. """ from scaffoldmaker.meshtypes.scaffold_base import Scaffold_base +from scaffoldmaker.utils.matrix import * from scaffoldmaker.utils.meshrefinement import MeshRefinement from scaffoldmaker.utils.tubemesh import * @@ -299,7 +300,7 @@ def generateBaseMesh(region, options): elif elementsCountAlongHaustra == 3: # 3 elementsAlongHaustra axisRot = crossproduct3(unitTangent, unitZ) - rotFrame = rotationMatrixAboutAxis(axisRot, math.pi/2) + rotFrame = getRotationMatrixFromAxisAngle(axisRot, math.pi/2) rotNormal = [rotFrame[j][0]*unitTangent[0] + rotFrame[j][1]*unitTangent[1] + rotFrame[j][2]*unitTangent[2] for j in range(3)] unitdx_ds3 = normalise(rotNormal) unitdx_ds1 = crossproduct3(unitTangent, unitdx_ds3) @@ -495,17 +496,3 @@ def interpolatefromInnerAndOuter( xInner, xOuter, thickness, xi3, curvatureInner return xList, dx_ds1List, dx_ds2List, dx_ds3List -def rotationMatrixAboutAxis(rotAxis, theta): - """ - Generate the rotation matrix for rotation about an axis. - :param rotAxis: axis of rotation - :param theta: angle of rotation - :return: rotation matrix - """ - cosTheta = math.cos(theta) - sinTheta = math.sin(theta) - C = 1 - cosTheta - rotMatrix = ([[rotAxis[0]*rotAxis[0]*C + cosTheta, rotAxis[0]*rotAxis[1]*C - rotAxis[2]*sinTheta, rotAxis[0]*rotAxis[2]*C + rotAxis[1]*sinTheta], - [rotAxis[1]*rotAxis[0]*C + rotAxis[2]*sinTheta, rotAxis[1]*rotAxis[1]*C + cosTheta, rotAxis[1]*rotAxis[2]*C - rotAxis[0]*sinTheta], - [rotAxis[2]*rotAxis[0]*C - rotAxis[1]*sinTheta, rotAxis[2]*rotAxis[1]*C + rotAxis[0]*sinTheta, rotAxis[2]*rotAxis[2]*C + cosTheta]]) - return rotMatrix \ No newline at end of file From d477b28ec44d42645799307d2d18ee38e57ddc43 Mon Sep 17 00:00:00 2001 From: Mabelle Lin Date: Thu, 7 Mar 2019 14:41:47 +1300 Subject: [PATCH 34/44] Make sure newline at end of script --- scaffoldmaker/meshtypes/meshtype_3d_haustra1.py | 1 - 1 file changed, 1 deletion(-) diff --git a/scaffoldmaker/meshtypes/meshtype_3d_haustra1.py b/scaffoldmaker/meshtypes/meshtype_3d_haustra1.py index 4cfcb558..12a399fe 100644 --- a/scaffoldmaker/meshtypes/meshtype_3d_haustra1.py +++ b/scaffoldmaker/meshtypes/meshtype_3d_haustra1.py @@ -495,4 +495,3 @@ def interpolatefromInnerAndOuter( xInner, xOuter, thickness, xi3, curvatureInner dx_ds3List.append(dx_ds3) return xList, dx_ds1List, dx_ds2List, dx_ds3List - From e822e354a03f00600bc8444e6329f7ca3aba5e7f Mon Sep 17 00:00:00 2001 From: Mabelle Lin Date: Thu, 7 Mar 2019 16:06:58 +1300 Subject: [PATCH 35/44] Update review changes to haustra scaffold --- .../meshtypes/meshtype_3d_haustra1.py | 547 +++++++++--------- 1 file changed, 281 insertions(+), 266 deletions(-) diff --git a/scaffoldmaker/meshtypes/meshtype_3d_haustra1.py b/scaffoldmaker/meshtypes/meshtype_3d_haustra1.py index 12a399fe..5dab2f81 100644 --- a/scaffoldmaker/meshtypes/meshtype_3d_haustra1.py +++ b/scaffoldmaker/meshtypes/meshtype_3d_haustra1.py @@ -1,7 +1,7 @@ """ -Generates a 3-D haustra mesh along a central line, with variable -numbers of elements around, along and through wall, with -variable radius and thickness along. +Generates a single 3-D haustra segment mesh along a central +line, with variable numbers of elements around, along and +through wall, with variable radius and thickness along. """ from scaffoldmaker.meshtypes.scaffold_base import Scaffold_base from scaffoldmaker.utils.matrix import * @@ -10,10 +10,11 @@ class MeshType_3d_haustra1(Scaffold_base): ''' - Generates a 3-D haustra mesh with variable numbers - of elements around, along the central line, and through wall. - The haustra has a triangular profile with rounded corners at the - interhaustral septa, and a clover profile outside the septa. + Generates a single 3-D haustra segment mesh with variable + numbers of elements around, along the central line, and + through wall. The haustra segment has a triangular profile + with rounded corners at the inter-haustral septa, and a + clover profile in the intra-haustral region. ''' @staticmethod def getName(): @@ -23,21 +24,20 @@ def getName(): def getDefaultOptions(parameterSetName='Default'): return { 'Number of elements around' : 9, - 'Number of elements along haustra' : 3, + 'Number of elements along haustrum' : 3, 'Number of elements through wall' : 1, 'Inner radius': 0.5, - 'Corner radius fraction': 0.5, + 'Corner inner radius factor': 0.5, + 'Haustra inner radius factor': 0.5, + 'Haustrum segment end-derivative factor': 0.5, + 'Haustrum segment mid-derivative factor': 1.0, 'Wall thickness': 0.01, - 'Number of haustra': 1, - 'Haustra radius fraction': 0.5, - 'Haustra length': 1.0, - 'Interhaustra fold factor': 0.5, - 'Haustra curvature factor': 1.0, + 'Haustra segment length': 1.0, 'Use cross derivatives' : False, 'Use linear through wall' : False, 'Refine' : False, 'Refine number of elements around' : 1, - 'Refine number of elements along' : 1, + 'Refine number of elements along haustrum' : 1, 'Refine number of elements through wall' : 1 } @@ -45,21 +45,20 @@ def getDefaultOptions(parameterSetName='Default'): def getOrderedOptionNames(): return [ 'Number of elements around', - 'Number of elements along haustra', + 'Number of elements along haustrum', 'Number of elements through wall', 'Inner radius', - 'Corner radius fraction', + 'Corner inner radius factor', + 'Haustra inner radius factor', + 'Haustrum segment end-derivative factor', + 'Haustrum segment mid-derivative factor', 'Wall thickness', - 'Number of haustra', - 'Haustra radius fraction', - 'Haustra length', - 'Interhaustra fold factor', - 'Haustra curvature factor', - 'Use cross derivatives', + 'Haustra segment length', + 'Use cross derivatives', 'Use linear through wall', 'Refine', 'Refine number of elements around', - 'Refine number of elements along', + 'Refine number of elements along haustrum', 'Refine number of elements through wall' ] @@ -67,9 +66,8 @@ def getOrderedOptionNames(): def checkOptions(options): for key in [ 'Number of elements through wall', - 'Number of haustra', 'Refine number of elements around', - 'Refine number of elements along', + 'Refine number of elements along haustrum', 'Refine number of elements through wall']: if options[key] < 1: options[key] = 1 @@ -77,21 +75,22 @@ def checkOptions(options): options['Number of elements around'] = 9 if (options['Number of elements around'] % 3 != 0) : options['Number of elements around'] = (options['Number of elements around']//3)*3 + if (options['Number of elements along haustrum'] < 2) : + options['Number of elements along haustrum'] = 2 for key in [ 'Inner radius', - 'Corner radius fraction', + 'Haustra inner radius factor', + 'Haustrum segment end-derivative factor', + 'Haustrum segment mid-derivative factor', 'Wall thickness', - 'Haustra radius fraction', - 'Haustra length', - 'Interhaustra fold factor', - 'Haustra curvature factor']: + 'Haustra segment length']: if options[key] < 0.0: options[key] = 0.0 - if options['Corner radius fraction'] < 0.1: - options['Corner radius fraction'] = 0.1 + if options['Corner inner radius factor'] < 0.1: + options['Corner inner radius factor'] = 0.1 for key in [ - 'Corner radius fraction', - 'Interhaustra fold factor']: + 'Corner inner radius factor', + 'Haustrum segment end-derivative factor']: if options[key] > 1.0: options[key] = 1.0 @@ -104,19 +103,26 @@ def generateBaseMesh(region, options): :return: None """ elementsCountAround = options['Number of elements around'] - elementsCountAlongHaustra = options['Number of elements along haustra'] + elementsCountAlongHaustrum = options['Number of elements along haustrum'] elementsCountThroughWall = options['Number of elements through wall'] radius = options['Inner radius'] - cornerRadiusFraction = options['Corner radius fraction'] + cornerInnerRadiusFactor = options['Corner inner radius factor'] + haustraInnerRadiusFactor = options['Haustra inner radius factor'] + haustrumSegmentEndDerivativeFactor = options['Haustrum segment end-derivative factor'] + haustrumSegmentMidDerivativeFactor = options['Haustrum segment mid-derivative factor'] wallThickness = options['Wall thickness'] - haustraCount = options['Number of haustra'] - haustraRadiusFraction = options['Haustra radius fraction'] - haustraLength = options['Haustra length'] - foldFactor = options['Interhaustra fold factor'] - haustraCurvatureFactor = options['Haustra curvature factor'] + haustraSegmentLength = options['Haustra segment length'] useCrossDerivatives = options['Use cross derivatives'] useCubicHermiteThroughWall = not(options['Use linear through wall']) - elementsCountAlong = int(elementsCountAlongHaustra*haustraCount) + elementsCountAlong = elementsCountAlongHaustrum + + nodeIdentifier = 1 + zero = [0.0, 0.0, 0.0] + d3InnerUnitList = [] + xList = [] + dx_ds1List = [] + dx_ds2List = [] + dx_ds3List = [] fm = region.getFieldmodule() fm.beginChange() @@ -150,205 +156,25 @@ def generateBaseMesh(region, options): elementtemplate.setElementShapeType(Element.SHAPE_TYPE_CUBE) result = elementtemplate.defineField(coordinates, -1, eft) - # create nodes - nodeIdentifier = 1 #nextNodeIdentifier - zero = [ 0.0, 0.0, 0.0 ] - x = [ 0.0, 0.0, 0.0 ] - dx_ds1 = [ 0.0, 0.0, 0.0 ] - dx_ds2 = [ 0.0, 0.0, 0.0 ] - dx_ds3 = [ 0.0, 0.0, 0.0 ] - radiansRangeRC = [7*math.pi/4, 0.0, math.pi/4] - cornerRC = cornerRadiusFraction*radius - unitZ = [0.0, 0.0, 1.0] - haustraRadius = (haustraRadiusFraction + 1)*radius - xAround = [] - d1Around = [] - xInner = [] - dx_ds1InnerList = [] - xHaustraSide = [] - xHaustraInner = [] - d1InnerHaustraRaw = [] - xInnerRaw = [] - dx_ds2InnerRaw = [] - xInnerList = [] - dx_ds2InnerList = [] - dx_ds3InnerUnitList = [] - xList = [] - dx_ds1List = [] - dx_ds2List = [] - dx_ds3List = [] - - # Pre-calculate node locations and derivatives on inner triangle - for n1 in range(3): - radiansAround = n1*2*math.pi / 3 - cosRadiansAround = math.cos(radiansAround) - sinRadiansAround = math.sin(radiansAround) - xc = [(radius - cornerRC) * cosRadiansAround, (radius - cornerRC) * sinRadiansAround, 0.0] - - for n in range(3): - radiansRC = radiansAround + radiansRangeRC[n] - cosRadiansRC = math.cos(radiansRC) - sinRadiansRC = math.sin(radiansRC) - x = [xc[0] + cornerRC*cosRadiansRC, xc[1] + cornerRC*sinRadiansRC, 0.0] - xAround.append(x) - d1 = [ cornerRC*math.pi/4 * -sinRadiansRC, cornerRC*math.pi/4 * cosRadiansRC, 0.0] - d1Around.append(d1) - - xSample = xAround[1:9] - xSample.append(xAround[0]) - xSample.append(xAround[1]) - d1Sample = d1Around[1:9] - d1Sample.append(d1Around[0]) - d1Sample.append(d1Around[1]) - sx, sd1, se, sxi, _= sampleCubicHermiteCurves(xSample, d1Sample, elementsCountAround) - xInner = xInner + sx[0:-1] - d1Inner = smoothCubicHermiteDerivativesLoop(sx[0:-1], sd1[0:-1]) - - # Pre-calculate node locations and derivatives on haustra inner cross-section - elementsCountAroundSide = int(elementsCountAround/3) - Ax = xInner[elementsCountAroundSide][0] - Ay = xInner[elementsCountAroundSide][1] - originRC = (Ax*Ax + Ay*Ay - haustraRadius*haustraRadius) / (2*(-Ax - haustraRadius)) - RC = haustraRadius - originRC - - if originRC > -Ax: - startTheta = math.asin(Ay/RC) - thetaRC = (math.pi - startTheta)*2 - else: - startTheta = math.pi - math.asin(Ay/RC) - thetaRC = math.asin(Ay/RC)*2 - thetaPerElementAround = thetaRC/(elementsCountAround/3) - - for n in range(elementsCountAroundSide + 1): - theta = startTheta + thetaPerElementAround * n - x = [RC*math.cos(theta) - originRC, - RC*math.sin(theta), - 0.0] - xHaustraSide.append(x) - - ang = [-2/3*math.pi, 0.0, 2/3*math.pi] - - for i in range(3): - rotAng = ang[i] - cosRotAng = math.cos(rotAng) - sinRotAng = math.sin(rotAng) - for n in range(elementsCountAroundSide): - theta = startTheta + thetaPerElementAround * n - cosTheta = math.cos(theta) - sinTheta = math.sin(theta) - x = [ (RC*cosTheta - originRC)*cosRotAng - RC*sinTheta*sinRotAng, - (RC*cosTheta - originRC)*sinRotAng + RC*sinTheta*cosRotAng, - 0.0] - xHaustraInner.append(x) - dx_ds1 = [(-RC*sinTheta*cosRotAng - RC*cosTheta*sinRotAng)*thetaPerElementAround, - (-RC*sinTheta*sinRotAng + RC*cosTheta*cosRotAng)*thetaPerElementAround, - 0.0] - d1InnerHaustraRaw.append(dx_ds1) - d1InnerHaustra = smoothCubicHermiteDerivativesLoop(xHaustraInner, d1InnerHaustraRaw) - - # Sample arclength of haustra - for n1 in range(elementsCountAround): - if n1%(elementsCountAround/3) > 0.0: - v1 = [xInner[n1][0], xInner[n1][1], 0.0] - startArcLength = foldFactor * haustraLength - d1 = [ c*startArcLength for c in unitZ] - v2 = [xHaustraInner[n1][0], xHaustraInner[n1][1], haustraLength/2] - midArcLength = haustraCurvatureFactor * haustraLength - d2 = [ c*midArcLength for c in unitZ] - v3 = [xInner[n1][0], xInner[n1][1], haustraLength] - d3 = [ c*startArcLength for c in unitZ] - else: - v1 = [xInner[n1][0], xInner[n1][1], 0.0] - v2 = [xInner[n1][0], xInner[n1][1], haustraLength/2] - v3 = [xInner[n1][0], xInner[n1][1], haustraLength] - d3 = d2 = d1 = [c* haustraLength/3 for c in unitZ] - nx = [v1, v2, v3] - nd1 = [d1, d2, d3] - sx, sd1, se, sxi, _ = sampleCubicHermiteCurves(nx, nd1, elementsCountAlongHaustra) - xInnerRaw.append(sx) - dx_ds2InnerRaw.append(sd1) - - # Re-arrange sample order & calculate dx_ds1 and dx_ds3 from dx_ds2 - dx_ds1InnerList = dx_ds1InnerList + d1Inner - for n2 in range(elementsCountAlongHaustra + 1): - xAround = [] - unitdx_ds1Around = [] - for n1 in range(elementsCountAround): - x = xInnerRaw[n1][n2] - xInnerList.append(x) - dx_ds2 = dx_ds2InnerRaw[n1][n2] - dx_ds2InnerList.append(dx_ds2) - - unitTangent = normalise(dx_ds2) - # Interhaustra - if n2 == 0 or n2 > elementsCountAlongHaustra - 1: - dx_ds1 = d1Inner[n1] - unitdx_ds3 = crossproduct3(normalise(dx_ds1), unitTangent) - else: - # Haustra - if elementsCountAlongHaustra == 2: - unitdx_ds1 = normalise(d1InnerHaustra[n1]) - else: - if n1%(elementsCountAround/3) == 0: # intersection points - unitdx_ds1 = normalise(d1InnerHaustra[n1]) - else: # points on clover - if elementsCountAlongHaustra > 3: - if n2 < int(elementsCountAlongHaustra/2): # first half of haustraLength - axisRot = crossproduct3(unitZ, unitTangent) - elif n2 > int(elementsCountAlongHaustra/2): # second half of haustraLength - axisRot = crossproduct3(unitTangent, unitZ) - elif elementsCountAlongHaustra == 3: # 3 elementsAlongHaustra - axisRot = crossproduct3(unitTangent, unitZ) - - rotFrame = getRotationMatrixFromAxisAngle(axisRot, math.pi/2) - rotNormal = [rotFrame[j][0]*unitTangent[0] + rotFrame[j][1]*unitTangent[1] + rotFrame[j][2]*unitTangent[2] for j in range(3)] - unitdx_ds3 = normalise(rotNormal) - unitdx_ds1 = crossproduct3(unitTangent, unitdx_ds3) - xAround.append(x) - unitdx_ds1Around.append(unitdx_ds1) - - if n2 > 0 and n2 < elementsCountAlongHaustra: - dx_ds1InnerAroundList = [] - for n1 in range(elementsCountAround): - v1 = xAround[n1] - d1 = unitdx_ds1Around[n1] - v2 = xAround[(n1+1)%elementsCountAround] - d2 = unitdx_ds1Around[(n1+1)%elementsCountAround] - arcLengthAround = computeCubicHermiteArcLength(v1, d1, v2, d2, True) - dx_ds1 = [c*arcLengthAround for c in d1] - dx_ds1InnerAroundList.append(dx_ds1) - d1Smoothed = smoothCubicHermiteDerivativesLoop(xAround, dx_ds1InnerAroundList) - dx_ds1InnerList = dx_ds1InnerList + d1Smoothed - - dx_ds1InnerList = dx_ds1InnerList + d1Inner + xInnerList, d1InnerList, d2InnerList, haustraSegmentAxis = getColonHaustraSegmentInnerPoints(elementsCountAround, elementsCountAlongHaustrum, radius, cornerInnerRadiusFactor, + haustraInnerRadiusFactor, haustrumSegmentEndDerivativeFactor, haustrumSegmentMidDerivativeFactor, haustraSegmentLength) for n in range(len(xInnerList)): - dx_ds3 = crossproduct3(normalise(dx_ds1InnerList[n]), normalise( dx_ds2InnerList[n])) + dx_ds3 = crossproduct3(normalise(d1InnerList[n]), normalise(d2InnerList[n])) unitdx_ds3 = normalise(dx_ds3) - dx_ds3InnerUnitList.append(unitdx_ds3) + d3InnerUnitList.append(unitdx_ds3) # Pre-calculate node locations and derivatives on outer boundary - xOuterList, d1Outer, curvatureInner = getOuterCoordinatesAndDerivativesFromInner(xInnerList, dx_ds1InnerList, dx_ds3InnerUnitList, wallThickness, elementsCountAlongHaustra, elementsCountAround) + xOuterList, curvatureInner = getOuterCoordinatesAndCurvatureFromInner(xInnerList, d1InnerList, d3InnerUnitList, wallThickness, elementsCountAlong, elementsCountAround) # Interpolate to get nodes through wall for n3 in range(elementsCountThroughWall + 1): xi3 = 1/elementsCountThroughWall * n3 - for nH in range(haustraCount): - if nH == 0: - x, dx_ds1, dx_ds2, dx_ds3 = interpolatefromInnerAndOuter( xInnerList, xOuterList, wallThickness, xi3, curvatureInner, dx_ds1InnerList, dx_ds2InnerList, dx_ds3InnerUnitList, elementsCountAround, elementsCountAlongHaustra, elementsCountThroughWall) - xList = xList + x - dx_ds1List = dx_ds1List + dx_ds1 - dx_ds2List = dx_ds2List + dx_ds2 - dx_ds3List = dx_ds3List + dx_ds3 - else: - xNext = [] - for n in range(elementsCountAround, len(x)): - newX = [x[n][0], x[n][1], x[n][2] + haustraLength*nH] - xNext.append(newX) - xList = xList + xNext - dx_ds1List = dx_ds1List + dx_ds1[elementsCountAround:] - dx_ds2List = dx_ds2List + dx_ds2[elementsCountAround:] - dx_ds3List = dx_ds3List + dx_ds3[elementsCountAround:] + x, dx_ds1, dx_ds2, dx_ds3 = interpolatefromInnerAndOuter( xInnerList, xOuterList, wallThickness, xi3, curvatureInner, d1InnerList, d2InnerList, d3InnerUnitList, elementsCountAround, elementsCountAlong, elementsCountThroughWall) + xList = xList + x + dx_ds1List = dx_ds1List + dx_ds1 + dx_ds2List = dx_ds2List + dx_ds2 + dx_ds3List = dx_ds3List + dx_ds3 for n in range(len(xList)): node = nodes.createNode(nodeIdentifier, nodetemplate) @@ -403,43 +229,231 @@ def generateMesh(cls, region, options): meshrefinement = MeshRefinement(baseRegion, region) meshrefinement.refineAllElementsCubeStandard3d(refineElementsCountAround, refineElementsCountAlong, refineElementsCountThroughWall) -def getOuterCoordinatesAndDerivativesFromInner(xInner, d1Inner, d3Inner, wallThickness, elementsCountAlongHaustra, elementsCountAround): +def getColonHaustraSegmentInnerPoints(elementsCountAround, elementsCountAlongHaustrum, radius, cornerInnerRadiusFactor, + haustraInnerRadiusFactor, haustrumSegmentEndDerivativeFactor, haustrumSegmentMidDerivativeFactor, haustraSegmentLength): """ - Generates coordinates and derivatives of outer surface from - coordinates and derivatives of inner surface using wall thickness - and normals. + Generates a 3-D haustra segment mesh with variable numbers + of elements around, along the central line, and through wall. + The haustra segment has a triangular profile with rounded corners + at the inter-haustral septa, and a clover profile in the intra-haustral + region. + :param elementsCountAround: Number of elements around haustra. + :param elementsCountAlongHaustrum: Number of elements along haustrum. + :param radius: Inner radius defined from center of triangular + profile to vertex of the triangle. + :param cornerInnerRadiusFactor: Roundness of triangular corners of + interhaustral septa. Factor is multiplied by inner radius + to get a radius of curvature at the corners. + :param haustraInnerRadiusFactor: Factor is multiplied by inner + radius to obtain radius of intersecting circles in the middle cross-section + along a haustra segment. + :param haustrumSegmentEndDerivativeFactor: Factor is multiplied by haustra + length to scale derivative along the end of a haustra segment length. + :param haustrumSegmentMidDerivativeFactor: Factor is multiplied by haustra + length to scale derivative along the mid length of the haustra segment. + :param haustraSegmentLength: Length of a haustra segment. + :return: coordinates, derivatives on inner surface of haustra segment. + """ + + # create nodes + x = [ 0.0, 0.0, 0.0 ] + dx_ds1 = [ 0.0, 0.0, 0.0 ] + dx_ds2 = [ 0.0, 0.0, 0.0 ] + dx_ds3 = [ 0.0, 0.0, 0.0 ] + radiansRangeRC = [7*math.pi/4, 0.0, math.pi/4] + cornerRC = cornerInnerRadiusFactor*radius + unitZ = [0.0, 0.0, 1.0] + haustraRadius = (haustraInnerRadiusFactor + 1)*radius + xAround = [] + d1Around = [] + xInner = [] + dx_ds1InnerList = [] + xHaustraSide = [] + xHaustraInner = [] + d1InnerHaustraRaw = [] + xInnerRaw = [] + dx_ds2InnerRaw = [] + xInnerList = [] + dx_ds2InnerList = [] + + # Pre-calculate node locations and derivatives on inner triangle + for n1 in range(3): + radiansAround = n1*2*math.pi / 3 + cosRadiansAround = math.cos(radiansAround) + sinRadiansAround = math.sin(radiansAround) + xc = [(radius - cornerRC) * cosRadiansAround, (radius - cornerRC) * sinRadiansAround, 0.0] + + for n in range(3): + radiansRC = radiansAround + radiansRangeRC[n] + cosRadiansRC = math.cos(radiansRC) + sinRadiansRC = math.sin(radiansRC) + x = [xc[0] + cornerRC*cosRadiansRC, xc[1] + cornerRC*sinRadiansRC, 0.0] + xAround.append(x) + d1 = [ cornerRC*math.pi/4 * -sinRadiansRC, cornerRC*math.pi/4 * cosRadiansRC, 0.0] + d1Around.append(d1) + + xSample = xAround[1:9] + xSample.append(xAround[0]) + xSample.append(xAround[1]) + d1Sample = d1Around[1:9] + d1Sample.append(d1Around[0]) + d1Sample.append(d1Around[1]) + sx, sd1, se, sxi, _= sampleCubicHermiteCurves(xSample, d1Sample, elementsCountAround) + xInner = xInner + sx[0:-1] + d1Inner = smoothCubicHermiteDerivativesLoop(sx[0:-1], sd1[0:-1]) + + # Pre-calculate node locations and derivatives on haustra inner cross-section + elementsCountAroundSide = int(elementsCountAround/3) + Ax = xInner[elementsCountAroundSide][0] + Ay = xInner[elementsCountAroundSide][1] + originRC = (Ax*Ax + Ay*Ay - haustraRadius*haustraRadius) / (2*(-Ax - haustraRadius)) + RC = haustraRadius - originRC + + if originRC > -Ax: + startTheta = math.asin(Ay/RC) + thetaRC = (math.pi - startTheta)*2 + else: + startTheta = math.pi - math.asin(Ay/RC) + thetaRC = math.asin(Ay/RC)*2 + thetaPerElementAround = thetaRC/(elementsCountAround/3) + + for n in range(elementsCountAroundSide + 1): + theta = startTheta + thetaPerElementAround * n + x = [RC*math.cos(theta) - originRC, + RC*math.sin(theta), + 0.0] + xHaustraSide.append(x) + + ang = [-2/3*math.pi, 0.0, 2/3*math.pi] + + for i in range(3): + rotAng = ang[i] + cosRotAng = math.cos(rotAng) + sinRotAng = math.sin(rotAng) + for n in range(elementsCountAroundSide): + theta = startTheta + thetaPerElementAround * n + cosTheta = math.cos(theta) + sinTheta = math.sin(theta) + x = [ (RC*cosTheta - originRC)*cosRotAng - RC*sinTheta*sinRotAng, + (RC*cosTheta - originRC)*sinRotAng + RC*sinTheta*cosRotAng, + 0.0] + xHaustraInner.append(x) + dx_ds1 = [(-RC*sinTheta*cosRotAng - RC*cosTheta*sinRotAng)*thetaPerElementAround, + (-RC*sinTheta*sinRotAng + RC*cosTheta*cosRotAng)*thetaPerElementAround, + 0.0] + d1InnerHaustraRaw.append(dx_ds1) + d1InnerHaustra = smoothCubicHermiteDerivativesLoop(xHaustraInner, d1InnerHaustraRaw) + + # Sample arclength of haustra segment + for n1 in range(elementsCountAround): + if n1%(elementsCountAround/3) > 0.0: + v1 = [xInner[n1][0], xInner[n1][1], 0.0] + startArcLength = haustrumSegmentEndDerivativeFactor * haustraSegmentLength + d1 = [ c*startArcLength for c in unitZ] + v2 = [xHaustraInner[n1][0], xHaustraInner[n1][1], haustraSegmentLength/2] + midArcLength = haustrumSegmentMidDerivativeFactor * haustraSegmentLength + d2 = [ c*midArcLength for c in unitZ] + v3 = [xInner[n1][0], xInner[n1][1], haustraSegmentLength] + d3 = [ c*startArcLength for c in unitZ] + else: + v1 = [xInner[n1][0], xInner[n1][1], 0.0] + v2 = [xInner[n1][0], xInner[n1][1], haustraSegmentLength/2] + v3 = [xInner[n1][0], xInner[n1][1], haustraSegmentLength] + d3 = d2 = d1 = [c* haustraSegmentLength/3 for c in unitZ] + nx = [v1, v2, v3] + nd1 = [d1, d2, d3] + sx, sd1, se, sxi, _ = sampleCubicHermiteCurves(nx, nd1, elementsCountAlongHaustrum) + xInnerRaw.append(sx) + dx_ds2InnerRaw.append(sd1) + + # Re-arrange sample order & calculate dx_ds1 and dx_ds3 from dx_ds2 + dx_ds1InnerList = dx_ds1InnerList + d1Inner + for n2 in range(elementsCountAlongHaustrum + 1): + xAround = [] + unitdx_ds1Around = [] + for n1 in range(elementsCountAround): + x = xInnerRaw[n1][n2] + xInnerList.append(x) + dx_ds2 = dx_ds2InnerRaw[n1][n2] + dx_ds2InnerList.append(dx_ds2) + unitTangent = normalise(dx_ds2) + # Inter-haustra + if n2 == 0 or n2 > elementsCountAlongHaustrum - 1: + dx_ds1 = d1Inner[n1] + unitdx_ds3 = crossproduct3(normalise(dx_ds1), unitTangent) + else: + # Intra-Haustra + if elementsCountAlongHaustrum == 2: + unitdx_ds1 = normalise(d1InnerHaustra[n1]) + else: + if n1%(elementsCountAround/3) == 0: # intersection points + unitdx_ds1 = normalise(d1InnerHaustra[n1]) + else: # points on clover + if elementsCountAlongHaustrum > 3: + if n2 < int(elementsCountAlongHaustrum/2): # first half of haustraSegmentLength + axisRot = crossproduct3(unitZ, unitTangent) + elif n2 > int(elementsCountAlongHaustrum/2): # second half of haustraSegmentLength + axisRot = crossproduct3(unitTangent, unitZ) + elif elementsCountAlongHaustrum == 3: # 3 elementsAlongHaustrum + axisRot = crossproduct3(unitTangent, unitZ) + + rotFrame = getRotationMatrixFromAxisAngle(axisRot, math.pi/2) + rotNormal = [rotFrame[j][0]*unitTangent[0] + rotFrame[j][1]*unitTangent[1] + rotFrame[j][2]*unitTangent[2] for j in range(3)] + unitdx_ds3 = normalise(rotNormal) + unitdx_ds1 = crossproduct3(unitTangent, unitdx_ds3) + xAround.append(x) + unitdx_ds1Around.append(unitdx_ds1) + + if n2 > 0 and n2 < elementsCountAlongHaustrum: + dx_ds1InnerAroundList = [] + for n1 in range(elementsCountAround): + v1 = xAround[n1] + d1 = unitdx_ds1Around[n1] + v2 = xAround[(n1+1)%elementsCountAround] + d2 = unitdx_ds1Around[(n1+1)%elementsCountAround] + arcLengthAround = computeCubicHermiteArcLength(v1, d1, v2, d2, True) + dx_ds1 = [c*arcLengthAround for c in d1] + dx_ds1InnerAroundList.append(dx_ds1) + d1Smoothed = smoothCubicHermiteDerivativesLoop(xAround, dx_ds1InnerAroundList) + dx_ds1InnerList = dx_ds1InnerList + d1Smoothed + + dx_ds1InnerList = dx_ds1InnerList + d1Inner + + return xInnerList, dx_ds1InnerList, dx_ds2InnerList, unitZ + +def getOuterCoordinatesAndCurvatureFromInner(xInner, d1Inner, d3Inner, wallThickness, elementsCountAlong, elementsCountAround): + """ + Generates coordinates on outer surface and curvature of inner + surface from coordinates and derivatives of inner surface using + wall thickness and normals. param xInner: Coordinates on inner surface - param d1Inner: Derivatives on inner surface around colon + param d1Inner: Derivatives on inner surface around tube param d3Inner: Derivatives on inner surface through wall param wallThickness: Thickness of wall - param elementsCountAlongHaustra: Number of elements along haustra - param elementsCountAround: Number of elements around colon + param elementsCountAlong: Number of elements along tube + param elementsCountAround: Number of elements around tube return xOuter: Coordinates on outer surface - return nd1: Derivatives on outer surface around colon return curvatureInner: Curvature of coordinates on inner surface """ xOuter = [] - nd1 = [] - dWall = [] curvatureInner = [] - for n2 in range(elementsCountAlongHaustra + 1): + for n2 in range(elementsCountAlong + 1): for n1 in range(elementsCountAround): n = n2*elementsCountAround + n1 x = [xInner[n][i] + d3Inner[n][i]*wallThickness for i in range(3)] prevIdx = n-1 if (n1 != 0) else (n2+1)*elementsCountAround - 1 nextIdx = n+1 if (n1 < elementsCountAround-1) else n2*elementsCountAround + norm = d3Inner[n] curvatureAround = 0.5*( - getCubicHermiteCurvature(xInner[prevIdx], d1Inner[prevIdx], xInner[n], d1Inner[n], d3Inner[n], 1.0) + - getCubicHermiteCurvature(xInner[n], d1Inner[n], xInner[nextIdx], d1Inner[nextIdx], d3Inner[n], 0.0)) - factor = 1.0 - curvatureAround*wallThickness - nd1Outer = [ factor*c for c in d1Inner[n]] + getCubicHermiteCurvature(xInner[prevIdx], d1Inner[prevIdx], xInner[n], d1Inner[n], norm, 1.0) + + getCubicHermiteCurvature(xInner[n], d1Inner[n], xInner[nextIdx], d1Inner[nextIdx], norm, 0.0)) xOuter.append(x) - nd1.append(nd1Outer) curvatureInner.append(curvatureAround) - return xOuter, nd1, curvatureInner - -def interpolatefromInnerAndOuter( xInner, xOuter, thickness, xi3, curvatureInner, d1Inner, d2Inner, d3InnerUnit, elementsCountAround, elementsCountAlongHaustra, elementsCountThroughWall): + return xOuter, curvatureInner + +def interpolatefromInnerAndOuter( xInner, xOuter, thickness, xi3, curvatureInner, d1Inner, d2Inner, d3InnerUnit, + elementsCountAround, elementsCountAlong, elementsCountThroughWall): """ Generate coordinates and derivatives at xi3 by interpolating with inner and outer coordinates and derivatives. @@ -447,11 +461,11 @@ def interpolatefromInnerAndOuter( xInner, xOuter, thickness, xi3, curvatureInner param xOuter: Coordinates on outer surface param thickness: Thickness of wall param curvatureInner: Curvature of coordinates on inner surface - param d1Inner: Derivatives on inner surface around colon - param d2Inner: Derivatives on inner surface along haustra + param d1Inner: Derivatives on inner surface around tube + param d2Inner: Derivatives on inner surface along tube param d3InnerUnit: Unit derivatives on inner surface through wall - param elementsCountAround: Number of elements around colon - param elementsCountAlongHaustra: Number of elements along haustra + param elementsCountAround: Number of elements around tube + param elementsCountAlong: Number of elements along tube param elementsCountThroughWall: Number of elements through wall return xList, dx_ds1List, dx_ds2List, dx_ds3List: Coordinates and derivatives on xi3 """ @@ -460,13 +474,14 @@ def interpolatefromInnerAndOuter( xInner, xOuter, thickness, xi3, curvatureInner dx_ds2List = [] dx_ds3List =[] - for n2 in range(elementsCountAlongHaustra+1): + for n2 in range(elementsCountAlong+1): for n1 in range(elementsCountAround): n = n2*elementsCountAround + n1 + norm = d3InnerUnit[n] # x innerx = xInner[n] outerx = xOuter[n] - dWall = [thickness*c for c in d3InnerUnit[n]] + dWall = [thickness*c for c in norm] x = interpolateCubicHermite(innerx, dWall, outerx, dWall, xi3) xList.append(x) # dx_ds1 @@ -474,24 +489,24 @@ def interpolatefromInnerAndOuter( xInner, xOuter, thickness, xi3, curvatureInner dx_ds1 = [ factor*c for c in d1Inner[n]] dx_ds1List.append(dx_ds1) # dx_ds2 - norm = d3InnerUnit[n] - if n2 > 0 and n2 < elementsCountAlongHaustra: + if n2 > 0 and n2 < elementsCountAlong: prevIdx = (n2-1)*elementsCountAround + n1 nextIdx = (n2+1)*elementsCountAround + n1 + curvatureAround = 0.5*( + getCubicHermiteCurvature(xInner[prevIdx], d2Inner[prevIdx], xInner[n], d2Inner[n], norm, 1.0)+ + getCubicHermiteCurvature(xInner[n], d2Inner[n], xInner[nextIdx], d2Inner[nextIdx], norm, 0.0)) elif n2 == 0: - prevIdx = (elementsCountAlongHaustra-1)*elementsCountAround + n1 nextIdx = (n2+1)*elementsCountAround + n1 + curvatureAround = getCubicHermiteCurvature(xInner[n], d2Inner[n], xInner[nextIdx], d2Inner[nextIdx], norm, 0.0) else: prevIdx = (n2-1)*elementsCountAround + n1 - nextIdx = elementsCountAround + n1 - curvatureAround = 0.5*( - getCubicHermiteCurvature(xInner[prevIdx], d2Inner[prevIdx], xInner[n], d2Inner[n], norm, 1.0)+ - getCubicHermiteCurvature(xInner[n], d2Inner[n], xInner[nextIdx], d2Inner[nextIdx], norm, 0.0)) + curvatureAround = getCubicHermiteCurvature(xInner[prevIdx], d2Inner[prevIdx], xInner[n], d2Inner[n], norm, 1.0) + factor = 1.0 - curvatureAround*thickness*xi3 dx_ds2 = [ factor*c for c in d2Inner[n]] dx_ds2List.append(dx_ds2) #dx_ds3 - dx_ds3 = [c * thickness/elementsCountThroughWall for c in d3InnerUnit[n]] + dx_ds3 = [c * thickness/elementsCountThroughWall for c in norm] dx_ds3List.append(dx_ds3) return xList, dx_ds1List, dx_ds2List, dx_ds3List From a4e9065418bcc068e9f6bc642ab7351a9197681d Mon Sep 17 00:00:00 2001 From: Mabelle Lin Date: Thu, 7 Mar 2019 16:15:05 +1300 Subject: [PATCH 36/44] Add annotation groups --- scaffoldmaker/meshtypes/meshtype_3d_haustra1.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/scaffoldmaker/meshtypes/meshtype_3d_haustra1.py b/scaffoldmaker/meshtypes/meshtype_3d_haustra1.py index 5dab2f81..d3c45892 100644 --- a/scaffoldmaker/meshtypes/meshtype_3d_haustra1.py +++ b/scaffoldmaker/meshtypes/meshtype_3d_haustra1.py @@ -118,6 +118,7 @@ def generateBaseMesh(region, options): nodeIdentifier = 1 zero = [0.0, 0.0, 0.0] + annotationGroups = [] d3InnerUnitList = [] xList = [] dx_ds1List = [] @@ -208,6 +209,8 @@ def generateBaseMesh(region, options): fm.endChange() + return annotationGroups + @classmethod def generateMesh(cls, region, options): """ @@ -224,10 +227,11 @@ def generateMesh(cls, region, options): refineElementsCountThroughWall = options['Refine number of elements through wall'] baseRegion = region.createRegion() - cls.generateBaseMesh(baseRegion, options) + baseAnnotationGroups = cls.generateBaseMesh(baseRegion, options) - meshrefinement = MeshRefinement(baseRegion, region) + meshrefinement = MeshRefinement(baseRegion, region, baseAnnotationGroups) meshrefinement.refineAllElementsCubeStandard3d(refineElementsCountAround, refineElementsCountAlong, refineElementsCountThroughWall) + return meshrefinement.getAnnotationGroups() def getColonHaustraSegmentInnerPoints(elementsCountAround, elementsCountAlongHaustrum, radius, cornerInnerRadiusFactor, haustraInnerRadiusFactor, haustrumSegmentEndDerivativeFactor, haustrumSegmentMidDerivativeFactor, haustraSegmentLength): From 25d8008443f70c8088a12fc61c34caa9d64caf2e Mon Sep 17 00:00:00 2001 From: Mabelle Lin Date: Fri, 8 Mar 2019 10:37:10 +1300 Subject: [PATCH 37/44] Update colon meshtype to use haustra meshtype --- scaffoldmaker/meshtypes/meshtype_3d_colon1.py | 280 ++---------------- .../meshtypes/meshtype_3d_haustra1.py | 90 ------ 2 files changed, 22 insertions(+), 348 deletions(-) diff --git a/scaffoldmaker/meshtypes/meshtype_3d_colon1.py b/scaffoldmaker/meshtypes/meshtype_3d_colon1.py index f969ff74..4d10e948 100644 --- a/scaffoldmaker/meshtypes/meshtype_3d_colon1.py +++ b/scaffoldmaker/meshtypes/meshtype_3d_colon1.py @@ -3,6 +3,8 @@ numbers of elements around, along and through wall, with variable radius and thickness along. """ + +from scaffoldmaker.meshtypes.meshtype_3d_haustra1 import MeshType_3d_haustra1, getColonHaustraSegmentInnerPoints from scaffoldmaker.meshtypes.scaffold_base import Scaffold_base from scaffoldmaker.utils.matrix import * from scaffoldmaker.utils.meshrefinement import MeshRefinement @@ -28,81 +30,35 @@ def getParameterSetNames(): @staticmethod def getDefaultOptions(parameterSetName='Default'): - options = { - 'Number of elements around': 15, - 'Number of elements along haustrum': 4, - 'Number of elements through wall': 1, + options = MeshType_3d_haustra1.getDefaultOptions(parameterSetName) + options['Number of elements around'] = 15 + options['Number of elements along haustrum'] = 4 + options['Inner radius'] = 1.0 + options['Haustrum segment mid-derivative factor'] = 2.0 + options['Wall thickness'] = 0.05 + optionsColon = { 'Number of haustra segments': 30, - 'Inner radius': 1.0, - 'Corner inner radius factor': 0.5, - 'Haustra inner radius factor': 0.5, - 'Haustrum segment end-derivative factor': 0.5, - 'Haustrum segment mid-derivative factor': 2.0, - 'Wall thickness': 0.05, - 'Tube type': 2, - 'Use cross derivatives': False, - 'Use linear through wall': False, - 'Refine': False, - 'Refine number of elements around': 1, - 'Refine number of elements along haustrum': 1, - 'Refine number of elements through wall': 1 - } + 'Tube type': 2 + } + options.update(optionsColon) if 'Human 1' in parameterSetName: options['Tube type'] = 3 return options @staticmethod def getOrderedOptionNames(): - return [ - 'Number of elements around', - 'Number of elements along haustrum', - 'Number of elements through wall', + optionNames = MeshType_3d_haustra1.getOrderedOptionNames() + optionNames.remove('Haustra segment length') + for optionName in [ 'Number of haustra segments', - 'Inner radius', - 'Corner inner radius factor', - 'Haustra inner radius factor', - 'Haustrum segment end-derivative factor', - 'Haustrum segment mid-derivative factor', - 'Wall thickness', - 'Tube type', - 'Use cross derivatives', - 'Use linear through wall', - 'Refine', - 'Refine number of elements around', - 'Refine number of elements along haustrum', - 'Refine number of elements through wall' - ] + 'Tube type']: + optionNames.insert(3, optionName) + return optionNames def checkOptions(options): - for key in [ - 'Number of elements through wall', - 'Number of haustra segments', - 'Refine number of elements around', - 'Refine number of elements along haustrum', - 'Refine number of elements through wall']: - if options[key] < 1: - options[key] = 1 - if (options['Number of elements around'] < 9) : - options['Number of elements around'] = 9 - if (options['Number of elements around'] % 3 != 0) : - options['Number of elements around'] = (options['Number of elements around']//3)*3 - if (options['Number of elements along haustrum'] < 2) : - options['Number of elements along haustrum'] = 2 - for key in [ - 'Inner radius', - 'Haustra inner radius factor', - 'Haustrum segment end-derivative factor', - 'Haustrum segment mid-derivative factor', - 'Wall thickness']: - if options[key] < 0.0: - options[key] = 0.0 - if options['Corner inner radius factor'] < 0.1: - options['Corner inner radius factor'] = 0.1 - for key in [ - 'Corner inner radius factor', - 'Haustrum segment end-derivative factor']: - if options[key] > 1.0: - options[key] = 1.0 + MeshType_3d_haustra1.checkOptions(options) + if options['Number of haustra segments'] < 1: + options['Number of haustra segments'] = 1 if options['Tube type'] < 1: options['Tube type'] = 1 if options['Tube type'] > 3: @@ -149,7 +105,7 @@ def generateBaseMesh(region, options): length += arcLength haustraSegmentLength = length / haustraSegmentCount - # Generate outer surface of a haustra segment + # Generate inner surface of a haustra segment xHaustraInner, d1HaustraInner, d2HaustraInner, haustraSegmentAxis = getColonHaustraSegmentInnerPoints(elementsCountAround, elementsCountAlongHaustrum, radius, cornerInnerRadiusFactor, haustraInnerRadiusFactor, haustrumSegmentEndDerivativeFactor, haustrumSegmentMidDerivativeFactor, haustraSegmentLength) @@ -181,195 +137,3 @@ def generateMesh(cls, region, options): meshrefinement = MeshRefinement(baseRegion, region, baseAnnotationGroups) meshrefinement.refineAllElementsCubeStandard3d(refineElementsCountAround, refineElementsCountAlong, refineElementsCountThroughWall) return meshrefinement.getAnnotationGroups() - -def getColonHaustraSegmentInnerPoints(elementsCountAround, elementsCountAlongHaustrum, radius, cornerInnerRadiusFactor, - haustraInnerRadiusFactor, haustrumSegmentEndDerivativeFactor, haustrumSegmentMidDerivativeFactor, haustraSegmentLength): - """ - Generates a 3-D haustra segment mesh with variable numbers - of elements around, along the central line, and through wall. - The haustra segment has a triangular profile with rounded corners - at the inter-haustral septa, and a clover profile in the intra-haustral - region. - :param elementsCountAround: Number of elements around haustra. - :param elementsCountAlongHaustrum: Number of elements along haustrum. - :param radius: Inner radius defined from center of triangular - profile to vertex of the triangle. - :param cornerInnerRadiusFactor: Roundness of triangular corners of - interhaustral septa. Factor is multiplied by inner radius - to get a radius of curvature at the corners. - :param haustraInnerRadiusFactor: Factor is multiplied by inner - radius to obtain radius of intersecting circles in the middle cross-section - along a haustra segment. - :param haustrumSegmentEndDerivativeFactor: Factor is multiplied by haustra - length to scale derivative along the end of a haustra segment length. - :param haustrumSegmentMidDerivativeFactor: Factor is multiplied by haustra - length to scale derivative along the mid length of the haustra segment. - :param haustraSegmentLength: Length of a haustra segment. - :return: coordinates, derivatives on inner surface of haustra segment. - """ - - # create nodes - x = [ 0.0, 0.0, 0.0 ] - dx_ds1 = [ 0.0, 0.0, 0.0 ] - dx_ds2 = [ 0.0, 0.0, 0.0 ] - dx_ds3 = [ 0.0, 0.0, 0.0 ] - radiansRangeRC = [7*math.pi/4, 0.0, math.pi/4] - cornerRC = cornerInnerRadiusFactor*radius - unitZ = [0.0, 0.0, 1.0] - haustraRadius = (haustraInnerRadiusFactor + 1)*radius - xAround = [] - d1Around = [] - xInner = [] - dx_ds1InnerList = [] - xHaustraSide = [] - xHaustraInner = [] - d1InnerHaustraRaw = [] - xInnerRaw = [] - dx_ds2InnerRaw = [] - xInnerList = [] - dx_ds2InnerList = [] - - # Pre-calculate node locations and derivatives on inner triangle - for n1 in range(3): - radiansAround = n1*2*math.pi / 3 - cosRadiansAround = math.cos(radiansAround) - sinRadiansAround = math.sin(radiansAround) - xc = [(radius - cornerRC) * cosRadiansAround, (radius - cornerRC) * sinRadiansAround, 0.0] - - for n in range(3): - radiansRC = radiansAround + radiansRangeRC[n] - cosRadiansRC = math.cos(radiansRC) - sinRadiansRC = math.sin(radiansRC) - x = [xc[0] + cornerRC*cosRadiansRC, xc[1] + cornerRC*sinRadiansRC, 0.0] - xAround.append(x) - d1 = [ cornerRC*math.pi/4 * -sinRadiansRC, cornerRC*math.pi/4 * cosRadiansRC, 0.0] - d1Around.append(d1) - - xSample = xAround[1:9] - xSample.append(xAround[0]) - xSample.append(xAround[1]) - d1Sample = d1Around[1:9] - d1Sample.append(d1Around[0]) - d1Sample.append(d1Around[1]) - sx, sd1, se, sxi, _= sampleCubicHermiteCurves(xSample, d1Sample, elementsCountAround) - xInner = xInner + sx[0:-1] - d1Inner = smoothCubicHermiteDerivativesLoop(sx[0:-1], sd1[0:-1]) - - # Pre-calculate node locations and derivatives on haustra inner cross-section - elementsCountAroundSide = int(elementsCountAround/3) - Ax = xInner[elementsCountAroundSide][0] - Ay = xInner[elementsCountAroundSide][1] - originRC = (Ax*Ax + Ay*Ay - haustraRadius*haustraRadius) / (2*(-Ax - haustraRadius)) - RC = haustraRadius - originRC - - if originRC > -Ax: - startTheta = math.asin(Ay/RC) - thetaRC = (math.pi - startTheta)*2 - else: - startTheta = math.pi - math.asin(Ay/RC) - thetaRC = math.asin(Ay/RC)*2 - thetaPerElementAround = thetaRC/(elementsCountAround/3) - - for n in range(elementsCountAroundSide + 1): - theta = startTheta + thetaPerElementAround * n - x = [RC*math.cos(theta) - originRC, - RC*math.sin(theta), - 0.0] - xHaustraSide.append(x) - - ang = [-2/3*math.pi, 0.0, 2/3*math.pi] - - for i in range(3): - rotAng = ang[i] - cosRotAng = math.cos(rotAng) - sinRotAng = math.sin(rotAng) - for n in range(elementsCountAroundSide): - theta = startTheta + thetaPerElementAround * n - cosTheta = math.cos(theta) - sinTheta = math.sin(theta) - x = [ (RC*cosTheta - originRC)*cosRotAng - RC*sinTheta*sinRotAng, - (RC*cosTheta - originRC)*sinRotAng + RC*sinTheta*cosRotAng, - 0.0] - xHaustraInner.append(x) - dx_ds1 = [(-RC*sinTheta*cosRotAng - RC*cosTheta*sinRotAng)*thetaPerElementAround, - (-RC*sinTheta*sinRotAng + RC*cosTheta*cosRotAng)*thetaPerElementAround, - 0.0] - d1InnerHaustraRaw.append(dx_ds1) - d1InnerHaustra = smoothCubicHermiteDerivativesLoop(xHaustraInner, d1InnerHaustraRaw) - - # Sample arclength of haustra segment - for n1 in range(elementsCountAround): - if n1%(elementsCountAround/3) > 0.0: - v1 = [xInner[n1][0], xInner[n1][1], 0.0] - startArcLength = haustrumSegmentEndDerivativeFactor * haustraSegmentLength - d1 = [ c*startArcLength for c in unitZ] - v2 = [xHaustraInner[n1][0], xHaustraInner[n1][1], haustraSegmentLength/2] - midArcLength = haustrumSegmentMidDerivativeFactor * haustraSegmentLength - d2 = [ c*midArcLength for c in unitZ] - v3 = [xInner[n1][0], xInner[n1][1], haustraSegmentLength] - d3 = [ c*startArcLength for c in unitZ] - else: - v1 = [xInner[n1][0], xInner[n1][1], 0.0] - v2 = [xInner[n1][0], xInner[n1][1], haustraSegmentLength/2] - v3 = [xInner[n1][0], xInner[n1][1], haustraSegmentLength] - d3 = d2 = d1 = [c* haustraSegmentLength/3 for c in unitZ] - nx = [v1, v2, v3] - nd1 = [d1, d2, d3] - sx, sd1, se, sxi, _ = sampleCubicHermiteCurves(nx, nd1, elementsCountAlongHaustrum) - xInnerRaw.append(sx) - dx_ds2InnerRaw.append(sd1) - - # Re-arrange sample order & calculate dx_ds1 and dx_ds3 from dx_ds2 - dx_ds1InnerList = dx_ds1InnerList + d1Inner - for n2 in range(elementsCountAlongHaustrum + 1): - xAround = [] - unitdx_ds1Around = [] - for n1 in range(elementsCountAround): - x = xInnerRaw[n1][n2] - xInnerList.append(x) - dx_ds2 = dx_ds2InnerRaw[n1][n2] - dx_ds2InnerList.append(dx_ds2) - unitTangent = normalise(dx_ds2) - # Inter-haustra - if n2 == 0 or n2 > elementsCountAlongHaustrum - 1: - dx_ds1 = d1Inner[n1] - unitdx_ds3 = crossproduct3(normalise(dx_ds1), unitTangent) - else: - # Intra-Haustra - if elementsCountAlongHaustrum == 2: - unitdx_ds1 = normalise(d1InnerHaustra[n1]) - else: - if n1%(elementsCountAround/3) == 0: # intersection points - unitdx_ds1 = normalise(d1InnerHaustra[n1]) - else: # points on clover - if elementsCountAlongHaustrum > 3: - if n2 < int(elementsCountAlongHaustrum/2): # first half of haustraSegmentLength - axisRot = crossproduct3(unitZ, unitTangent) - elif n2 > int(elementsCountAlongHaustrum/2): # second half of haustraSegmentLength - axisRot = crossproduct3(unitTangent, unitZ) - elif elementsCountAlongHaustrum == 3: # 3 elementsAlongHaustrum - axisRot = crossproduct3(unitTangent, unitZ) - - rotFrame = getRotationMatrixFromAxisAngle(axisRot, math.pi/2) - rotNormal = [rotFrame[j][0]*unitTangent[0] + rotFrame[j][1]*unitTangent[1] + rotFrame[j][2]*unitTangent[2] for j in range(3)] - unitdx_ds3 = normalise(rotNormal) - unitdx_ds1 = crossproduct3(unitTangent, unitdx_ds3) - xAround.append(x) - unitdx_ds1Around.append(unitdx_ds1) - - if n2 > 0 and n2 < elementsCountAlongHaustrum: - dx_ds1InnerAroundList = [] - for n1 in range(elementsCountAround): - v1 = xAround[n1] - d1 = unitdx_ds1Around[n1] - v2 = xAround[(n1+1)%elementsCountAround] - d2 = unitdx_ds1Around[(n1+1)%elementsCountAround] - arcLengthAround = computeCubicHermiteArcLength(v1, d1, v2, d2, True) - dx_ds1 = [c*arcLengthAround for c in d1] - dx_ds1InnerAroundList.append(dx_ds1) - d1Smoothed = smoothCubicHermiteDerivativesLoop(xAround, dx_ds1InnerAroundList) - dx_ds1InnerList = dx_ds1InnerList + d1Smoothed - - dx_ds1InnerList = dx_ds1InnerList + d1Inner - - return xInnerList, dx_ds1InnerList, dx_ds2InnerList, unitZ diff --git a/scaffoldmaker/meshtypes/meshtype_3d_haustra1.py b/scaffoldmaker/meshtypes/meshtype_3d_haustra1.py index d3c45892..3c8918a4 100644 --- a/scaffoldmaker/meshtypes/meshtype_3d_haustra1.py +++ b/scaffoldmaker/meshtypes/meshtype_3d_haustra1.py @@ -424,93 +424,3 @@ def getColonHaustraSegmentInnerPoints(elementsCountAround, elementsCountAlongHau dx_ds1InnerList = dx_ds1InnerList + d1Inner return xInnerList, dx_ds1InnerList, dx_ds2InnerList, unitZ - -def getOuterCoordinatesAndCurvatureFromInner(xInner, d1Inner, d3Inner, wallThickness, elementsCountAlong, elementsCountAround): - """ - Generates coordinates on outer surface and curvature of inner - surface from coordinates and derivatives of inner surface using - wall thickness and normals. - param xInner: Coordinates on inner surface - param d1Inner: Derivatives on inner surface around tube - param d3Inner: Derivatives on inner surface through wall - param wallThickness: Thickness of wall - param elementsCountAlong: Number of elements along tube - param elementsCountAround: Number of elements around tube - return xOuter: Coordinates on outer surface - return curvatureInner: Curvature of coordinates on inner surface - """ - xOuter = [] - curvatureInner = [] - for n2 in range(elementsCountAlong + 1): - for n1 in range(elementsCountAround): - n = n2*elementsCountAround + n1 - x = [xInner[n][i] + d3Inner[n][i]*wallThickness for i in range(3)] - prevIdx = n-1 if (n1 != 0) else (n2+1)*elementsCountAround - 1 - nextIdx = n+1 if (n1 < elementsCountAround-1) else n2*elementsCountAround - norm = d3Inner[n] - curvatureAround = 0.5*( - getCubicHermiteCurvature(xInner[prevIdx], d1Inner[prevIdx], xInner[n], d1Inner[n], norm, 1.0) + - getCubicHermiteCurvature(xInner[n], d1Inner[n], xInner[nextIdx], d1Inner[nextIdx], norm, 0.0)) - xOuter.append(x) - curvatureInner.append(curvatureAround) - - return xOuter, curvatureInner - -def interpolatefromInnerAndOuter( xInner, xOuter, thickness, xi3, curvatureInner, d1Inner, d2Inner, d3InnerUnit, - elementsCountAround, elementsCountAlong, elementsCountThroughWall): - """ - Generate coordinates and derivatives at xi3 by interpolating with - inner and outer coordinates and derivatives. - param xInner: Coordinates on inner surface - param xOuter: Coordinates on outer surface - param thickness: Thickness of wall - param curvatureInner: Curvature of coordinates on inner surface - param d1Inner: Derivatives on inner surface around tube - param d2Inner: Derivatives on inner surface along tube - param d3InnerUnit: Unit derivatives on inner surface through wall - param elementsCountAround: Number of elements around tube - param elementsCountAlong: Number of elements along tube - param elementsCountThroughWall: Number of elements through wall - return xList, dx_ds1List, dx_ds2List, dx_ds3List: Coordinates and derivatives on xi3 - """ - xList = [] - dx_ds1List = [] - dx_ds2List = [] - dx_ds3List =[] - - for n2 in range(elementsCountAlong+1): - for n1 in range(elementsCountAround): - n = n2*elementsCountAround + n1 - norm = d3InnerUnit[n] - # x - innerx = xInner[n] - outerx = xOuter[n] - dWall = [thickness*c for c in norm] - x = interpolateCubicHermite(innerx, dWall, outerx, dWall, xi3) - xList.append(x) - # dx_ds1 - factor = 1.0 - curvatureInner[n]*thickness*xi3 - dx_ds1 = [ factor*c for c in d1Inner[n]] - dx_ds1List.append(dx_ds1) - # dx_ds2 - if n2 > 0 and n2 < elementsCountAlong: - prevIdx = (n2-1)*elementsCountAround + n1 - nextIdx = (n2+1)*elementsCountAround + n1 - curvatureAround = 0.5*( - getCubicHermiteCurvature(xInner[prevIdx], d2Inner[prevIdx], xInner[n], d2Inner[n], norm, 1.0)+ - getCubicHermiteCurvature(xInner[n], d2Inner[n], xInner[nextIdx], d2Inner[nextIdx], norm, 0.0)) - elif n2 == 0: - nextIdx = (n2+1)*elementsCountAround + n1 - curvatureAround = getCubicHermiteCurvature(xInner[n], d2Inner[n], xInner[nextIdx], d2Inner[nextIdx], norm, 0.0) - else: - prevIdx = (n2-1)*elementsCountAround + n1 - curvatureAround = getCubicHermiteCurvature(xInner[prevIdx], d2Inner[prevIdx], xInner[n], d2Inner[n], norm, 1.0) - - factor = 1.0 - curvatureAround*thickness*xi3 - dx_ds2 = [ factor*c for c in d2Inner[n]] - dx_ds2List.append(dx_ds2) - #dx_ds3 - dx_ds3 = [c * thickness/elementsCountThroughWall for c in norm] - dx_ds3List.append(dx_ds3) - - return xList, dx_ds1List, dx_ds2List, dx_ds3List From 065122194b8c0f01f7987ce5367a9175ca2e0743 Mon Sep 17 00:00:00 2001 From: Mabelle Lin Date: Fri, 8 Mar 2019 11:05:14 +1300 Subject: [PATCH 38/44] Set arcLengthDerivatives=true --- scaffoldmaker/utils/tubemesh.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/scaffoldmaker/utils/tubemesh.py b/scaffoldmaker/utils/tubemesh.py index 77b1a659..188d936b 100644 --- a/scaffoldmaker/utils/tubemesh.py +++ b/scaffoldmaker/utils/tubemesh.py @@ -52,7 +52,7 @@ def generatetubemesh(region, elementsCountAlong = elementsCountAlongSegment*segmentCountAlong # Sample central line to get same number of elements as elementsCountAlong - sx, sd1, se, sxi, _ = sampleCubicHermiteCurves(cx, cd1, elementsCountAlong) + sx, sd1, se, sxi, _ = sampleCubicHermiteCurves(cx, cd1, elementsCountAlong, arcLengthDerivatives = True) # Find unit normals and binormals at each sample points sNormal = [] @@ -76,7 +76,10 @@ def generatetubemesh(region, cp = crossproduct3(prevUnitTangent, unitTangent) if magnitude(cp)> 0.0: axisRot = normalise(cp) - thetaRot = math.acos(dotproduct(prevUnitTangent, unitTangent)) + dp = dotproduct(prevUnitTangent, unitTangent) + if (dp - 1.0) > 0.0 and (dp - 1.0) < 1e-6: + dp = 1.0 + thetaRot = math.acos(dp) rotFrame = getRotationMatrixFromAxisAngle(axisRot, thetaRot) rotNormal = [rotFrame[j][0]*prevUnitNormal[0] + rotFrame[j][1]*prevUnitNormal[1] + rotFrame[j][2]*prevUnitNormal[2] for j in range(3)] unitNormal = normalise(rotNormal) From 17766f6c86578f28b1f370ecaecbd8e1ec097a50 Mon Sep 17 00:00:00 2001 From: Mabelle Lin Date: Fri, 8 Mar 2019 14:35:44 +1300 Subject: [PATCH 39/44] Remove arcLengthDerivatives = True --- scaffoldmaker/utils/tubemesh.py | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/scaffoldmaker/utils/tubemesh.py b/scaffoldmaker/utils/tubemesh.py index 188d936b..77b1a659 100644 --- a/scaffoldmaker/utils/tubemesh.py +++ b/scaffoldmaker/utils/tubemesh.py @@ -52,7 +52,7 @@ def generatetubemesh(region, elementsCountAlong = elementsCountAlongSegment*segmentCountAlong # Sample central line to get same number of elements as elementsCountAlong - sx, sd1, se, sxi, _ = sampleCubicHermiteCurves(cx, cd1, elementsCountAlong, arcLengthDerivatives = True) + sx, sd1, se, sxi, _ = sampleCubicHermiteCurves(cx, cd1, elementsCountAlong) # Find unit normals and binormals at each sample points sNormal = [] @@ -76,10 +76,7 @@ def generatetubemesh(region, cp = crossproduct3(prevUnitTangent, unitTangent) if magnitude(cp)> 0.0: axisRot = normalise(cp) - dp = dotproduct(prevUnitTangent, unitTangent) - if (dp - 1.0) > 0.0 and (dp - 1.0) < 1e-6: - dp = 1.0 - thetaRot = math.acos(dp) + thetaRot = math.acos(dotproduct(prevUnitTangent, unitTangent)) rotFrame = getRotationMatrixFromAxisAngle(axisRot, thetaRot) rotNormal = [rotFrame[j][0]*prevUnitNormal[0] + rotFrame[j][1]*prevUnitNormal[1] + rotFrame[j][2]*prevUnitNormal[2] for j in range(3)] unitNormal = normalise(rotNormal) From cdd2da9633c6ecde32a6152f2e7d35942422932c Mon Sep 17 00:00:00 2001 From: Mabelle Lin Date: Fri, 8 Mar 2019 14:43:47 +1300 Subject: [PATCH 40/44] Switch from computeCubicHermiteArcLength to getCubicHermiteArcLength --- scaffoldmaker/meshtypes/meshtype_3d_colon1.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/scaffoldmaker/meshtypes/meshtype_3d_colon1.py b/scaffoldmaker/meshtypes/meshtype_3d_colon1.py index 4d10e948..b35d39d4 100644 --- a/scaffoldmaker/meshtypes/meshtype_3d_colon1.py +++ b/scaffoldmaker/meshtypes/meshtype_3d_colon1.py @@ -100,8 +100,10 @@ def generateBaseMesh(region, options): # find arclength of colon length = 0.0 elementsCountIn = len(cx) - 1 + sd1 = smoothCubicHermiteDerivativesLine(cx, cd1, fixAllDirections = True, + magnitudeScalingMode = DerivativeScalingMode.HARMONIC_MEAN) for e in range(elementsCountIn): - arcLength = computeCubicHermiteArcLength(cx[e], cd1[e], cx[e + 1], cd1[e + 1], rescaleDerivatives = True) + arcLength = getCubicHermiteArcLength(cx[e], sd1[e], cx[e + 1], sd1[e + 1]) length += arcLength haustraSegmentLength = length / haustraSegmentCount From c7f07f1e0cf4285ae56adc4a17fcb43249a213a5 Mon Sep 17 00:00:00 2001 From: Mabelle Lin Date: Tue, 12 Mar 2019 11:20:23 +1300 Subject: [PATCH 41/44] Rename options for haustrum length derivative factors --- scaffoldmaker/meshtypes/meshtype_3d_colon1.py | 8 ++--- .../meshtypes/meshtype_3d_haustra1.py | 34 +++++++++---------- 2 files changed, 21 insertions(+), 21 deletions(-) diff --git a/scaffoldmaker/meshtypes/meshtype_3d_colon1.py b/scaffoldmaker/meshtypes/meshtype_3d_colon1.py index b35d39d4..59b6f7bd 100644 --- a/scaffoldmaker/meshtypes/meshtype_3d_colon1.py +++ b/scaffoldmaker/meshtypes/meshtype_3d_colon1.py @@ -34,7 +34,7 @@ def getDefaultOptions(parameterSetName='Default'): options['Number of elements around'] = 15 options['Number of elements along haustrum'] = 4 options['Inner radius'] = 1.0 - options['Haustrum segment mid-derivative factor'] = 2.0 + options['Haustrum length mid derivative factor'] = 2.0 options['Wall thickness'] = 0.05 optionsColon = { 'Number of haustra segments': 30, @@ -79,8 +79,8 @@ def generateBaseMesh(region, options): radius = options['Inner radius'] cornerInnerRadiusFactor = options['Corner inner radius factor'] haustraInnerRadiusFactor = options['Haustra inner radius factor'] - haustrumSegmentEndDerivativeFactor = options['Haustrum segment end-derivative factor'] - haustrumSegmentMidDerivativeFactor = options['Haustrum segment mid-derivative factor'] + haustrumLengthEndDerivativeFactor = options['Haustrum length end derivative factor'] + haustrumLengthMidDerivativeFactor = options['Haustrum length mid derivative factor'] wallThickness = options['Wall thickness'] tubeType = options['Tube type'] useCrossDerivatives = options['Use cross derivatives'] @@ -109,7 +109,7 @@ def generateBaseMesh(region, options): # Generate inner surface of a haustra segment xHaustraInner, d1HaustraInner, d2HaustraInner, haustraSegmentAxis = getColonHaustraSegmentInnerPoints(elementsCountAround, elementsCountAlongHaustrum, radius, cornerInnerRadiusFactor, - haustraInnerRadiusFactor, haustrumSegmentEndDerivativeFactor, haustrumSegmentMidDerivativeFactor, haustraSegmentLength) + haustraInnerRadiusFactor, haustrumLengthEndDerivativeFactor, haustrumLengthMidDerivativeFactor, haustraSegmentLength) # Generate tube mesh annotationGroups, nextNodeIdentifier, nextElementIdentifier = generatetubemesh(region, elementsCountAround, elementsCountAlongHaustrum, elementsCountThroughWall, haustraSegmentCount, diff --git a/scaffoldmaker/meshtypes/meshtype_3d_haustra1.py b/scaffoldmaker/meshtypes/meshtype_3d_haustra1.py index 3c8918a4..26e2951e 100644 --- a/scaffoldmaker/meshtypes/meshtype_3d_haustra1.py +++ b/scaffoldmaker/meshtypes/meshtype_3d_haustra1.py @@ -29,8 +29,8 @@ def getDefaultOptions(parameterSetName='Default'): 'Inner radius': 0.5, 'Corner inner radius factor': 0.5, 'Haustra inner radius factor': 0.5, - 'Haustrum segment end-derivative factor': 0.5, - 'Haustrum segment mid-derivative factor': 1.0, + 'Haustrum length end derivative factor': 0.5, + 'Haustrum length mid derivative factor': 1.0, 'Wall thickness': 0.01, 'Haustra segment length': 1.0, 'Use cross derivatives' : False, @@ -50,8 +50,8 @@ def getOrderedOptionNames(): 'Inner radius', 'Corner inner radius factor', 'Haustra inner radius factor', - 'Haustrum segment end-derivative factor', - 'Haustrum segment mid-derivative factor', + 'Haustrum length end derivative factor', + 'Haustrum length mid derivative factor', 'Wall thickness', 'Haustra segment length', 'Use cross derivatives', @@ -80,8 +80,8 @@ def checkOptions(options): for key in [ 'Inner radius', 'Haustra inner radius factor', - 'Haustrum segment end-derivative factor', - 'Haustrum segment mid-derivative factor', + 'Haustrum length end derivative factor', + 'Haustrum length mid derivative factor', 'Wall thickness', 'Haustra segment length']: if options[key] < 0.0: @@ -90,7 +90,7 @@ def checkOptions(options): options['Corner inner radius factor'] = 0.1 for key in [ 'Corner inner radius factor', - 'Haustrum segment end-derivative factor']: + 'Haustrum length end derivative factor']: if options[key] > 1.0: options[key] = 1.0 @@ -108,8 +108,8 @@ def generateBaseMesh(region, options): radius = options['Inner radius'] cornerInnerRadiusFactor = options['Corner inner radius factor'] haustraInnerRadiusFactor = options['Haustra inner radius factor'] - haustrumSegmentEndDerivativeFactor = options['Haustrum segment end-derivative factor'] - haustrumSegmentMidDerivativeFactor = options['Haustrum segment mid-derivative factor'] + haustrumLengthEndDerivativeFactor = options['Haustrum length end derivative factor'] + haustrumLengthMidDerivativeFactor = options['Haustrum length mid derivative factor'] wallThickness = options['Wall thickness'] haustraSegmentLength = options['Haustra segment length'] useCrossDerivatives = options['Use cross derivatives'] @@ -158,7 +158,7 @@ def generateBaseMesh(region, options): result = elementtemplate.defineField(coordinates, -1, eft) xInnerList, d1InnerList, d2InnerList, haustraSegmentAxis = getColonHaustraSegmentInnerPoints(elementsCountAround, elementsCountAlongHaustrum, radius, cornerInnerRadiusFactor, - haustraInnerRadiusFactor, haustrumSegmentEndDerivativeFactor, haustrumSegmentMidDerivativeFactor, haustraSegmentLength) + haustraInnerRadiusFactor, haustrumLengthEndDerivativeFactor, haustrumLengthMidDerivativeFactor, haustraSegmentLength) for n in range(len(xInnerList)): dx_ds3 = crossproduct3(normalise(d1InnerList[n]), normalise(d2InnerList[n])) @@ -234,7 +234,7 @@ def generateMesh(cls, region, options): return meshrefinement.getAnnotationGroups() def getColonHaustraSegmentInnerPoints(elementsCountAround, elementsCountAlongHaustrum, radius, cornerInnerRadiusFactor, - haustraInnerRadiusFactor, haustrumSegmentEndDerivativeFactor, haustrumSegmentMidDerivativeFactor, haustraSegmentLength): + haustraInnerRadiusFactor, haustrumLengthEndDerivativeFactor, haustrumLengthMidDerivativeFactor, haustraSegmentLength): """ Generates a 3-D haustra segment mesh with variable numbers of elements around, along the central line, and through wall. @@ -251,10 +251,10 @@ def getColonHaustraSegmentInnerPoints(elementsCountAround, elementsCountAlongHau :param haustraInnerRadiusFactor: Factor is multiplied by inner radius to obtain radius of intersecting circles in the middle cross-section along a haustra segment. - :param haustrumSegmentEndDerivativeFactor: Factor is multiplied by haustra - length to scale derivative along the end of a haustra segment length. - :param haustrumSegmentMidDerivativeFactor: Factor is multiplied by haustra - length to scale derivative along the mid length of the haustra segment. + :param haustrumLengthEndDerivativeFactor: Factor is multiplied by haustrum + length to scale derivative along the end of a haustrum length. + :param haustrumLengthMidDerivativeFactor: Factor is multiplied by haustrum + length to scale derivative along the mid length of the haustrum. :param haustraSegmentLength: Length of a haustra segment. :return: coordinates, derivatives on inner surface of haustra segment. """ @@ -352,10 +352,10 @@ def getColonHaustraSegmentInnerPoints(elementsCountAround, elementsCountAlongHau for n1 in range(elementsCountAround): if n1%(elementsCountAround/3) > 0.0: v1 = [xInner[n1][0], xInner[n1][1], 0.0] - startArcLength = haustrumSegmentEndDerivativeFactor * haustraSegmentLength + startArcLength = haustrumLengthEndDerivativeFactor * haustraSegmentLength d1 = [ c*startArcLength for c in unitZ] v2 = [xHaustraInner[n1][0], xHaustraInner[n1][1], haustraSegmentLength/2] - midArcLength = haustrumSegmentMidDerivativeFactor * haustraSegmentLength + midArcLength = haustrumLengthMidDerivativeFactor * haustraSegmentLength d2 = [ c*midArcLength for c in unitZ] v3 = [xInner[n1][0], xInner[n1][1], haustraSegmentLength] d3 = [ c*startArcLength for c in unitZ] From d92ca43fc1310b379549e054d97d08f0ac659841 Mon Sep 17 00:00:00 2001 From: Mabelle Lin Date: Tue, 12 Mar 2019 11:39:40 +1300 Subject: [PATCH 42/44] Rename haustrum length --- scaffoldmaker/meshtypes/meshtype_3d_colon1.py | 8 ++--- .../meshtypes/meshtype_3d_haustra1.py | 32 +++++++++---------- 2 files changed, 20 insertions(+), 20 deletions(-) diff --git a/scaffoldmaker/meshtypes/meshtype_3d_colon1.py b/scaffoldmaker/meshtypes/meshtype_3d_colon1.py index 59b6f7bd..5dbbcba2 100644 --- a/scaffoldmaker/meshtypes/meshtype_3d_colon1.py +++ b/scaffoldmaker/meshtypes/meshtype_3d_colon1.py @@ -48,7 +48,7 @@ def getDefaultOptions(parameterSetName='Default'): @staticmethod def getOrderedOptionNames(): optionNames = MeshType_3d_haustra1.getOrderedOptionNames() - optionNames.remove('Haustra segment length') + optionNames.remove('Haustrum length') for optionName in [ 'Number of haustra segments', 'Tube type']: @@ -105,15 +105,15 @@ def generateBaseMesh(region, options): for e in range(elementsCountIn): arcLength = getCubicHermiteArcLength(cx[e], sd1[e], cx[e + 1], sd1[e + 1]) length += arcLength - haustraSegmentLength = length / haustraSegmentCount + haustrumLength = length / haustraSegmentCount # Generate inner surface of a haustra segment xHaustraInner, d1HaustraInner, d2HaustraInner, haustraSegmentAxis = getColonHaustraSegmentInnerPoints(elementsCountAround, elementsCountAlongHaustrum, radius, cornerInnerRadiusFactor, - haustraInnerRadiusFactor, haustrumLengthEndDerivativeFactor, haustrumLengthMidDerivativeFactor, haustraSegmentLength) + haustraInnerRadiusFactor, haustrumLengthEndDerivativeFactor, haustrumLengthMidDerivativeFactor, haustrumLength) # Generate tube mesh annotationGroups, nextNodeIdentifier, nextElementIdentifier = generatetubemesh(region, elementsCountAround, elementsCountAlongHaustrum, elementsCountThroughWall, haustraSegmentCount, - cx, cd1, xHaustraInner, d1HaustraInner, d2HaustraInner, wallThickness, haustraSegmentAxis, haustraSegmentLength, useCrossDerivatives, useCubicHermiteThroughWall) + cx, cd1, xHaustraInner, d1HaustraInner, d2HaustraInner, wallThickness, haustraSegmentAxis, haustrumLength, useCrossDerivatives, useCubicHermiteThroughWall) return annotationGroups diff --git a/scaffoldmaker/meshtypes/meshtype_3d_haustra1.py b/scaffoldmaker/meshtypes/meshtype_3d_haustra1.py index 26e2951e..3f6a3187 100644 --- a/scaffoldmaker/meshtypes/meshtype_3d_haustra1.py +++ b/scaffoldmaker/meshtypes/meshtype_3d_haustra1.py @@ -32,7 +32,7 @@ def getDefaultOptions(parameterSetName='Default'): 'Haustrum length end derivative factor': 0.5, 'Haustrum length mid derivative factor': 1.0, 'Wall thickness': 0.01, - 'Haustra segment length': 1.0, + 'Haustrum length': 1.0, 'Use cross derivatives' : False, 'Use linear through wall' : False, 'Refine' : False, @@ -53,7 +53,7 @@ def getOrderedOptionNames(): 'Haustrum length end derivative factor', 'Haustrum length mid derivative factor', 'Wall thickness', - 'Haustra segment length', + 'Haustrum length', 'Use cross derivatives', 'Use linear through wall', 'Refine', @@ -83,7 +83,7 @@ def checkOptions(options): 'Haustrum length end derivative factor', 'Haustrum length mid derivative factor', 'Wall thickness', - 'Haustra segment length']: + 'Haustrum length']: if options[key] < 0.0: options[key] = 0.0 if options['Corner inner radius factor'] < 0.1: @@ -111,7 +111,7 @@ def generateBaseMesh(region, options): haustrumLengthEndDerivativeFactor = options['Haustrum length end derivative factor'] haustrumLengthMidDerivativeFactor = options['Haustrum length mid derivative factor'] wallThickness = options['Wall thickness'] - haustraSegmentLength = options['Haustra segment length'] + haustrumLength = options['Haustrum length'] useCrossDerivatives = options['Use cross derivatives'] useCubicHermiteThroughWall = not(options['Use linear through wall']) elementsCountAlong = elementsCountAlongHaustrum @@ -158,7 +158,7 @@ def generateBaseMesh(region, options): result = elementtemplate.defineField(coordinates, -1, eft) xInnerList, d1InnerList, d2InnerList, haustraSegmentAxis = getColonHaustraSegmentInnerPoints(elementsCountAround, elementsCountAlongHaustrum, radius, cornerInnerRadiusFactor, - haustraInnerRadiusFactor, haustrumLengthEndDerivativeFactor, haustrumLengthMidDerivativeFactor, haustraSegmentLength) + haustraInnerRadiusFactor, haustrumLengthEndDerivativeFactor, haustrumLengthMidDerivativeFactor, haustrumLength) for n in range(len(xInnerList)): dx_ds3 = crossproduct3(normalise(d1InnerList[n]), normalise(d2InnerList[n])) @@ -234,7 +234,7 @@ def generateMesh(cls, region, options): return meshrefinement.getAnnotationGroups() def getColonHaustraSegmentInnerPoints(elementsCountAround, elementsCountAlongHaustrum, radius, cornerInnerRadiusFactor, - haustraInnerRadiusFactor, haustrumLengthEndDerivativeFactor, haustrumLengthMidDerivativeFactor, haustraSegmentLength): + haustraInnerRadiusFactor, haustrumLengthEndDerivativeFactor, haustrumLengthMidDerivativeFactor, haustrumLength): """ Generates a 3-D haustra segment mesh with variable numbers of elements around, along the central line, and through wall. @@ -255,7 +255,7 @@ def getColonHaustraSegmentInnerPoints(elementsCountAround, elementsCountAlongHau length to scale derivative along the end of a haustrum length. :param haustrumLengthMidDerivativeFactor: Factor is multiplied by haustrum length to scale derivative along the mid length of the haustrum. - :param haustraSegmentLength: Length of a haustra segment. + :param haustrumLength: Length of a haustrum. :return: coordinates, derivatives on inner surface of haustra segment. """ @@ -352,18 +352,18 @@ def getColonHaustraSegmentInnerPoints(elementsCountAround, elementsCountAlongHau for n1 in range(elementsCountAround): if n1%(elementsCountAround/3) > 0.0: v1 = [xInner[n1][0], xInner[n1][1], 0.0] - startArcLength = haustrumLengthEndDerivativeFactor * haustraSegmentLength + startArcLength = haustrumLengthEndDerivativeFactor * haustrumLength d1 = [ c*startArcLength for c in unitZ] - v2 = [xHaustraInner[n1][0], xHaustraInner[n1][1], haustraSegmentLength/2] - midArcLength = haustrumLengthMidDerivativeFactor * haustraSegmentLength + v2 = [xHaustraInner[n1][0], xHaustraInner[n1][1], haustrumLength/2] + midArcLength = haustrumLengthMidDerivativeFactor * haustrumLength d2 = [ c*midArcLength for c in unitZ] - v3 = [xInner[n1][0], xInner[n1][1], haustraSegmentLength] + v3 = [xInner[n1][0], xInner[n1][1], haustrumLength] d3 = [ c*startArcLength for c in unitZ] else: v1 = [xInner[n1][0], xInner[n1][1], 0.0] - v2 = [xInner[n1][0], xInner[n1][1], haustraSegmentLength/2] - v3 = [xInner[n1][0], xInner[n1][1], haustraSegmentLength] - d3 = d2 = d1 = [c* haustraSegmentLength/3 for c in unitZ] + v2 = [xInner[n1][0], xInner[n1][1], haustrumLength/2] + v3 = [xInner[n1][0], xInner[n1][1], haustrumLength] + d3 = d2 = d1 = [c* haustrumLength/3 for c in unitZ] nx = [v1, v2, v3] nd1 = [d1, d2, d3] sx, sd1, se, sxi, _ = sampleCubicHermiteCurves(nx, nd1, elementsCountAlongHaustrum) @@ -394,9 +394,9 @@ def getColonHaustraSegmentInnerPoints(elementsCountAround, elementsCountAlongHau unitdx_ds1 = normalise(d1InnerHaustra[n1]) else: # points on clover if elementsCountAlongHaustrum > 3: - if n2 < int(elementsCountAlongHaustrum/2): # first half of haustraSegmentLength + if n2 < int(elementsCountAlongHaustrum/2): # first half of haustrumLength axisRot = crossproduct3(unitZ, unitTangent) - elif n2 > int(elementsCountAlongHaustrum/2): # second half of haustraSegmentLength + elif n2 > int(elementsCountAlongHaustrum/2): # second half of haustrumLength axisRot = crossproduct3(unitTangent, unitZ) elif elementsCountAlongHaustrum == 3: # 3 elementsAlongHaustrum axisRot = crossproduct3(unitTangent, unitZ) From 6cb34d757400b7bb94db0bc4b1ca9d65d8ecc4d4 Mon Sep 17 00:00:00 2001 From: Mabelle Lin Date: Tue, 12 Mar 2019 11:51:49 +1300 Subject: [PATCH 43/44] Rename haustrum inner radius factor --- scaffoldmaker/meshtypes/meshtype_3d_colon1.py | 4 ++-- .../meshtypes/meshtype_3d_haustra1.py | 20 +++++++++---------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/scaffoldmaker/meshtypes/meshtype_3d_colon1.py b/scaffoldmaker/meshtypes/meshtype_3d_colon1.py index 5dbbcba2..160d6556 100644 --- a/scaffoldmaker/meshtypes/meshtype_3d_colon1.py +++ b/scaffoldmaker/meshtypes/meshtype_3d_colon1.py @@ -78,7 +78,7 @@ def generateBaseMesh(region, options): haustraSegmentCount = options['Number of haustra segments'] radius = options['Inner radius'] cornerInnerRadiusFactor = options['Corner inner radius factor'] - haustraInnerRadiusFactor = options['Haustra inner radius factor'] + haustrumInnerRadiusFactor = options['Haustrum inner radius factor'] haustrumLengthEndDerivativeFactor = options['Haustrum length end derivative factor'] haustrumLengthMidDerivativeFactor = options['Haustrum length mid derivative factor'] wallThickness = options['Wall thickness'] @@ -109,7 +109,7 @@ def generateBaseMesh(region, options): # Generate inner surface of a haustra segment xHaustraInner, d1HaustraInner, d2HaustraInner, haustraSegmentAxis = getColonHaustraSegmentInnerPoints(elementsCountAround, elementsCountAlongHaustrum, radius, cornerInnerRadiusFactor, - haustraInnerRadiusFactor, haustrumLengthEndDerivativeFactor, haustrumLengthMidDerivativeFactor, haustrumLength) + haustrumInnerRadiusFactor, haustrumLengthEndDerivativeFactor, haustrumLengthMidDerivativeFactor, haustrumLength) # Generate tube mesh annotationGroups, nextNodeIdentifier, nextElementIdentifier = generatetubemesh(region, elementsCountAround, elementsCountAlongHaustrum, elementsCountThroughWall, haustraSegmentCount, diff --git a/scaffoldmaker/meshtypes/meshtype_3d_haustra1.py b/scaffoldmaker/meshtypes/meshtype_3d_haustra1.py index 3f6a3187..c0b6d263 100644 --- a/scaffoldmaker/meshtypes/meshtype_3d_haustra1.py +++ b/scaffoldmaker/meshtypes/meshtype_3d_haustra1.py @@ -28,7 +28,7 @@ def getDefaultOptions(parameterSetName='Default'): 'Number of elements through wall' : 1, 'Inner radius': 0.5, 'Corner inner radius factor': 0.5, - 'Haustra inner radius factor': 0.5, + 'Haustrum inner radius factor': 0.5, 'Haustrum length end derivative factor': 0.5, 'Haustrum length mid derivative factor': 1.0, 'Wall thickness': 0.01, @@ -49,7 +49,7 @@ def getOrderedOptionNames(): 'Number of elements through wall', 'Inner radius', 'Corner inner radius factor', - 'Haustra inner radius factor', + 'Haustrum inner radius factor', 'Haustrum length end derivative factor', 'Haustrum length mid derivative factor', 'Wall thickness', @@ -79,7 +79,7 @@ def checkOptions(options): options['Number of elements along haustrum'] = 2 for key in [ 'Inner radius', - 'Haustra inner radius factor', + 'Haustrum inner radius factor', 'Haustrum length end derivative factor', 'Haustrum length mid derivative factor', 'Wall thickness', @@ -107,7 +107,7 @@ def generateBaseMesh(region, options): elementsCountThroughWall = options['Number of elements through wall'] radius = options['Inner radius'] cornerInnerRadiusFactor = options['Corner inner radius factor'] - haustraInnerRadiusFactor = options['Haustra inner radius factor'] + haustrumInnerRadiusFactor = options['Haustrum inner radius factor'] haustrumLengthEndDerivativeFactor = options['Haustrum length end derivative factor'] haustrumLengthMidDerivativeFactor = options['Haustrum length mid derivative factor'] wallThickness = options['Wall thickness'] @@ -158,7 +158,7 @@ def generateBaseMesh(region, options): result = elementtemplate.defineField(coordinates, -1, eft) xInnerList, d1InnerList, d2InnerList, haustraSegmentAxis = getColonHaustraSegmentInnerPoints(elementsCountAround, elementsCountAlongHaustrum, radius, cornerInnerRadiusFactor, - haustraInnerRadiusFactor, haustrumLengthEndDerivativeFactor, haustrumLengthMidDerivativeFactor, haustrumLength) + haustrumInnerRadiusFactor, haustrumLengthEndDerivativeFactor, haustrumLengthMidDerivativeFactor, haustrumLength) for n in range(len(xInnerList)): dx_ds3 = crossproduct3(normalise(d1InnerList[n]), normalise(d2InnerList[n])) @@ -234,7 +234,7 @@ def generateMesh(cls, region, options): return meshrefinement.getAnnotationGroups() def getColonHaustraSegmentInnerPoints(elementsCountAround, elementsCountAlongHaustrum, radius, cornerInnerRadiusFactor, - haustraInnerRadiusFactor, haustrumLengthEndDerivativeFactor, haustrumLengthMidDerivativeFactor, haustrumLength): + haustrumInnerRadiusFactor, haustrumLengthEndDerivativeFactor, haustrumLengthMidDerivativeFactor, haustrumLength): """ Generates a 3-D haustra segment mesh with variable numbers of elements around, along the central line, and through wall. @@ -248,7 +248,7 @@ def getColonHaustraSegmentInnerPoints(elementsCountAround, elementsCountAlongHau :param cornerInnerRadiusFactor: Roundness of triangular corners of interhaustral septa. Factor is multiplied by inner radius to get a radius of curvature at the corners. - :param haustraInnerRadiusFactor: Factor is multiplied by inner + :param haustrumInnerRadiusFactor: Factor is multiplied by inner radius to obtain radius of intersecting circles in the middle cross-section along a haustra segment. :param haustrumLengthEndDerivativeFactor: Factor is multiplied by haustrum @@ -267,7 +267,7 @@ def getColonHaustraSegmentInnerPoints(elementsCountAround, elementsCountAlongHau radiansRangeRC = [7*math.pi/4, 0.0, math.pi/4] cornerRC = cornerInnerRadiusFactor*radius unitZ = [0.0, 0.0, 1.0] - haustraRadius = (haustraInnerRadiusFactor + 1)*radius + haustrumRadius = (haustrumInnerRadiusFactor + 1)*radius xAround = [] d1Around = [] xInner = [] @@ -310,8 +310,8 @@ def getColonHaustraSegmentInnerPoints(elementsCountAround, elementsCountAlongHau elementsCountAroundSide = int(elementsCountAround/3) Ax = xInner[elementsCountAroundSide][0] Ay = xInner[elementsCountAroundSide][1] - originRC = (Ax*Ax + Ay*Ay - haustraRadius*haustraRadius) / (2*(-Ax - haustraRadius)) - RC = haustraRadius - originRC + originRC = (Ax*Ax + Ay*Ay - haustrumRadius*haustrumRadius) / (2*(-Ax - haustrumRadius)) + RC = haustrumRadius - originRC if originRC > -Ax: startTheta = math.asin(Ay/RC) From ed38d64a320f8a6830ddf8ae11865070761936ae Mon Sep 17 00:00:00 2001 From: Mabelle Lin Date: Tue, 12 Mar 2019 12:13:40 +1300 Subject: [PATCH 44/44] Update to generate mesh using tubemesh --- .../meshtypes/meshtype_3d_haustra1.py | 97 ++----------------- 1 file changed, 8 insertions(+), 89 deletions(-) diff --git a/scaffoldmaker/meshtypes/meshtype_3d_haustra1.py b/scaffoldmaker/meshtypes/meshtype_3d_haustra1.py index c0b6d263..f3db9857 100644 --- a/scaffoldmaker/meshtypes/meshtype_3d_haustra1.py +++ b/scaffoldmaker/meshtypes/meshtype_3d_haustra1.py @@ -115,99 +115,18 @@ def generateBaseMesh(region, options): useCrossDerivatives = options['Use cross derivatives'] useCubicHermiteThroughWall = not(options['Use linear through wall']) elementsCountAlong = elementsCountAlongHaustrum + haustraSegmentCount = 1 - nodeIdentifier = 1 - zero = [0.0, 0.0, 0.0] - annotationGroups = [] - d3InnerUnitList = [] - xList = [] - dx_ds1List = [] - dx_ds2List = [] - dx_ds3List = [] + cx = [ [ 0.0, 0.0, 0.0 ], [ haustrumLength, 0.0, 0.0 ] ] + cd1 = [ [ haustrumLength, 0.0, 0.0 ], [ haustrumLength, 0.0, 0.0 ] ] - fm = region.getFieldmodule() - fm.beginChange() - cache = fm.createFieldcache() - coordinates = getOrCreateCoordinateField(fm) - - nodes = fm.findNodesetByFieldDomainType(Field.DOMAIN_TYPE_NODES) - nodetemplate = nodes.createNodetemplate() - nodetemplate.defineField(coordinates) - nodetemplate.setValueNumberOfVersions(coordinates, -1, Node.VALUE_LABEL_VALUE, 1) - nodetemplate.setValueNumberOfVersions(coordinates, -1, Node.VALUE_LABEL_D_DS1, 1) - nodetemplate.setValueNumberOfVersions(coordinates, -1, Node.VALUE_LABEL_D_DS2, 1) - if useCrossDerivatives: - nodetemplate.setValueNumberOfVersions(coordinates, -1, Node.VALUE_LABEL_D2_DS1DS2, 1) - if useCubicHermiteThroughWall: - nodetemplate.setValueNumberOfVersions(coordinates, -1, Node.VALUE_LABEL_D_DS3, 1) - if useCrossDerivatives: - nodetemplate.setValueNumberOfVersions(coordinates, -1, Node.VALUE_LABEL_D2_DS1DS3, 1) - nodetemplate.setValueNumberOfVersions(coordinates, -1, Node.VALUE_LABEL_D2_DS2DS3, 1) - nodetemplate.setValueNumberOfVersions(coordinates, -1, Node.VALUE_LABEL_D3_DS1DS2DS3, 1) - - mesh = fm.findMeshByDimension(3) - - if useCubicHermiteThroughWall: - eftfactory = eftfactory_tricubichermite(mesh, useCrossDerivatives) - else: - eftfactory = eftfactory_bicubichermitelinear(mesh, useCrossDerivatives) - eft = eftfactory.createEftBasic() - - elementtemplate = mesh.createElementtemplate() - elementtemplate.setElementShapeType(Element.SHAPE_TYPE_CUBE) - result = elementtemplate.defineField(coordinates, -1, eft) - - xInnerList, d1InnerList, d2InnerList, haustraSegmentAxis = getColonHaustraSegmentInnerPoints(elementsCountAround, elementsCountAlongHaustrum, radius, cornerInnerRadiusFactor, + # Generate inner surface of a haustra segment + xHaustraInner, d1HaustraInner, d2HaustraInner, haustraSegmentAxis = getColonHaustraSegmentInnerPoints(elementsCountAround, elementsCountAlongHaustrum, radius, cornerInnerRadiusFactor, haustrumInnerRadiusFactor, haustrumLengthEndDerivativeFactor, haustrumLengthMidDerivativeFactor, haustrumLength) - for n in range(len(xInnerList)): - dx_ds3 = crossproduct3(normalise(d1InnerList[n]), normalise(d2InnerList[n])) - unitdx_ds3 = normalise(dx_ds3) - d3InnerUnitList.append(unitdx_ds3) - - # Pre-calculate node locations and derivatives on outer boundary - xOuterList, curvatureInner = getOuterCoordinatesAndCurvatureFromInner(xInnerList, d1InnerList, d3InnerUnitList, wallThickness, elementsCountAlong, elementsCountAround) - - # Interpolate to get nodes through wall - for n3 in range(elementsCountThroughWall + 1): - xi3 = 1/elementsCountThroughWall * n3 - x, dx_ds1, dx_ds2, dx_ds3 = interpolatefromInnerAndOuter( xInnerList, xOuterList, wallThickness, xi3, curvatureInner, d1InnerList, d2InnerList, d3InnerUnitList, elementsCountAround, elementsCountAlong, elementsCountThroughWall) - xList = xList + x - dx_ds1List = dx_ds1List + dx_ds1 - dx_ds2List = dx_ds2List + dx_ds2 - dx_ds3List = dx_ds3List + dx_ds3 - - for n in range(len(xList)): - node = nodes.createNode(nodeIdentifier, nodetemplate) - cache.setNode(node) - coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_VALUE, 1, xList[n]) - coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS1, 1, dx_ds1List[n]) - coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS2, 1, dx_ds2List[n]) - coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS3, 1, dx_ds3List[n]) - # print('NodeIdentifier = ', nodeIdentifier, xList[n]) - if useCrossDerivatives: - coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D2_DS1DS2, 1, zero) - coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D2_DS1DS3, 1, zero) - coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D2_DS2DS3, 1, zero) - coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D3_DS1DS2DS3, 1, zero) - nodeIdentifier = nodeIdentifier + 1 - - # create elements - elementIdentifier = 1 # nextElementIdentifier - now = (elementsCountAlong + 1)*elementsCountAround - for e3 in range(elementsCountThroughWall): - for e2 in range(elementsCountAlong): - for e1 in range(elementsCountAround): - element = mesh.createElement(elementIdentifier, elementtemplate) - bni11 = e3*now + e2*elementsCountAround + e1 + 1 - bni12 = e3*now + e2*elementsCountAround + (e1 + 1)%elementsCountAround + 1 - bni21 = e3*now + (e2 + 1)*elementsCountAround + e1 + 1 - bni22 = e3*now + (e2 + 1)*elementsCountAround + (e1 + 1)%elementsCountAround + 1 - nodeIdentifiers = [ bni11, bni12, bni21, bni22, bni11 + now, bni12 + now, bni21 + now, bni22 + now ] - result = element.setNodesByIdentifier(eft, nodeIdentifiers) - elementIdentifier = elementIdentifier + 1 - - fm.endChange() + # Generate tube mesh + annotationGroups, nextNodeIdentifier, nextElementIdentifier = generatetubemesh(region, elementsCountAround, elementsCountAlongHaustrum, elementsCountThroughWall, haustraSegmentCount, + cx, cd1, xHaustraInner, d1HaustraInner, d2HaustraInner, wallThickness, haustraSegmentAxis, haustrumLength, useCrossDerivatives, useCubicHermiteThroughWall) return annotationGroups