Exemplo n.º 1
0
 def resampleHermiteCurvePointsSmooth(self,
                                      nx,
                                      nd1,
                                      nd2,
                                      nd3,
                                      nProportions,
                                      derivativeMagnitudeStart=None,
                                      derivativeMagnitudeEnd=None):
     '''
     Call interp.sampleCubicHermiteCurvesSmooth on nx, nd1 and recalculate positions, nd2, nd3 for points.
     :return: nx[], nd1[], nd2[], nd3[], nProportions[]
     '''
     elementsCount = len(nx) - 1
     #print(nx, nd1, elementsCount, derivativeMagnitudeStart, derivativeMagnitudeEnd)
     nx, nd1 = interp.sampleCubicHermiteCurvesSmooth(
         nx, nd1, elementsCount, derivativeMagnitudeStart,
         derivativeMagnitudeEnd)[0:2]
     mag2 = vector.magnitude(nd2[0])
     if mag2 > 0.0:
         nd2[0] = vector.setMagnitude(nd2[0], vector.magnitude(nd1[0]))
     for n in range(1, elementsCount):
         p = self.findNearestPosition(
             nx[n], self.createPositionProportion(*nProportions[n]))
         nProportions[n] = self.getProportion(p)
         _, sd1, sd2 = self.evaluateCoordinates(p, derivatives=True)
         _, d2, d3 = calculate_surface_axes(sd1, sd2,
                                            vector.normalise(nd1[n]))
         nd2[n] = vector.setMagnitude(d2, vector.magnitude(nd1[n]))
         nd3[n] = d3
     mag2 = vector.magnitude(nd2[-1])
     if mag2 > 0.0:
         nd2[-1] = vector.setMagnitude(nd2[-1], vector.magnitude(nd1[-1]))
     return nx, nd1, nd2, nd3, nProportions
Exemplo n.º 2
0
    def __init__(self, region, centralPath, elementsCount):
        """
        :param region: Zinc region to define model in.
        :param centralPath: Central path subscaffold comes from meshtype_1d_path1 and used to calculate ellipse radii.
        :param elementsCount: Number of elements needs to be sampled along the central path.
        """
        tmpRegion = region.createRegion()
        centralPath.generate(tmpRegion)
        cx, cd1, cd2, cd3, cd12, cd13 = extractPathParametersFromRegion(
            tmpRegion, [
                Node.VALUE_LABEL_VALUE, Node.VALUE_LABEL_D_DS1,
                Node.VALUE_LABEL_D_DS2, Node.VALUE_LABEL_D_DS3,
                Node.VALUE_LABEL_D2_DS1DS2, Node.VALUE_LABEL_D2_DS1DS3
            ])
        del tmpRegion
        # for i in range(len(cx)):
        #     print(i, '[', cx[i], ',', cd1[i], ',', cd2[i], ',', cd12[i], ',', cd3[i], ',', cd13[i], '],')

        sx, sd1, se, sxi, ssf = sampleCubicHermiteCurves(
            cx, cd1, elementsCount)
        sd2, sd12 = interpolateSampleCubicHermite(cd2, cd12, se, sxi, ssf)
        sd3, sd13 = interpolateSampleCubicHermite(cd3, cd13, se, sxi, ssf)

        self.centres = sx
        self.majorRadii = [vector.magnitude(a) for a in sd2]
        self.majorAxis = sd2
        self.minorRadii = [vector.magnitude(a) for a in sd3]
        self.minorAxis = sd3
        self.alongAxis = sd1
Exemplo n.º 3
0
 def makeSideDerivativesNormal(cls, region, options, functionOptions, editGroupName):
     makeD2Normal = options['D2 derivatives'] and functionOptions['Make D2 normal']
     makeD3Normal = options['D3 derivatives'] and functionOptions['Make D3 normal']
     if not (makeD2Normal or makeD3Normal):
         return False, False
     valueLabels = [ Node.VALUE_LABEL_D_DS1 ]
     if options['D2 derivatives']:
         valueLabels.append(Node.VALUE_LABEL_D_DS2)
     if options['D3 derivatives']:
         valueLabels.append(Node.VALUE_LABEL_D_DS3)
     parameters = extractPathParametersFromRegion(region, valueLabels)
     d1 = parameters[0]
     modifyParameters = []
     modifyValueLabels = []
     if makeD2Normal:
         d2 = parameters[1]
         for c in range(len(d1)):
             td2 = vector.vectorRejection(d2[c], d1[c])
             d2[c] = vector.setMagnitude(td2, vector.magnitude(d2[c]))
         modifyParameters.append(d2)
         modifyValueLabels.append(Node.VALUE_LABEL_D_DS2)
     if makeD3Normal:
         d3 = parameters[-1]
         if options['D2 derivatives']:
             d2 = parameters[1]
             for c in range(len(d1)):
                 d3[c] = vector.setMagnitude(vector.crossproduct3(d1[c], d2[c]), vector.magnitude(d3[c]))
         else:
             for c in range(len(d1)):
                 td3 = vector.vectorRejection(d3[c], d1[c])
                 d3[c] = vector.setMagnitude(td3, vector.magnitude(d3[c]))
         modifyParameters.append(d3)
         modifyValueLabels.append(Node.VALUE_LABEL_D_DS3)
     setPathParameters(region, modifyValueLabels, modifyParameters, editGroupName)
     return False, True  # settings not changed, nodes changed
Exemplo n.º 4
0
def createEllipsoidPoints(centre, poleAxis, sideAxis, elementsCountAround,
                          elementsCountUp, height):
    '''
    Generate a set of points and derivatives for circle of revolution of an ellipse
    starting at pole poleAxis from centre.
    :param centre: Centre of full ellipsoid.
    :param poleAxis: Vector in direction of starting pole, magnitude is ellipse axis length.
    :param sideAxis: Vector normal to poleAxis, magnitude is ellipse side axis length.
    :param height: Height of arc of ellipsoid from starting pole along poleAxis.
    :return: Lists nx, nd1, nd2. Ordered fastest around, starting at pole. Suitable for passing to TrackSurface.
    '''
    nx = []
    nd1 = []
    nd2 = []
    magPoleAxis = vector.magnitude(poleAxis)
    magSideAxis = vector.magnitude(sideAxis)
    unitPoleAxis = vector.normalise(poleAxis)
    unitSideAxis1 = vector.normalise(sideAxis)
    unitSideAxis2 = vector.normalise(vector.crossproduct3(sideAxis, poleAxis))
    useHeight = min(max(0.0, height), 2.0 * magPoleAxis)
    totalRadiansUp = getEllipseRadiansToX(magPoleAxis,
                                          0.0,
                                          magPoleAxis - useHeight,
                                          initialTheta=0.5 * math.pi *
                                          useHeight / magPoleAxis)
    radiansUp = 0.0
    lengthUp = getEllipseArcLength(magPoleAxis, magSideAxis, radiansUp,
                                   totalRadiansUp)
    elementLengthUp = lengthUp / elementsCountUp
    radiansPerElementAround = 2.0 * math.pi / elementsCountAround
    for n2 in range(elementsCountUp + 1):
        cosRadiansUp = math.cos(radiansUp)
        sinRadiansUp = math.sin(radiansUp)
        radius = sinRadiansUp * magSideAxis
        d2r, d2z = vector.setMagnitude(
            [cosRadiansUp * magSideAxis, sinRadiansUp * magPoleAxis],
            elementLengthUp)
        cx = [(centre[c] + cosRadiansUp * poleAxis[c]) for c in range(3)]
        elementLengthAround = radius * radiansPerElementAround
        radiansAround = 0.0
        for n in range(elementsCountAround):
            cosRadiansAround = math.cos(radiansAround)
            sinRadiansAround = math.sin(radiansAround)
            nx.append([(cx[c] + radius * (cosRadiansAround * unitSideAxis1[c] +
                                          sinRadiansAround * unitSideAxis2[c]))
                       for c in range(3)])
            nd1.append([
                (elementLengthAround * (-sinRadiansAround * unitSideAxis1[c] +
                                        cosRadiansAround * unitSideAxis2[c]))
                for c in range(3)
            ])
            nd2.append([(d2r * (cosRadiansAround * unitSideAxis1[c] +
                                sinRadiansAround * unitSideAxis2[c]) -
                         d2z * unitPoleAxis[c]) for c in range(3)])
            radiansAround += radiansPerElementAround
        radiansUp = updateEllipseAngleByArcLength(magPoleAxis, magSideAxis,
                                                  radiansUp, elementLengthUp)
    return nx, nd1, nd2
Exemplo n.º 5
0
 def __init__(self,
              centre,
              majorAxis,
              minorAxis,
              elementsCountAcrossMajor,
              elementsCountAcrossMinor,
              elementsCountAcrossShell,
              elementsCountAcrossTransition,
              shellProportion,
              coreMajorRadius,
              coreMinorRadius,
              ellipseShape=EllipseShape.Ellipse_SHAPE_FULL):
     """
     :param centre: Ellipse centre.
     :param majorAxis: A vector for ellipse major axis.
     :param minorAxis: Ellipse minor axis.
     :param elementsCountAcrossMajor:
     :param elementsCountAcrossMinor:
     :param ellipseShape: The shape of the ellipse which can be full or lower half.
     """
     self.centre = centre
     self.majorAxis = majorAxis
     self.minorAxis = minorAxis
     self.majorRadius = vector.magnitude(majorAxis)
     self.minorRadius = vector.magnitude(minorAxis)
     self.coreMajorRadius = coreMajorRadius
     self.coreMinorRadius = coreMinorRadius
     elementsCountRim = elementsCountAcrossShell + elementsCountAcrossTransition - 1
     shieldMode = ShieldShape2D.SHIELD_SHAPE_FULL if ellipseShape is EllipseShape.Ellipse_SHAPE_FULL\
         else ShieldShape2D.SHIELD_SHAPE_LOWER_HALF
     shield = ShieldMesh2D(elementsCountAcrossMinor,
                           elementsCountAcrossMajor,
                           elementsCountRim,
                           None,
                           1,
                           shieldMode,
                           shieldType=ShieldRimDerivativeMode.
                           SHIELD_RIM_DERIVATIVE_MODE_AROUND)
     self.elementsCountAcrossMajor = elementsCountAcrossMajor
     self.elementsCountAround = shield.elementsCountAroundFull
     self.elementsCountUp = shield.elementsCountUp
     self.elementsCountAcrossMinor = elementsCountAcrossMinor
     self.elementsCountAcrossShell = elementsCountAcrossShell
     self.elementsCountAcrossTransition = elementsCountAcrossTransition
     self.elementsCountAcrossRim = elementsCountRim
     self.shellProportion = shellProportion
     self.nodeId = shield.nodeId
     self.px = shield.px[0]
     self.pd1 = shield.pd1[0]
     self.pd2 = shield.pd2[0]
     self.pd3 = shield.pd3[0]
     self.__shield = shield
     self.ellipseShape = ellipseShape
     # generate the ellipse
     self.generate2DEllipseMesh()
Exemplo n.º 6
0
def createEllipsePerimeter(centre, majorAxis, minorAxis, elementsCountAround,
                           height):
    """
    Generate a set of points and derivatives for an ellipse
    starting at pole majorAxis from centre.
    :param elementsCountAround: Number of elements around.
    :param centre: Centre of full ellipse.
    :param majorAxis: Vector in direction of starting major radius, magnitude is ellipse major radius.
    :param minorAxis: Vector normal to major axis, magnitude is ellipse minor axis length.
    :param height: Height of arc of ellipsoid from starting point along majorAxis.
    :return: Lists nx, nd1. Ordered fastest around, starting at major radius.
    """
    nx = []
    nd1 = []
    magMajorAxis = vector.magnitude(majorAxis)
    magMinorAxis = vector.magnitude(minorAxis)
    unitMajorAxis = vector.normalise(majorAxis)
    unitMinorAxis = vector.normalise(minorAxis)
    useHeight = min(max(0.0, height), 2.0 * magMajorAxis)

    totalRadians = math.acos((magMajorAxis - useHeight) / magMajorAxis)
    radians = 0.0

    arcLengthUp = geometry.getEllipseArcLength(magMajorAxis, magMinorAxis,
                                               radians, totalRadians,
                                               'integrate')
    elementsCountUp = elementsCountAround // 2
    elementArcLength = arcLengthUp / elementsCountUp

    radians = geometry.updateEllipseAngleByArcLength(magMajorAxis,
                                                     magMinorAxis,
                                                     radians,
                                                     -arcLengthUp,
                                                     method='Newton')
    for n1 in range(2 * elementsCountUp + 1):
        cosRadians = math.cos(radians)
        sinRadians = math.sin(radians)
        nx.append([
            (centre[c] + cosRadians * majorAxis[c] + sinRadians * minorAxis[c])
            for c in range(3)
        ])

        ndab = vector.setMagnitude(
            [-sinRadians * magMajorAxis, cosRadians * magMinorAxis],
            elementArcLength)
        nd1.append([(ndab[0] * unitMajorAxis[c] + ndab[1] * unitMinorAxis[c])
                    for c in range(3)])
        radians = geometry.updateEllipseAngleByArcLength(magMajorAxis,
                                                         magMinorAxis,
                                                         radians,
                                                         elementArcLength,
                                                         method='Newton')
    return nx, nd1
Exemplo n.º 7
0
def getDoubleCubicHermiteCurvesMidDerivative(ax, ad1, mx, bx, bd1):
    """
    Get derivative at centre of two cubic curves.
    :return: Derivative at mx to balance ax, ad1 with bx, bd1.
    """
    md1 = [ (bx[c] - ax[c]) for c in range(3) ]
    arcLengtha = computeCubicHermiteArcLength(ax, ad1, mx, md1, rescaleDerivatives = True)
    arcLengthb = computeCubicHermiteArcLength(mx, md1, bx, bd1, rescaleDerivatives = True)
    maga = vector.magnitude(ad1)
    magb = vector.magnitude(bd1)
    magm = arcLengtha + arcLengthb - 0.5*(maga + magb)
    return vector.setMagnitude(md1, magm)
Exemplo n.º 8
0
def interpolateNodesCubicHermite(cache, coordinates, xi, normal_scale, \
        node1, derivative1, scale1, cross_derivative1, cross_scale1, \
        node2, derivative2, scale2, cross_derivative2, cross_scale2):
    """
    Interpolates position and first derivative with cubic Hermite basis.
    Interpolates cross derivative linearly.
    :param cache: Field cache to evaluate in.
    :param coordinates: Coordinates field.
    :param xi: Element coordinate to interpolate at.
    :param normal_scale: Magnitude of normal derivative to return.
    :param node1, node2: Start and end nodes.
    :param derivative1, derivative2: Node value label for derivatives.
    :param scale1, scale2: Real value scaling derivatives, to reverse if needed.
    :param cross_derivative1, cross_derivative2: Node value label for cross derivatives.
    :param cross_scale1, cross_scale2: Real value scaling cross_derivatives, to reverse if needed.
    :return: x, dx_ds, dx_ds_cross, dx_ds_normal
    """
    cache.setNode(node1)
    result, v1 = coordinates.getNodeParameters(cache, -1,
                                               Node.VALUE_LABEL_VALUE, 1, 3)
    result, d1 = coordinates.getNodeParameters(cache, -1, derivative1, 1, 3)
    result, d1c = coordinates.getNodeParameters(cache, -1, cross_derivative1,
                                                1, 3)
    d1 = [scale1 * d for d in d1]
    d1c = [cross_scale1 * d for d in d1c]
    cache.setNode(node2)
    result, v2 = coordinates.getNodeParameters(cache, -1,
                                               Node.VALUE_LABEL_VALUE, 1, 3)
    result, d2 = coordinates.getNodeParameters(cache, -1, derivative2, 1, 3)
    result, d2c = coordinates.getNodeParameters(cache, -1, cross_derivative2,
                                                1, 3)
    d2 = [scale2 * d for d in d2]
    d2c = [cross_scale2 * d for d in d2c]

    arcLength = interp.computeCubicHermiteArcLength(v1, d1, v2, d2, True)
    mag = arcLength / vector.magnitude(d1)
    d1 = [mag * d for d in d1]
    mag = arcLength / vector.magnitude(d2)
    d2 = [mag * d for d in d2]

    xr = 1.0 - xi
    x = interp.interpolateCubicHermite(v1, d1, v2, d2, xi)
    dx_ds = interp.interpolateCubicHermiteDerivative(v1, d1, v2, d2, xi)
    scale = min(xi, xr)
    dx_ds = [scale * d for d in dx_ds]
    dx_ds_cross = [(xr * d1c[c] + xi * d2c[c]) for c in range(3)]

    radialVector = vector.normalise(vector.crossproduct3(dx_ds_cross, dx_ds))
    dx_ds_normal = [normal_scale * d for d in radialVector]

    return x, dx_ds, dx_ds_cross, dx_ds_normal
Exemplo n.º 9
0
def getCubicHermiteCurvatureSimple(v1, d1, v2, d2, xi):
    """
    :param v1, v2: Values at xi = 0.0 and xi = 1.0, respectively.
    :param d1, d2: Derivatives w.r.t. xi at xi = 0.0 and xi = 1.0, respectively.
    :param xi: Position in curve, nominally in [0.0, 1.0].
    :return: Scalar curvature (1/R) of the 1-D cubic Hermite curve.
    """

    tangent = interpolateCubicHermiteDerivative(v1, d1, v2, d2, xi)
    dTangent = interpolateCubicHermiteSecondDerivative(v1, d1, v2, d2, xi)
    cp = vector.crossproduct3(tangent, dTangent)
    curvature = vector.magnitude(cp) / (vector.magnitude(tangent)*vector.magnitude(tangent)*vector.magnitude(tangent))

    return curvature
Exemplo n.º 10
0
def createEllipsePoints(cx,
                        radian,
                        axis1,
                        axis2,
                        elementsCountAround,
                        startRadians=0.0):
    '''
    Create ellipse points centred at cx, from axis1 around through axis2.
    Assumes axis1 and axis2 are orthogonal.
    :param cx: centre
    :param radian: Part of ellipse to be created based on radian (will be 2*math.pi for a complete ellipse).
    :param axis1: Vector from cx to inside at zero angle
    :param axis2: Vector from cx to inside at 90 degree angle.
    :param elementsCountAround: Number of elements around.
    :param startRadians: Angle from axis1 to start creating the ellipse.
    :return: lists px, pd1
    '''
    px = []
    pd1 = []
    magAxis1 = vector.magnitude(axis1)
    magAxis2 = vector.magnitude(axis2)
    totalEllipsePerimeter = getApproximateEllipsePerimeter(magAxis1, magAxis2)
    partOfEllipsePerimeter = radian * totalEllipsePerimeter / (2 * math.pi)
    elementLength = partOfEllipsePerimeter / elementsCountAround
    if radian != 2 * math.pi:
        elementsCountAround = elementsCountAround + 1

    unitSideAxis1 = vector.normalise(axis1)
    unitSideAxis2 = vector.normalise(axis2)
    for n in range(elementsCountAround):
        angle = startRadians
        arcLength = n * elementLength
        newAngle = updateEllipseAngleByArcLength(magAxis1, magAxis2, angle,
                                                 arcLength)
        cosRadiansAround = math.cos(newAngle)
        sinRadiansAround = math.sin(newAngle)
        x = [
            cx[0] + cosRadiansAround * axis1[0] - sinRadiansAround * axis2[0],
            cx[1] + cosRadiansAround * axis1[1] + sinRadiansAround * axis2[1],
            cx[2] + cosRadiansAround * axis1[2] + sinRadiansAround * axis2[2]
        ]
        px.append(x)
        rd1 = [
            magAxis1 * (-sinRadiansAround * unitSideAxis1[c]) + magAxis2 *
            (cosRadiansAround * unitSideAxis2[c]) for c in range(3)
        ]
        rd1Norm = vector.normalise(rd1)
        pd1.append([elementLength * (rd1Norm[c]) for c in range(3)])

    return px, pd1
def getSemilunarValveSinusPoints(centre, axisSide1, axisSide2, radius, sinusRadialDisplacement, startMidCusp, elementsCountAround = 6):
    '''
    Get points around a circle of radius with every second point displaced radially.
    :return: x[], d1[] for 6 points around
    '''
    assert elementsCountAround == 6, 'getArterialRootLowerPoints.  Only supports 6 elements around'
    px = []
    pd1 = []
    # every second outer points is displaced by sinusRadialDisplacement to make sinus dilatation
    nMidCusp = 0 if startMidCusp else 1
    radiusPlus = radius + sinusRadialDisplacement
    radiansPerElementAround = 2.0*math.pi/elementsCountAround
    for n in range(elementsCountAround):
        radiansAround = n*radiansPerElementAround
        midCusp = (n % 2) == nMidCusp
        r = radiusPlus if midCusp else radius
        rcosRadiansAround = r*math.cos(radiansAround)
        rsinRadiansAround = r*math.sin(radiansAround)
        px.append([ (centre[c] + rcosRadiansAround*axisSide1[c] + rsinRadiansAround*axisSide2[c]) for c in range(3) ])
        pd1.append([ radiansPerElementAround*(-rsinRadiansAround*axisSide1[c] + rcosRadiansAround*axisSide2[c]) for c in range(3) ])
    # smooth to get derivative in sinus
    sd1 = interp.smoothCubicHermiteDerivativesLine(px[1 - nMidCusp:3 - nMidCusp], pd1[1 - nMidCusp:3 - nMidCusp], fixStartDerivative=True, fixEndDirection=True)
    magSinus = vector.magnitude(sd1[1])
    for n in range(nMidCusp, elementsCountAround, 2):
        pd1[n] = vector.setMagnitude(pd1[n], magSinus)

    return px, pd1
Exemplo n.º 12
0
 def __init__(self,
              elementsCountAcrossMajor,
              elementsCountAcrossMinor,
              centre=None,
              alongAxis=None,
              majorAxis=None,
              minorRadius=None):
     """
     :param elementsCountAcrossMajor: Number of elements across major axis. Must be at least 2 + elementsCountRim for
      half and 4 + elementsCountRim for full cylinder.
     :param elementsCountAcrossMinor: Number of elements across minor axis.
     :param centre: Centre of the ellipse.
     :param alongAxis: The cylinder axis that the base is extruded along.
     :param majorAxis: The major axis of the base. Should be perpendicular to alongAxis
     :param minorRadius: The minor radius of the ellipse.
     """
     self._centre = centre
     self._alongAxis = alongAxis
     self._majorAxis = majorAxis
     self._minorRadius = minorRadius
     if alongAxis:
         self._minorAxis = vector.setMagnitude(
             vector.crossproduct3(alongAxis, majorAxis), minorRadius)
     self._elementsCountAcrossMinor = elementsCountAcrossMinor
     self._elementsCountAcrossMajor = elementsCountAcrossMajor
     self._majorRadius = vector.magnitude(majorAxis)
     self.px = None
     self.pd1 = None
     self.pd2 = None
     self.pd3 = None
Exemplo n.º 13
0
    def __init__(self,
                 fieldModule,
                 coordinates,
                 elementsCountAlong,
                 base=None,
                 end=None,
                 cylinderShape=CylinderShape.CYLINDER_SHAPE_FULL,
                 tapered=None,
                 cylinderCentralPath=None,
                 useCrossDerivatives=False):
        """
        :param fieldModule: Zinc fieldModule to create elements in.
        :param coordinates: Coordinate field to define.
        :param base: Cylinder base ellipse. It is an instance of class CylinderEnds.
        :param end: Cylinder end ellipse. It is an instance of class CylinderEnds.
        :param elementsCountAlong: Number of elements along the cylinder axis.
        :param cylinderShape: A value from enum CylinderMode specifying.
        """

        self._centres = None
        self._majorAxis = None
        self._minorAxis = None
        self._majorRadii = None
        self._minorRadii = None
        self._base = base
        self._end = end
        self._shield = None
        self._elementsCountAcrossMinor = base._elementsCountAcrossMinor
        self._elementsCountAcrossMajor = base._elementsCountAcrossMajor
        self._elementsCountUp = base._elementsCountAcrossMajor // 2 \
            if cylinderShape == CylinderShape.CYLINDER_SHAPE_FULL else base._elementsCountAcrossMajor
        self._elementsCountAlong = elementsCountAlong
        self._elementsCountAround = 2 * (self._elementsCountUp -
                                         2) + self._elementsCountAcrossMinor
        self._startNodeIdentifier = 1
        self._startElementIdentifier = 1
        self._endNodeIdentifier = 1
        self._endElementIdentifier = 1
        self._cylinderShape = cylinderShape
        self._cylinderType = CylinderType.CYLINDER_STRAIGHT
        if (tapered is not None) or cylinderCentralPath:
            self._cylinderType = CylinderType.CYLINDER_TAPERED
            self._tapered = tapered
        self._useCrossDerivatives = useCrossDerivatives
        self._cylinderCentralPath = cylinderCentralPath
        if cylinderCentralPath:
            self.calculateEllipseParams(
                cylinderCentralPath=self._cylinderCentralPath)
            self._base = CylinderEnds(base._elementsCountAcrossMajor,
                                      base._elementsCountAcrossMinor,
                                      self._centres[0], None,
                                      self._majorAxis[0], self._minorRadii[0])
        else:
            self._length = vector.magnitude(base._alongAxis)
            arcLengthAlong = self._length / elementsCountAlong
            self.calculateEllipseParams(
                arcLengthAlong, cylinderCentralPath=self._cylinderCentralPath)

        # generate the mesh
        self.createCylinderMesh3d(fieldModule, coordinates)
Exemplo n.º 14
0
def computeCubicHermiteDerivativeScaling(v1, d1, v2, d2):
    '''
    Compute scaling for d1, d2 which makes their sum twice the arc length.
    :return: Scale factor to multiply d1, d2
    '''
    origMag = 0.5*(vector.magnitude(d1) + vector.magnitude(d2))
    scaling = 1.0
    for iters in range(100):
        mag = origMag*scaling
        arcLength = getCubicHermiteArcLength(v1, [ d*scaling for d in d1 ], v2, [ d*scaling for d in d2 ])
        if math.fabs(arcLength - mag) < 0.000001*arcLength:
            #print('compute scaling', v1, d1, v2, d2, '\n  --> scaling',scaling)
            return scaling
        scaling *= arcLength/mag
    print('computeCubicHermiteDerivativeScaling:  Max iters reached:', iters, ' mag', mag, 'arc', arcLength)
    return scaling
Exemplo n.º 15
0
 def __init__(self, mirrorPlane):
     '''
     :param mirrorPlane: Plane ax+by+cd=d defined as a list p=[a,b,c,d].
     '''
     self._plane = mirrorPlane
     self._planeUnitNormalVector = self.getPlaneNormalVector()
     self._magNormal = vector.magnitude(mirrorPlane[:3])
     self._planeDParam = mirrorPlane[3]
Exemplo n.º 16
0
def calculate_surface_delta_xi(d1, d2, direction):
    '''
    Calculate dxi1, dxi2 in 3-D vector direction.
    :param d1, d2: Derivatives of coordinate w.r.t. xi1, xi2.
    :param direction: 3-D vector.
    :return: delta_xi1, delta_xi2
    '''
    # overdetermined system (3-D vector, 2-D surface), solve least squares
    # A transpose A x = A transpose b
    a = [[0.0, 0.0], [0.0, 0.0]]
    b = [0.0, 0.0]
    dx_dxi = [d1, d2]
    for i in range(2):
        for j in range(2):
            for k in range(3):
                a[i][j] += dx_dxi[i][k] * dx_dxi[j][k]
        for k in range(3):
            b[i] += dx_dxi[i][k] * direction[k]
    # 2x2 matrix inverse
    deta = a[0][0] * a[1][1] - a[0][1] * a[1][0]
    if deta > 0.0:
        inva = [[a[1][1] / deta, -a[0][1] / deta],
                [-a[1][0] / deta, a[0][0] / deta]]
        delta_xi1 = inva[0][0] * b[0] + inva[0][1] * b[1]
        delta_xi2 = inva[1][0] * b[0] + inva[1][1] * b[1]
    else:
        # at pole: assume direction is inline with d1 or d2 and other is zero
        delta_xi2 = vector.dotproduct(d2, direction)
        if math.fabs(delta_xi2) > 0.0:
            delta_xi1 = 0.0
            delta_xi2 = (1.0 if (delta_xi2 > 0.0) else -1.0
                         ) * vector.magnitude(direction) / vector.magnitude(d2)
        else:
            delta_xi1 = vector.dotproduct(d1, direction)
            if math.fabs(delta_xi1) > 0.0:
                delta_xi1 = (1.0 if
                             (delta_xi1 > 0.0) else -1.0) * vector.magnitude(
                                 direction) / vector.magnitude(d1)
                delta_xi2 = 0.0
    #delx = [ (delta_xi1*d1[c] + delta_xi2*d2[c]) for c in range(3) ]
    #print('delx', delx, 'dir', direction, 'diff', vector.magnitude([ (delx[c] - direction[c]) for c in range(3) ]))
    return delta_xi1, delta_xi2
Exemplo n.º 17
0
 def makeD2Normal(cls, region, options, editGroupName):
     if not options['D2 derivatives']:
         return
     d1, d2 = extractPathParametersFromRegion(
         region, [Node.VALUE_LABEL_D_DS1, Node.VALUE_LABEL_D_DS2])
     for c in range(len(d1)):
         td2 = vector.vectorRejection(d2[c], d1[c])
         d2[c] = vector.setMagnitude(td2, vector.magnitude(d2[c]))
     setPathParameters(region, [Node.VALUE_LABEL_D_DS2], [d2],
                       editGroupName)
     return False, True  # settings not changed, nodes changed
Exemplo n.º 18
0
 def makeD3Normal(cls, region, options, editGroupName):
     if not options['D3 derivatives']:
         return
     if options['D2 derivatives']:
         d1, d2, d3 = extractPathParametersFromRegion(
             region, [
                 Node.VALUE_LABEL_D_DS1, Node.VALUE_LABEL_D_DS2,
                 Node.VALUE_LABEL_D_DS3
             ])
         for c in range(len(d1)):
             d3[c] = vector.setMagnitude(vector.crossproduct3(d1[c], d2[c]),
                                         vector.magnitude(d3[c]))
     else:
         d1, d3 = extractPathParametersFromRegion(
             region, [Node.VALUE_LABEL_D_DS1, Node.VALUE_LABEL_D_DS3])
         for c in range(len(d1)):
             td3 = vector.vectorRejection(d3[c], d1[c])
             d3[c] = vector.setMagnitude(td3, vector.magnitude(d3[c]))
     setPathParameters(region, [Node.VALUE_LABEL_D_DS3], [d3],
                       editGroupName)
     return False, True  # settings not changed, nodes changed
Exemplo n.º 19
0
def getCubicHermiteCurvature(v1, d1, v2, d2, radialVector, xi):
    """
    :param radialVector: Radial direction, assumed unit normal to curve tangent at point.
    :return: Scalar curvature (1/R) of the 1-D cubic Hermite curve.
    """
    tangent = interpolateCubicHermiteDerivative(v1, d1, v2, d2, xi)
    dTangent = interpolateCubicHermiteSecondDerivative(v1, d1, v2, d2, xi)
    #tangentVector = vector.normalise(tangent)
    #tangentCurvature = vector.dotproduct(dTangent, tangentVector)
    radialCurvature = vector.dotproduct(dTangent, radialVector)
    magTangent = vector.magnitude(tangent)
    curvature = radialCurvature / (magTangent * magTangent)
    return curvature
Exemplo n.º 20
0
def getCubicHermiteCurvature(v1, d1, v2, d2, radialVector, xi):
    """
    :param v1, v2: Values at xi = 0.0 and xi = 1.0, respectively.
    :param d1, d2: Derivatives w.r.t. xi at xi = 0.0 and xi = 1.0, respectively.
    :param radialVector: Radial direction, assumed unit normal to curve tangent at point.
    :param xi: Position in curve, nominally in [0.0, 1.0].
    :return: Scalar curvature (1/R) of the 1-D cubic Hermite curve.
    """
    tangent = interpolateCubicHermiteDerivative(v1, d1, v2, d2, xi)
    dTangent = interpolateCubicHermiteSecondDerivative(v1, d1, v2, d2, xi)
    #tangentVector = vector.normalise(tangent)
    #tangentCurvature = vector.dotproduct(dTangent, tangentVector)
    radialCurvature = vector.dotproduct(dTangent, radialVector)
    magTangent = vector.magnitude(tangent)
    curvature = radialCurvature / (magTangent * magTangent)

    return curvature
Exemplo n.º 21
0
def calculate_surface_axes(d1, d2, direction):
    '''
    :return: Vectors ax1, ax2, ax3: ax1 in-plane in 3-D vector direction,
    ax2 in-plane normal to a and ax3 normal to the surface plane.
    Vectors all have unit magnitude.
    '''
    delta_xi1, delta_xi2 = calculate_surface_delta_xi(d1, d2, direction)
    ax1 = vector.normalise(
        [delta_xi1 * d1[c] + delta_xi2 * d2[c] for c in range(3)])
    ax3 = vector.crossproduct3(d1, d2)
    mag3 = vector.magnitude(ax3)
    if mag3 > 0.0:
        ax3 = [s / mag3 for s in ax3]
        ax2 = vector.normalise(vector.crossproduct3(ax3, ax1))
    else:
        ax3 = [0.0, 0.0, 0.0]
        ax2 = [0.0, 0.0, 0.0]
    return ax1, ax2, ax3
Exemplo n.º 22
0
 def __init__(self,
              elementsCountAcrossMajor,
              elementsCountAcrossMinor,
              elementsCountAcrossShell=0,
              elementsCountAcrossTransition=1,
              shellProportion=1.0,
              centre=None,
              alongAxis=None,
              majorAxis=None,
              minorRadius=None):
     """
     :param elementsCountAcrossMajor: Number of elements across major axis. Must be at least 2 + elementsCountRim for
      half and 4 + elementsCountRim for full cylinder.
     :param elementsCountAcrossMinor: Number of elements across minor axis.
     :param elementsCountAcrossShell: Number of elements across shell.
     :param elementsCountAcrossTransition: Number of elements between core boundary and inner square.
     :param shellProportion: Ratio of thickness of each layer in shell wrt thickness of each layer in core.
     :param centre: Centre of the ellipse.
     :param alongAxis: The cylinder axis that the base is extruded along.
     :param majorAxis: The major axis of the base. Should be perpendicular to alongAxis
     :param minorRadius: The minor radius of the ellipse.
     """
     self._centre = centre
     self._alongAxis = alongAxis
     self._majorAxis = majorAxis
     self._minorRadius = minorRadius
     if alongAxis:
         self._minorAxis = vector.setMagnitude(
             vector.crossproduct3(alongAxis, majorAxis), minorRadius)
     self._elementsCountAcrossMinor = elementsCountAcrossMinor
     self._elementsCountAcrossMajor = elementsCountAcrossMajor
     self._elementsCountAcrossShell = elementsCountAcrossShell
     self._elementsCountAcrossTransition = elementsCountAcrossTransition
     self._shellProportion = shellProportion
     self._majorRadius = vector.magnitude(majorAxis)
     self.px = None
     self.pd1 = None
     self.pd2 = None
     self.pd3 = None
Exemplo n.º 23
0
    def generateBase1DMesh(self, rx):
        """
        Generate nodes around the perimeter of the ellipse.
        """
        btx = self.px
        btd1 = self.pd1
        btd2 = self.pd2
        btd3 = self.pd3

        ratio = rx / self.elementsCountAcrossShell if self.elementsCountAcrossShell > 0 else 0
        majorAxis = [
            d * (1 - ratio * (1 - self.coreMajorRadius / self.majorRadius))
            for d in self.majorAxis
        ]
        minorAxis = [
            d * (1 - ratio * (1 - self.coreMinorRadius / self.minorRadius))
            for d in self.minorAxis
        ]
        majorRadius = vector.magnitude(majorAxis)

        nx, nd1 = createEllipsePerimeter(self.centre, majorAxis, minorAxis,
                                         self.elementsCountAround, majorRadius)
        nte = normalToEllipse(self.majorAxis, self.minorAxis)

        tbx, tbd1, tbd2, tbd3 = [], [], [], []
        for n in range(self.elementsCountAround + 1):
            tbx.append(nx[n])
            tbd1.append(nd1[n])
            tbd2.append(nte)
            tbd3.append(vector.normalise(vector.crossproduct3(tbd1[n], nte)))

        for n in range(self.elementsCountAround + 1):
            n1, n2 = self.__shield.convertRimIndex(n, rx)
            btx[n2][n1] = tbx[n]
            btd1[n2][n1] = tbd1[n]
            btd2[n2][n1] = tbd2[n]
            btd3[n2][n1] = tbd3[n]
Exemplo n.º 24
0
def createAnnulusMesh3d(nodes,
                        mesh,
                        nextNodeIdentifier,
                        nextElementIdentifier,
                        startPointsx,
                        startPointsd1,
                        startPointsd2,
                        startPointsd3,
                        startNodeId,
                        startDerivativesMap,
                        endPointsx,
                        endPointsd1,
                        endPointsd2,
                        endPointsd3,
                        endNodeId,
                        endDerivativesMap,
                        forceStartLinearXi3=False,
                        forceMidLinearXi3=False,
                        forceEndLinearXi3=False,
                        maxStartThickness=None,
                        maxEndThickness=None,
                        useCrossDerivatives=False,
                        elementsCountRadial=1,
                        meshGroups=None,
                        wallAnnotationGroups=None,
                        tracksurface=None,
                        startProportions=None,
                        endProportions=None,
                        rescaleStartDerivatives=False,
                        rescaleEndDerivatives=False,
                        sampleBlend=0.0):
    """
    Create an annulus mesh from a loop of start points/nodes with specified derivative mappings to
    a loop of end points/nodes with specified derivative mappings.
    Derivative d3 is through the wall. Currently limited to single element layer through wall.
    Points/nodes order cycles fastest around the annulus, then through the wall.
    Note doesn't support cross derivatives.
    Arrays are indexed by n3 (node through wall, size 2), n2 (node along/radial), n1 (node around, variable size)
    and coordinate component c.
    :param nodes: The nodeset to create nodes in.
    :param mesh: The mesh to create elements in.
    :param nextNodeIdentifier, nextElementIdentifier: Next identifiers to use and increment.
    :param startPointsx, startPointsd1, startPointsd2, startPointsd3, endPointsx, endPointsd1, endPointsd2, endPointsd3:
        List array[n3][n1][c] or start/point coordinates and derivatives. To linearise through the wall, pass None to
        d3. If both ends are linear through the wall, interior points are linear through the wall.
    :param startNodeId, endNodeId: List array [n3][n1] of existing node identifiers to use at start/end. Pass None for
        argument if no nodes are specified at end. These arguments are 'all or nothing'.
    :param startDerivativesMap, endDerivativesMap: List array[n3][n1] of mappings for d/dxi1, d/dxi2, d/dxi3 at
        start/end of form:
        ( (1, -1, 0), (1, 0, 0), None ) where the first tuple means d/dxi1 = d/ds1 - d/ds2. Only 0, 1 and -1 may be
        used.
        None means use default e.g. d/dxi2 = d/ds2.
        Pass None for the entire argument to use the defaults d/dxi1 = d/ds1, d/dxi2 = d/ds2, d/dxi3 = d/ds3.
        Pass a 4th mapping to apply to d/dxi1 on other side of node; if not supplied first mapping applies both sides.
    :param forceStartLinearXi3, forceMidLinearXi3, forceEndLinearXi3: Force start, middle or
        end elements to be linear through the wall, even if d3 is supplied at either end.
        Can only use forceMidLinearXi3 only if at least one end is linear in d3.
    :param maxStartThickness, maxEndThickness: Optional maximum override on start/end thicknesses.
    :param useCrossDerivatives: May only be True if no derivatives maps are in use.
    :param elementsCountRadial: Optional number of elements in radial direction between start and end.
    :param meshGroups:  Optional sequence of Zinc MeshGroup for adding all new elements to, or a sequence of
    length elementsCountRadial containing sequences of mesh groups to add rows of radial elements to
    from start to end.
    :param wallAnnotationGroups: Annotation groups for adding all new elements to a sequence
    of groups to add to elements through wall.
    :param tracksurface: Description for outer surface representation used for creating annulus mesh. Provides
    information for creating radial nodes on annulus that sit on tracksurface. Need startProportions and endProportions
    to work.
    :param startProportions: Proportion around and along of startPoints on tracksurface. These vary with nodes
    around as for startPoints. Values only given for tracksurface for outer layer (xi3 == 1).
    :param endProportions: Proportion around and along of endPoints on track surface. These vary with nodes
    around as for endPoints. Values only given for tracksurface for outer layer (xi3 == 1).
    :param rescaleStartDerivatives, rescaleEndDerivatives: Optional flags to compute and multiply additional scale
    factors on start, end or both radial derivatives to fit arc length, needed if derivatives are of the wrong scale
    for the radial distances and the chosen elementsCountRadial. If either is True, derivatives and sampled radial
    nodes are spaced for a gradual change of derivative from that at the other end. If both are True, scaling is set to
    give even sampling and arclength derivatives.
    :param sampleBlend: Real value varying from 0.0 to 1.0 controlling weighting of start and end
    derivatives when interpolating extra points in-between, where 0.0 = sample with equal end derivatives,
    and 1.0 = proportional to current magnitudes, interpolated in between.
    :return: Final values of nextNodeIdentifier, nextElementIdentifier
    """
    assert (elementsCountRadial >=
            1), 'createAnnulusMesh3d:  Invalid number of radial elements'
    startLinearXi3 = (not startPointsd3) or forceStartLinearXi3
    endLinearXi3 = (not endPointsd3) or forceEndLinearXi3
    midLinearXi3 = (startLinearXi3 and endLinearXi3) or (
        (startLinearXi3 or endLinearXi3) and forceMidLinearXi3)
    # get list whether each row of nodes in elements is linear in Xi3
    # this is for element use; start/end nodes may have d3 even if element is linear
    rowLinearXi3 = [
        startLinearXi3
    ] + [midLinearXi3] * (elementsCountRadial - 1) + [endLinearXi3]
    assert (not useCrossDerivatives) or ((not startDerivativesMap) and (not endDerivativesMap)), \
        'createAnnulusMesh3d:  Cannot use cross derivatives with derivatives map'
    nodesCountWall = len(startPointsx)
    assert (len(startPointsd1) == nodesCountWall) and (len(startPointsd2) == nodesCountWall) and \
           (startLinearXi3 or (len(startPointsd3) == nodesCountWall)) and \
           (len(endPointsx) == nodesCountWall) and (len(endPointsd1) == nodesCountWall) and \
           (len(endPointsd2) == nodesCountWall) and (endLinearXi3 or (len(endPointsd3) == nodesCountWall)) and \
           ((startNodeId is None) or (len(startNodeId) == nodesCountWall)) and \
           ((endNodeId is None) or (len(endNodeId) == nodesCountWall)) and \
           ((startDerivativesMap is None) or (len(startDerivativesMap) == nodesCountWall)) and \
           ((endDerivativesMap is None) or (len(endDerivativesMap) == nodesCountWall)),\
        'createAnnulusMesh3d:  Mismatch in number of layers through wall'
    elementsCountAround = nodesCountAround = len(startPointsx[0])
    assert (
        nodesCountAround > 1
    ), 'createAnnulusMesh3d:  Invalid number of points/nodes around annulus'
    for n3 in range(nodesCountWall):
        assert (len(startPointsx[n3]) == nodesCountAround) and (len(startPointsd1[n3]) == nodesCountAround) and \
               (len(startPointsd2[n3]) == nodesCountAround) and \
               (startLinearXi3 or (len(startPointsd3[n3]) == nodesCountAround)) and\
               (len(endPointsx[n3]) == nodesCountAround) and (len(endPointsd1[n3]) == nodesCountAround) and \
               (len(endPointsd2[n3]) == nodesCountAround) and \
               (endLinearXi3 or (len(endPointsd3[n3]) == nodesCountAround)) and \
               ((startNodeId is None) or (len(startNodeId[n3]) == nodesCountAround)) and\
               ((endNodeId is None) or (len(endNodeId[n3]) == nodesCountAround)) and \
               ((startDerivativesMap is None) or (len(startDerivativesMap[n3]) == nodesCountAround)) and \
               ((endDerivativesMap is None) or (len(endDerivativesMap[n3]) == nodesCountAround)), \
            'createAnnulusMesh3d:  Mismatch in number of points/nodes in layers through wall'
    rowMeshGroups = meshGroups
    if meshGroups:
        assert isinstance(
            meshGroups,
            Sequence), 'createAnnulusMesh3d:  Mesh groups is not a sequence'
        if (len(meshGroups) == 0) or (not isinstance(meshGroups[0], Sequence)):
            rowMeshGroups = [meshGroups] * elementsCountRadial
        else:
            assert len(meshGroups) == elementsCountRadial, \
                'createAnnulusMesh3d:  Length of meshGroups sequence does not equal elementsCountRadial'
    if wallAnnotationGroups:
        assert len(wallAnnotationGroups) == nodesCountWall - 1, \
            'createAnnulusMesh3d:  Length of wallAnnotationGroups sequence does not equal elementsCountThroughWall'
    if tracksurface:
        assert startProportions and endProportions, \
            'createAnnulusMesh3d: Missing start and/or end proportions for use with tracksurface'
        assert len(startProportions) == nodesCountAround, \
            'createAnnulusMesh3d: Length of startProportions does not equal nodesCountAround'
        assert len(endProportions) == nodesCountAround, \
            'createAnnulusMesh3d: Length of endProportions does not equal nodesCountAround'

    fm = mesh.getFieldmodule()
    fm.beginChange()
    cache = fm.createFieldcache()
    coordinates = findOrCreateFieldCoordinates(fm)

    # Build arrays of points from start to end
    px = [[] for n3 in range(nodesCountWall)]
    pd1 = [[] for n3 in range(nodesCountWall)]
    pd2 = [[] for n3 in range(nodesCountWall)]
    pd3 = [[] for n3 in range(nodesCountWall)]

    # Find total wall thickness
    thicknessProportions = []
    thicknesses = []
    thicknesses.append([
        vector.magnitude([
            (startPointsx[nodesCountWall - 1][n1][c] - startPointsx[0][n1][c])
            for c in range(3)
        ]) for n1 in range(nodesCountAround)
    ])
    for n2 in range(1, elementsCountRadial):
        thicknesses.append([None] * nodesCountAround)
    thicknesses.append([
        vector.magnitude([
            (endPointsx[nodesCountWall - 1][n1][c] - endPointsx[0][n1][c])
            for c in range(3)
        ]) for n1 in range(nodesCountAround)
    ])

    for n3 in range(nodesCountWall):
        px[n3] = [startPointsx[n3], endPointsx[n3]]
        pd1[n3] = [startPointsd1[n3], endPointsd1[n3]]
        pd2[n3] = [startPointsd2[n3], endPointsd2[n3]]
        pd3[n3] = [
            startPointsd3[n3] if (startPointsd3 is not None) else None,
            endPointsd3[n3] if (endPointsd3 is not None) else None
        ]

        startThicknessList = \
            [vector.magnitude([(startPointsx[n3][n1][c] - startPointsx[n3 - (1 if n3 > 0 else 0)][n1][c])
                               for c in range(3)]) for n1 in range(len(startPointsx[n3]))]
        endThicknessList = \
            [vector.magnitude([(endPointsx[n3][n1][c] - endPointsx[n3 - (1 if n3 > 0 else 0)][n1][c])
                               for c in range(3)]) for n1 in range(len(endPointsx[n3]))]
        thicknessList = [startThicknessList,
                         endThicknessList]  # thickness of each layer

        startThicknessProportions = [
            thicknessList[0][c] / thicknesses[0][c]
            for c in range(nodesCountAround)
        ]
        endThicknessProportions = [
            thicknessList[1][c] / thicknesses[-1][c]
            for c in range(nodesCountAround)
        ]
        thicknessProportions.append(
            [startThicknessProportions, endThicknessProportions])

    if rescaleStartDerivatives:
        scaleFactorMapStart = [[] for n3 in range(nodesCountWall)]
    if rescaleEndDerivatives:
        scaleFactorMapEnd = [[] for n3 in range(nodesCountWall)]

    # following code adds in-between points, but also handles rescaling for 1 radial element
    for n3 in range(nodesCountWall):
        for n2 in range(1, elementsCountRadial):
            px[n3].insert(n2, [None] * nodesCountAround)
            pd1[n3].insert(n2, [None] * nodesCountAround)
            pd2[n3].insert(n2, [None] * nodesCountAround)
            pd3[n3].insert(n2,
                           None if midLinearXi3 else [None] * nodesCountAround)
            thicknessProportions[n3].insert(n2, [None] * nodesCountAround)

    if maxStartThickness:
        for n1 in range(nodesCountAround):
            thicknesses[0][n1] = min(thicknesses[0][n1], maxStartThickness)
    if maxEndThickness:
        for n1 in range(nodesCountAround):
            thicknesses[-1][n1] = min(thicknesses[-1][n1], maxEndThickness)
    n3 = nodesCountWall - 1
    for n1 in range(nodesCountAround):
        ax = startPointsx[n3][n1]
        ad1, ad2 = getMappedD1D2(
            [startPointsd1[n3][n1], startPointsd2[n3][n1]] +
            ([startPointsd3[n3][n1]] if startPointsd3 else []),
            startDerivativesMap[n3][n1] if startDerivativesMap else None)
        bx = endPointsx[n3][n1]
        bd1, bd2 = getMappedD1D2(
            [endPointsd1[n3][n1], endPointsd2[n3][n1]] +
            ([endPointsd3[n3][n1]] if endPointsd3 else []),
            endDerivativesMap[n3][n1] if endDerivativesMap else None)

        # sample between start and end points and derivatives
        # scaling end derivatives to arc length gives even curvature along the curve
        aMag = vector.magnitude(ad2)
        bMag = vector.magnitude(bd2)
        ad2Scaled = vector.setMagnitude(
            ad2,
            0.5 * ((1.0 + sampleBlend) * aMag + (1.0 - sampleBlend) * bMag))
        bd2Scaled = vector.setMagnitude(
            bd2,
            0.5 * ((1.0 + sampleBlend) * bMag + (1.0 - sampleBlend) * aMag))
        scaling = interp.computeCubicHermiteDerivativeScaling(
            ax, ad2Scaled, bx, bd2Scaled)
        ad2Scaled = [d * scaling for d in ad2Scaled]
        bd2Scaled = [d * scaling for d in bd2Scaled]
        derivativeMagnitudeStart = None if rescaleStartDerivatives else vector.magnitude(
            ad2)
        derivativeMagnitudeEnd = None if rescaleEndDerivatives else vector.magnitude(
            bd2)
        if tracksurface:
            mx, md2, md1, md3, mProportions = \
                tracksurface.createHermiteCurvePoints(startProportions[n1][0], startProportions[n1][1],
                                                      endProportions[n1][0], endProportions[n1][1], elementsCountRadial,
                                                      derivativeStart=[d / elementsCountRadial for d in ad2Scaled],
                                                      derivativeEnd=[d / elementsCountRadial for d in bd2Scaled])
            mx, md2, md1 = \
                tracksurface.resampleHermiteCurvePointsSmooth(mx, md2, md1, md3, mProportions,
                                                              derivativeMagnitudeStart, derivativeMagnitudeEnd)[0:3]
            # interpolate thicknesses using xi calculated from radial arclength distances to points
            arcLengthInsideToRadialPoint = \
                [0.0] + [interp.getCubicHermiteArcLength(mx[n2], md2[n2], mx[n2 + 1], md2[n2 + 1])
                         for n2 in range(elementsCountRadial)]
            arclengthInsideToOutside = sum(arcLengthInsideToRadialPoint)
            thi = []
            for n2 in range(elementsCountRadial + 1):
                xi2 = arcLengthInsideToRadialPoint[
                    n2 - 1] / arclengthInsideToOutside
                thi.append(thicknesses[-1][n1] * xi2 + thicknesses[0][n1] *
                           (1.0 - xi2))
            thiProportion = []
            for m3 in range(nodesCountWall):
                thiProportionRadial = []
                for n2 in range(elementsCountRadial + 1):
                    xi2 = arcLengthInsideToRadialPoint[
                        n2 - 1] / arclengthInsideToOutside
                    thiProportionRadial.append(
                        thicknessProportions[m3][-1][n1] * xi2 +
                        thicknessProportions[m3][0][n1] * (1.0 - xi2))
                thiProportion.append(thiProportionRadial)
        else:
            mx, md2, me, mxi = interp.sampleCubicHermiteCurvesSmooth(
                [ax, bx], [ad2Scaled, bd2Scaled], elementsCountRadial,
                derivativeMagnitudeStart, derivativeMagnitudeEnd)[0:4]
            md1 = interp.interpolateSampleLinear([ad1, bd1], me, mxi)
            thi = interp.interpolateSampleLinear(
                [thicknesses[0][n1], thicknesses[-1][n1]], me, mxi)
            thiProportion = []
            for m3 in range(nodesCountWall):
                thiProportion.append(
                    interp.interpolateSampleLinear([
                        thicknessProportions[m3][0][n1],
                        thicknessProportions[m3][-1][n1]
                    ], me, mxi))

        # set scalefactors if rescaling, make same on inside for now
        if rescaleStartDerivatives:
            scaleFactor = vector.magnitude(md2[0]) / vector.magnitude(ad2)
            scaleFactorMapStart[n3].append(scaleFactor)
        if rescaleEndDerivatives:
            scaleFactor = vector.magnitude(md2[-1]) / vector.magnitude(bd2)
            scaleFactorMapEnd[n3].append(scaleFactor)

        for n2 in range(1, elementsCountRadial):
            px[n3][n2][n1] = mx[n2]
            pd1[n3][n2][n1] = md1[n2]
            pd2[n3][n2][n1] = md2[n2]
            thicknesses[n2][n1] = thi[n2]
            for m3 in range(nodesCountWall):
                thicknessProportions[m3][n2][n1] = thiProportion[m3][n2]

    xi3List = [[[[] for n1 in range(nodesCountAround)]
                for n2 in range(elementsCountRadial + 1)]
               for n3 in range(nodesCountWall)]
    for n1 in range(nodesCountAround):
        for n2 in range(elementsCountRadial + 1):
            xi3 = 0.0
            for n3 in range(nodesCountWall):
                xi3 += thicknessProportions[n3][n2][n1]
                xi3List[n3][n2][n1] = xi3

    # now get inner positions from normal and thickness, derivatives from curvature
    for n2 in range(1, elementsCountRadial):
        # first smooth derivative 1 around outer loop
        pd1[-1][n2] = \
            interp.smoothCubicHermiteDerivativesLoop(px[-1][n2], pd1[-1][n2],
                                                     magnitudeScalingMode=interp.DerivativeScalingMode.HARMONIC_MEAN)

        for n3 in range(0, nodesCountWall - 1):
            for n1 in range(nodesCountAround):
                xi3 = 1 - xi3List[n3][n2][n1]
                normal = vector.normalise(
                    vector.crossproduct3(pd1[-1][n2][n1], pd2[-1][n2][n1]))
                thickness = thicknesses[n2][n1] * xi3
                d3 = [d * thickness for d in normal]
                px[n3][n2][n1] = [(px[-1][n2][n1][c] - d3[c])
                                  for c in range(3)]
                # calculate inner d1 from curvature around
                n1m = n1 - 1
                n1p = (n1 + 1) % nodesCountAround
                curvature = 0.5 * (interp.getCubicHermiteCurvature(
                    px[-1][n2][n1m], pd1[-1][n2][n1m], px[-1][n2][n1], pd1[-1]
                    [n2][n1], normal, 1.0) + interp.getCubicHermiteCurvature(
                        px[-1][n2][n1], pd1[-1][n2][n1], px[-1][n2][n1p],
                        pd1[-1][n2][n1p], normal, 0.0))
                factor = 1.0 + curvature * thickness
                pd1[n3][n2][n1] = [factor * d for d in pd1[-1][n2][n1]]
                # calculate inner d2 from curvature radially
                n2m = n2 - 1
                n2p = n2 + 1
                curvature = 0.5 * (interp.getCubicHermiteCurvature(
                    px[-1][n2m][n1], pd2[-1][n2m][n1], px[-1][n2][n1], pd2[-1]
                    [n2][n1], normal, 1.0) + interp.getCubicHermiteCurvature(
                        px[-1][n2][n1], pd2[-1][n2][n1], px[-1][n2p][n1],
                        pd2[-1][n2p][n1], normal, 0.0))
                factor = 1.0 + curvature * thickness
                pd2[n3][n2][n1] = [factor * d for d in pd2[-1][n2][n1]]
                d2Scaled = [factor * d for d in pd2[-1][n2][n1]]
                if vector.dotproduct(vector.normalise(pd2[-1][n2][n1]),
                                     vector.normalise(d2Scaled)) == -1:
                    pd2[n3][n2][n1] = [-factor * d for d in pd2[-1][n2][n1]]
                if not midLinearXi3:
                    pd3[n3][n2][n1] = pd3[-1][n2][n1] = \
                        [d * thicknesses[n2][n1] * thicknessProportions[n3 + 1][n2][n1] for d in normal]

            # smooth derivative 1 around inner loop
            pd1[n3][n2] = interp.smoothCubicHermiteDerivativesLoop(
                px[n3][n2],
                pd1[n3][n2],
                magnitudeScalingMode=interp.DerivativeScalingMode.HARMONIC_MEAN
            )

    for n3 in range(0, nodesCountWall):
        # smooth derivative 2 radially/along annulus
        for n1 in range(nodesCountAround):
            mx = [px[n3][n2][n1] for n2 in range(elementsCountRadial + 1)]
            md2 = [pd2[n3][n2][n1] for n2 in range(elementsCountRadial + 1)]
            # replace mapped start/end d2
            md2[0] = getMappedD1D2(
                [startPointsd1[n3][n1], startPointsd2[n3][n1]] +
                ([startPointsd3[n3][n1]] if startPointsd3 else []),
                startDerivativesMap[n3][n1]
                if startDerivativesMap else None)[1]
            md2[-1] = getMappedD1D2(
                [endPointsd1[n3][n1], endPointsd2[n3][n1]] +
                ([endPointsd3[n3][n1]] if endPointsd3 else []),
                endDerivativesMap[n3][n1] if endDerivativesMap else None)[1]

            sd2 = interp.smoothCubicHermiteDerivativesLine(
                mx,
                md2,
                fixAllDirections=True,
                fixStartDerivative=not rescaleStartDerivatives,
                fixStartDirection=rescaleStartDerivatives,
                fixEndDerivative=not rescaleEndDerivatives,
                fixEndDirection=rescaleEndDerivatives,
                magnitudeScalingMode=interp.DerivativeScalingMode.HARMONIC_MEAN
            )
            if rescaleStartDerivatives:
                scaleFactor = vector.magnitude(sd2[0]) / vector.magnitude(
                    md2[0])
                scaleFactorMapStart[n3].append(scaleFactor)
            if rescaleEndDerivatives:
                scaleFactor = vector.magnitude(sd2[-1]) / vector.magnitude(
                    md2[-1])
                scaleFactorMapEnd[n3].append(scaleFactor)

            for n2 in range(1, elementsCountRadial):
                pd2[n3][n2][n1] = sd2[n2]

    ##############
    # Create 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)
    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)
    nodetemplateLinearS3 = nodes.createNodetemplate()
    nodetemplateLinearS3.defineField(coordinates)
    nodetemplateLinearS3.setValueNumberOfVersions(coordinates, -1,
                                                  Node.VALUE_LABEL_VALUE, 1)
    nodetemplateLinearS3.setValueNumberOfVersions(coordinates, -1,
                                                  Node.VALUE_LABEL_D_DS1, 1)
    nodetemplateLinearS3.setValueNumberOfVersions(coordinates, -1,
                                                  Node.VALUE_LABEL_D_DS2, 1)
    if useCrossDerivatives:
        nodetemplateLinearS3.setValueNumberOfVersions(
            coordinates, -1, Node.VALUE_LABEL_D2_DS1DS2, 1)

    nodeIdentifier = nextNodeIdentifier
    nodeId = [[] for n3 in range(nodesCountWall)]
    for n2 in range(elementsCountRadial + 1):
        for n3 in range(nodesCountWall):
            if (n2 == 0) and (startNodeId is not None):
                rowNodeId = copy.deepcopy(startNodeId[n3])
            elif (n2 == elementsCountRadial) and (endNodeId is not None):
                rowNodeId = copy.deepcopy(endNodeId[n3])
            else:
                rowNodeId = []
                nodetemplate1 = nodetemplate if pd3[n3][
                    n2] else nodetemplateLinearS3
                for n1 in range(nodesCountAround):
                    node = nodes.createNode(nodeIdentifier, nodetemplate1)
                    rowNodeId.append(nodeIdentifier)
                    cache.setNode(node)
                    coordinates.setNodeParameters(cache, -1,
                                                  Node.VALUE_LABEL_VALUE, 1,
                                                  px[n3][n2][n1])
                    coordinates.setNodeParameters(cache, -1,
                                                  Node.VALUE_LABEL_D_DS1, 1,
                                                  pd1[n3][n2][n1])
                    coordinates.setNodeParameters(cache, -1,
                                                  Node.VALUE_LABEL_D_DS2, 1,
                                                  pd2[n3][n2][n1])
                    if pd3[n3][n2]:
                        coordinates.setNodeParameters(cache, -1,
                                                      Node.VALUE_LABEL_D_DS3,
                                                      1, pd3[n3][n2][n1])
                    nodeIdentifier = nodeIdentifier + 1
            nodeId[n3].append(rowNodeId)

    #################
    # Create elements
    #################

    tricubichermite = eftfactory_tricubichermite(mesh, useCrossDerivatives)
    bicubichermitelinear = eftfactory_bicubichermitelinear(
        mesh, useCrossDerivatives)

    elementIdentifier = nextElementIdentifier

    elementtemplateStandard = mesh.createElementtemplate()
    elementtemplateStandard.setElementShapeType(Element.SHAPE_TYPE_CUBE)
    elementtemplateX = mesh.createElementtemplate()
    elementtemplateX.setElementShapeType(Element.SHAPE_TYPE_CUBE)
    elementsCountWall = nodesCountWall - 1

    for e2 in range(elementsCountRadial):
        nonlinearXi3 = (not rowLinearXi3[e2]) or (not rowLinearXi3[e2 + 1])
        eftFactory = tricubichermite if nonlinearXi3 else bicubichermitelinear
        eftStandard = eftFactory.createEftBasic()
        elementtemplateStandard.defineField(coordinates, -1, eftStandard)
        mapStartDerivatives = (e2 == 0) and (startDerivativesMap
                                             or rescaleStartDerivatives)
        mapStartLinearDerivativeXi3 = nonlinearXi3 and rowLinearXi3[e2]
        mapEndDerivatives = (e2 == (elementsCountRadial - 1)) and (
            endDerivativesMap or rescaleEndDerivatives)
        mapEndLinearDerivativeXi3 = nonlinearXi3 and rowLinearXi3[e2 + 1]
        mapDerivatives = mapStartDerivatives or mapStartLinearDerivativeXi3 or \
                         mapEndDerivatives or mapEndLinearDerivativeXi3
        for e3 in range(elementsCountWall):
            for e1 in range(elementsCountAround):
                en = (e1 + 1) % elementsCountAround
                nids = [
                    nodeId[e3][e2][e1], nodeId[e3][e2][en],
                    nodeId[e3][e2 + 1][e1], nodeId[e3][e2 + 1][en],
                    nodeId[e3 + 1][e2][e1], nodeId[e3 + 1][e2][en],
                    nodeId[e3 + 1][e2 + 1][e1], nodeId[e3 + 1][e2 + 1][en]
                ]
                scaleFactors = []

                if mapDerivatives:
                    eft1 = eftFactory.createEftNoCrossDerivatives()
                    # work out if scaling by global -1
                    scaleMinus1 = mapStartLinearDerivativeXi3 or mapEndLinearDerivativeXi3
                    if (not scaleMinus1
                        ) and mapStartDerivatives and startDerivativesMap:
                        for n3 in range(2):
                            n3Idx = n3 + e3
                            # need to handle 3 or 4 maps (e1 uses last 3, en uses first 3)
                            for map in startDerivativesMap[n3Idx][e1][-3:]:
                                if map and (-1 in map):
                                    scaleMinus1 = True
                                    break
                            for map in startDerivativesMap[n3Idx][en][:3]:
                                if map and (-1 in map):
                                    scaleMinus1 = True
                                    break
                    if (not scaleMinus1
                        ) and mapEndDerivatives and endDerivativesMap:
                        for n3 in range(2):
                            n3Idx = n3 + e3
                            # need to handle 3 or 4 maps (e1 uses last 3, en uses first 3)
                            for map in endDerivativesMap[n3Idx][e1][-3:]:
                                if map and (-1 in map):
                                    scaleMinus1 = True
                                    break
                            for map in endDerivativesMap[n3Idx][en][:3]:
                                if map and (-1 in map):
                                    scaleMinus1 = True
                                    break
                    # make node scale factors vary fastest by local node varying across lower xi
                    nodeScaleFactorIds = []
                    for n3 in range(2):
                        n3Idx = n3 + e3
                        if mapStartDerivatives and rescaleStartDerivatives:
                            for i in range(2):
                                derivativesMap = (
                                    startDerivativesMap[n3Idx][e1][1] if
                                    (i == 0) else startDerivativesMap[n3Idx]
                                    [en][1]) if startDerivativesMap else None
                                nodeScaleFactorIds.append(
                                    getQuadrantID(derivativesMap
                                                  if derivativesMap else (0, 1,
                                                                          0)))
                        if mapEndDerivatives and rescaleEndDerivatives:
                            for i in range(2):
                                derivativesMap = (
                                    endDerivativesMap[n3Idx][e1][1] if
                                    (i == 0) else endDerivativesMap[n3Idx][en]
                                    [1]) if endDerivativesMap else None
                                nodeScaleFactorIds.append(
                                    getQuadrantID(derivativesMap
                                                  if derivativesMap else (0, 1,
                                                                          0)))
                    setEftScaleFactorIds(eft1, [1] if scaleMinus1 else [],
                                         nodeScaleFactorIds)
                    firstNodeScaleFactorIndex = 2 if scaleMinus1 else 1
                    firstStartNodeScaleFactorIndex = \
                        firstNodeScaleFactorIndex if (mapStartDerivatives and rescaleStartDerivatives) else None
                    firstEndNodeScaleFactorIndex = \
                        (firstNodeScaleFactorIndex + (2 if firstStartNodeScaleFactorIndex else 0)) \
                            if (mapEndDerivatives and rescaleEndDerivatives) else None
                    layerNodeScaleFactorIndexOffset = \
                        4 if (firstStartNodeScaleFactorIndex and firstEndNodeScaleFactorIndex) else 2
                    if scaleMinus1:
                        scaleFactors.append(-1.0)
                    for n3 in range(2):
                        n3Idx = n3 + e3
                        if firstStartNodeScaleFactorIndex:
                            scaleFactors.append(scaleFactorMapStart[n3Idx][e1])
                            scaleFactors.append(scaleFactorMapStart[n3Idx][en])
                        if firstEndNodeScaleFactorIndex:
                            scaleFactors.append(scaleFactorMapEnd[n3Idx][e1])
                            scaleFactors.append(scaleFactorMapEnd[n3Idx][en])

                    if mapStartLinearDerivativeXi3:
                        eftFactory.setEftLinearDerivative2(
                            eft1, [1, 5, 2, 6], Node.VALUE_LABEL_D_DS3,
                            [Node.VALUE_LABEL_D2_DS1DS3])
                    if mapStartDerivatives:
                        for i in range(2):
                            lns = [1, 5] if (i == 0) else [2, 6]
                            for n3 in range(2):
                                n3Idx = n3 + e3
                                derivativesMap = \
                                    (startDerivativesMap[n3Idx][e1] if (i == 0) else startDerivativesMap[n3Idx][en]) \
                                        if startDerivativesMap else (None, None, None)
                                # handle different d1 on each side of node
                                d1Map = \
                                    derivativesMap[0] if ((i == 1) or (len(derivativesMap) < 4)) else derivativesMap[3]
                                d2Map = derivativesMap[1] if derivativesMap[
                                    1] else (0, 1, 0)
                                d3Map = derivativesMap[2]
                                # use temporary to safely swap DS1 and DS2:
                                ln = [lns[n3]]
                                if d1Map:
                                    remapEftNodeValueLabel(
                                        eft1, ln, Node.VALUE_LABEL_D_DS1,
                                        [(Node.VALUE_LABEL_D2_DS1DS2, [])])
                                if d3Map:
                                    remapEftNodeValueLabel(
                                        eft1, ln, Node.VALUE_LABEL_D_DS3,
                                        [(Node.VALUE_LABEL_D2_DS2DS3, [])])
                                if d2Map:
                                    remapEftNodeValueLabel(
                                        eft1, ln, Node.VALUE_LABEL_D_DS2,
                                        derivativeSignsToExpressionTerms(
                                            (Node.VALUE_LABEL_D_DS1,
                                             Node.VALUE_LABEL_D_DS2,
                                             Node.VALUE_LABEL_D_DS3), d2Map,
                                            (firstStartNodeScaleFactorIndex +
                                             i + n3 *
                                             layerNodeScaleFactorIndexOffset)
                                            if rescaleStartDerivatives else
                                            None))
                                if d1Map:
                                    remapEftNodeValueLabel(
                                        eft1, ln, Node.VALUE_LABEL_D2_DS1DS2,
                                        derivativeSignsToExpressionTerms(
                                            (Node.VALUE_LABEL_D_DS1,
                                             Node.VALUE_LABEL_D_DS2,
                                             Node.VALUE_LABEL_D_DS3), d1Map))
                                if d3Map:
                                    remapEftNodeValueLabel(
                                        eft1, ln, Node.VALUE_LABEL_D2_DS2DS3,
                                        derivativeSignsToExpressionTerms(
                                            (Node.VALUE_LABEL_D_DS1,
                                             Node.VALUE_LABEL_D_DS2,
                                             Node.VALUE_LABEL_D_DS3), d3Map))
                    if mapEndLinearDerivativeXi3:
                        eftFactory.setEftLinearDerivative2(
                            eft1, [3, 7, 4, 8], Node.VALUE_LABEL_D_DS3,
                            [Node.VALUE_LABEL_D2_DS1DS3])
                    if mapEndDerivatives:
                        for i in range(2):
                            lns = [3, 7] if (i == 0) else [4, 8]
                            for n3 in range(2):
                                n3Idx = n3 + e3
                                derivativesMap = \
                                    (endDerivativesMap[n3Idx][e1] if (i == 0) else endDerivativesMap[n3Idx][en]) \
                                        if endDerivativesMap else (None, None, None)
                                # handle different d1 on each side of node
                                d1Map = derivativesMap[0] if ((i == 1) or (len(derivativesMap) < 4)) else \
                                    derivativesMap[3]
                                d2Map = derivativesMap[1] if derivativesMap[
                                    1] else (0, 1, 0)
                                d3Map = derivativesMap[2]

                                # use temporary to safely swap DS1 and DS2:
                                ln = [lns[n3]]
                                if d1Map:
                                    remapEftNodeValueLabel(
                                        eft1, ln, Node.VALUE_LABEL_D_DS1,
                                        [(Node.VALUE_LABEL_D2_DS1DS2, [])])
                                if d3Map:
                                    remapEftNodeValueLabel(
                                        eft1, ln, Node.VALUE_LABEL_D_DS3,
                                        [(Node.VALUE_LABEL_D2_DS2DS3, [])])
                                if d2Map:
                                    remapEftNodeValueLabel(
                                        eft1, ln, Node.VALUE_LABEL_D_DS2,
                                        derivativeSignsToExpressionTerms(
                                            (Node.VALUE_LABEL_D_DS1,
                                             Node.VALUE_LABEL_D_DS2,
                                             Node.VALUE_LABEL_D_DS3), d2Map,
                                            (firstEndNodeScaleFactorIndex + i +
                                             n3 *
                                             layerNodeScaleFactorIndexOffset)
                                            if rescaleEndDerivatives else
                                            None))
                                if d1Map:
                                    remapEftNodeValueLabel(
                                        eft1, ln, Node.VALUE_LABEL_D2_DS1DS2,
                                        derivativeSignsToExpressionTerms(
                                            (Node.VALUE_LABEL_D_DS1,
                                             Node.VALUE_LABEL_D_DS2,
                                             Node.VALUE_LABEL_D_DS3), d1Map))
                                if d3Map:
                                    remapEftNodeValueLabel(
                                        eft1, ln, Node.VALUE_LABEL_D2_DS2DS3,
                                        derivativeSignsToExpressionTerms(
                                            (Node.VALUE_LABEL_D_DS1,
                                             Node.VALUE_LABEL_D_DS2,
                                             Node.VALUE_LABEL_D_DS3), d3Map))

                    elementtemplateX.defineField(coordinates, -1, eft1)
                    elementtemplate1 = elementtemplateX
                else:
                    eft1 = eftStandard
                    elementtemplate1 = elementtemplateStandard

                element = mesh.createElement(elementIdentifier,
                                             elementtemplate1)
                result2 = element.setNodesByIdentifier(eft1, nids)
                if scaleFactors:
                    result3 = element.setScaleFactors(eft1, scaleFactors)
                # print('create element annulus', element.isValid(), elementIdentifier, eft1.validate(),
                #       result2, result3 if scaleFactors else None, nids)
                elementIdentifier += 1

                if rowMeshGroups:
                    for meshGroup in rowMeshGroups[e2]:
                        meshGroup.addElement(element)

                if wallAnnotationGroups:
                    for annotationGroup in wallAnnotationGroups[e3]:
                        meshGroup = annotationGroup.getMeshGroup(mesh)
                        meshGroup.addElement(element)

    fm.endChange()

    return nodeIdentifier, elementIdentifier
Exemplo n.º 25
0
def smoothCubicHermiteCrossDerivativesLine(nx,
                                           nd1,
                                           nd2,
                                           nd12,
                                           fixStartDerivative=False,
                                           fixEndDerivative=False,
                                           instrument=False):
    """
    Smooth derivatives of cross directions of hermite curves.
    Assumes initial nd12 derivatives are zero or reasonable.
    Where directions are smoothed the weighted/harmonic mean is used.
    :param nx: List of coordinates of nodes along curves.
    :param nd1: List of derivatives of nodes along curves.
    :param nd2: List of lateral direction vectors of nodes along curves.
    :param nd12: List of derivatives of lateral directions along curves.
    :param fixStartDerivative, fixEndDerivative: Set to True to fix derivative direction and magnitude at respective end.
    :return: Modified nd12
    """
    nodesCount = len(nx)
    elementsCount = nodesCount - 1
    assert elementsCount > 0, 'smoothCubicHermiteCrossDerivativesLine.  Too few nodes/elements'
    assert len(
        nd1
    ) == nodesCount, 'smoothCubicHermiteCrossDerivativesLine.  Mismatched number of derivatives'
    md12 = copy.copy(nd12)
    componentsCount = len(nx[0])
    componentRange = range(componentsCount)
    # special case where equal derivatives at each end are sought
    if (elementsCount == 1) and not (fixStartDerivative or fixEndDerivative):
        delta = [(nd2[1][c] - nd2[0][c]) for c in componentRange]
        return [delta, copy.deepcopy(delta)]
    tol = 1.0E-6
    arcLengths = [
        getCubicHermiteArcLength(nx[e], nd1[e], nx[e + 1], nd1[e + 1])
        for e in range(elementsCount)
    ]
    dtol = tol * sum(vector.magnitude(d) for d in nd2)
    if instrument:
        print('iter 0', md12)
    for iter in range(100):
        lastmd12 = copy.copy(md12)
        # start
        if not fixStartDerivative:
            md12[0] = interpolateLagrangeHermiteDerivative(
                nd2[0], nd2[1], lastmd12[1], 0.0)
        # middle
        for n in range(1, nodesCount - 1):
            nm = n - 1
            # get mean of directions from point n to points (n - 1) and (n + 1)
            np = n + 1
            dirm = [(nd2[n][c] - nd2[nm][c]) for c in componentRange]
            dirp = [(nd2[np][c] - nd2[n][c]) for c in componentRange]
            # mean weighted by fraction towards that end, equivalent to harmonic mean
            arcLengthmp = arcLengths[nm] + arcLengths[n]
            wm = arcLengths[n] / arcLengthmp
            wp = arcLengths[nm] / arcLengthmp
            md12[n] = [(wm * dirm[c] + wp * dirp[c]) for c in componentRange]
        # end
        if not fixEndDerivative:
            md12[-1] = interpolateHermiteLagrangeDerivative(
                nd2[-2], lastmd12[-2], nd2[-1], 1.0)
        if instrument:
            print('iter', iter + 1, md12)
        for n in range(nodesCount):
            for c in componentRange:
                if math.fabs(md12[n][c] - lastmd12[n][c]) > dtol:
                    break
            else:
                continue
            break
        else:
            if instrument:
                print(
                    'smoothCubicHermiteCrossDerivativesLine converged after iter:',
                    iter + 1)
            return md12

    cmax = 0.0
    for n in range(nodesCount):
        for c in componentRange:
            cmax = max(cmax, math.fabs(md12[n][c] - lastmd12[n][c]))
    closeness = cmax / dtol
    print('smoothCubicHermiteCrossDerivativesLine max iters reached:',
          iter + 1, ', cmax = ', round(closeness, 2), 'x tolerance')
    return md12
Exemplo n.º 26
0
def smoothCubicHermiteDerivativesLine(
        nx,
        nd1,
        fixAllDirections=False,
        fixStartDerivative=False,
        fixEndDerivative=False,
        fixStartDirection=False,
        fixEndDirection=False,
        magnitudeScalingMode=DerivativeScalingMode.ARITHMETIC_MEAN,
        instrument=False):
    """
    Modifies derivatives nd1 to be smoothly varying and near arc length.
    Values are treated as being in a line.
    Assumes initial derivatives are zero or reasonable.
    Where directions are smoothed the weighted/harmonic mean is used.
    :param nx: List of coordinates of nodes along curves.
    :param nd1: List of derivatives of nodes along curves.
    :param fixAllDirections: Set to True to only smooth magnitudes, otherwise both direction and magnitude are adjusted.
    :param fixStartDerivative, fixEndDerivative: Set to True to fix derivative direction and magnitude at respective end.
    :param fixStartDirection, fixEndDirection: Set to True to fix direction at respective end.
    Redundant if fixAllDirections or respective fixStart/EndDerivative is True.
    :param magnitudeScalingMode: A value from enum DerivativeScalingMode specifying
    expression used to get derivative magnitude from adjacent arc lengths.
    :return: Modified nd1
    """
    nodesCount = len(nx)
    elementsCount = nodesCount - 1
    assert elementsCount > 0, 'smoothCubicHermiteDerivativesLine.  Too few nodes/elements'
    assert len(
        nd1
    ) == nodesCount, 'smoothCubicHermiteDerivativesLine.  Mismatched number of derivatives'
    arithmeticMeanMagnitude = magnitudeScalingMode is DerivativeScalingMode.ARITHMETIC_MEAN
    assert arithmeticMeanMagnitude or (magnitudeScalingMode is DerivativeScalingMode.HARMONIC_MEAN), \
        'smoothCubicHermiteDerivativesLine. Invalid magnitude scaling mode'
    md1 = copy.copy(nd1)
    componentsCount = len(nx[0])
    componentRange = range(componentsCount)
    if elementsCount == 1:
        # special cases for one element
        if not (fixStartDerivative or fixEndDerivative or fixStartDirection
                or fixEndDirection or fixAllDirections):
            # straight line
            delta = [(nx[1][c] - nx[0][c]) for c in componentRange]
            return [delta, copy.deepcopy(delta)]
        if fixAllDirections or (fixStartDirection and fixEndDirection):
            # fixed directions, equal magnitude
            arcLength = computeCubicHermiteArcLength(nx[0],
                                                     nd1[0],
                                                     nx[1],
                                                     nd1[1],
                                                     rescaleDerivatives=True)
            return [
                vector.setMagnitude(nd1[0], arcLength),
                vector.setMagnitude(nd1[1], arcLength)
            ]
    tol = 1.0E-6
    if instrument:
        print('iter 0', md1)
    for iter in range(100):
        lastmd1 = copy.copy(md1)
        arcLengths = [
            getCubicHermiteArcLength(nx[e], md1[e], nx[e + 1], md1[e + 1])
            for e in range(elementsCount)
        ]
        # start
        if not fixStartDerivative:
            if fixAllDirections or fixStartDirection:
                mag = 2.0 * arcLengths[0] - vector.magnitude(lastmd1[1])
                md1[0] = vector.setMagnitude(
                    nd1[0], mag) if (mag > 0.0) else [0.0, 0.0, 0.0]
            else:
                md1[0] = interpolateLagrangeHermiteDerivative(
                    nx[0], nx[1], lastmd1[1], 0.0)
        # middle
        for n in range(1, nodesCount - 1):
            nm = n - 1
            if not fixAllDirections:
                # get mean of directions from point n to points (n - 1) and (n + 1)
                np = n + 1
                dirm = [(nx[n][c] - nx[nm][c]) for c in componentRange]
                dirp = [(nx[np][c] - nx[n][c]) for c in componentRange]
                # mean weighted by fraction towards that end, equivalent to harmonic mean
                arcLengthmp = arcLengths[nm] + arcLengths[n]
                wm = arcLengths[n] / arcLengthmp
                wp = arcLengths[nm] / arcLengthmp
                md1[n] = [(wm * dirm[c] + wp * dirp[c])
                          for c in componentRange]
            if arithmeticMeanMagnitude:
                mag = 0.5 * (arcLengths[nm] + arcLengths[n])
            else:  # harmonicMeanMagnitude
                mag = 2.0 / (1.0 / arcLengths[nm] + 1.0 / arcLengths[n])
            md1[n] = vector.setMagnitude(md1[n], mag)
        # end
        if not fixEndDerivative:
            if fixAllDirections or fixEndDirection:
                mag = 2.0 * arcLengths[-1] - vector.magnitude(lastmd1[-2])
                md1[-1] = vector.setMagnitude(
                    nd1[-1], mag) if (mag > 0.0) else [0.0, 0.0, 0.0]
            else:
                md1[-1] = interpolateHermiteLagrangeDerivative(
                    nx[-2], lastmd1[-2], nx[-1], 1.0)
        if instrument:
            print('iter', iter + 1, md1)
        dtol = tol * sum(arcLengths) / len(arcLengths)
        for n in range(nodesCount):
            for c in componentRange:
                if math.fabs(md1[n][c] - lastmd1[n][c]) > dtol:
                    break
            else:
                continue
            break
        else:
            if instrument:
                print(
                    'smoothCubicHermiteDerivativesLine converged after iter:',
                    iter + 1)
            return md1

    cmax = 0.0
    for n in range(nodesCount):
        for c in componentRange:
            cmax = max(cmax, math.fabs(md1[n][c] - lastmd1[n][c]))
    closeness = cmax / dtol
    print('smoothCubicHermiteDerivativesLine max iters reached:', iter + 1,
          ', cmax = ', round(closeness, 2), 'x tolerance')
    return md1
Exemplo n.º 27
0
def sampleCubicHermiteCurvesSmooth(nx,
                                   nd1,
                                   elementsCountOut,
                                   derivativeMagnitudeStart=None,
                                   derivativeMagnitudeEnd=None):
    """
    Get smoothly spaced points and derivatives over cubic Hermite interpolated
    curves with nodes nx and derivatives nd1. The first element uses the first two nodes.
    Gives smooth variation of element size to fit the supplied start and end derivative
    magnitudes.
    :param nx: Coordinates of nodes along curves.
    :param nd1: Derivatives of nodes along curves.
    :param derivativeMagnitudeStart, derivativeMagnitudeEnd: Optional magnitudes of start and end
    derivatives appropriate for elementsCountOut. If unspecified these are calculated from the other
    end or set to be equal for even spaced elements.
    :return: px[], pd1[], pe[], pxi[], psf[], where pe[] and pxi[] are lists of element indices and
    and xi locations in the 'in' elements to pass to partner interpolateSample functions. psf[] is
    a list of scale factors for converting derivatives from old to new xi coordinates: dxi(old)/dxi(new).
    """
    elementsCountIn = len(nx) - 1
    assert (elementsCountIn > 0) and (len(nd1) == (elementsCountIn + 1)) and (elementsCountOut > 0), \
        'sampleCubicHermiteCurvesSmooth.  Invalid arguments'
    lengths = [0.0]
    length = 0.0
    for e in range(elementsCountIn):
        length += getCubicHermiteArcLength(nx[e], nd1[e], nx[e + 1],
                                           nd1[e + 1])
        lengths.append(length)
    if derivativeMagnitudeStart and derivativeMagnitudeEnd:
        pass
    elif derivativeMagnitudeEnd:
        derivativeMagnitudeStart = (2.0 * length - elementsCountOut *
                                    derivativeMagnitudeEnd) / elementsCountOut
    elif derivativeMagnitudeStart:
        derivativeMagnitudeEnd = (2.0 * length - elementsCountOut *
                                  derivativeMagnitudeStart) / elementsCountOut
    else:
        derivativeMagnitudeStart = derivativeMagnitudeEnd = length / elementsCountOut
    # sample over length to get distances to elements boundaries
    x1 = 0.0
    d1 = derivativeMagnitudeStart * elementsCountOut
    x2 = length
    d2 = derivativeMagnitudeEnd * elementsCountOut
    nodeDistances = []
    nodeDerivativeMagnitudes = []
    for n in range(elementsCountOut + 1):
        xi = n / elementsCountOut
        f1, f2, f3, f4 = getCubicHermiteBasis(xi)
        distance = f1 * x1 + f2 * d1 + f3 * x2 + f4 * d2
        nodeDistances.append(distance)
        f1, f2, f3, f4 = getCubicHermiteBasisDerivatives(xi)
        derivative = f1 * x1 + f2 * d1 + f3 * x2 + f4 * d2
        nodeDerivativeMagnitudes.append(derivative / elementsCountOut)
    #print('nodeDerivativeMagnitudesIn ', [ vector.magnitude(d1) for d1 in nd1 ])
    #print('nodeDerivativeMagnitudesOut', nodeDerivativeMagnitudes)
    px = []
    pd1 = []
    pe = []
    pxi = []
    psf = []
    e = 0
    for eOut in range(elementsCountOut):
        distance = nodeDistances[eOut]
        while e < elementsCountIn:
            if distance < lengths[e + 1]:
                partDistance = distance - lengths[e]
                x, d1, _, xi = getCubicHermiteCurvesPointAtArcDistance(
                    nx[e:e + 2], nd1[e:e + 2], partDistance)
                sf = nodeDerivativeMagnitudes[eOut] / vector.magnitude(d1)
                px.append(x)
                pd1.append([sf * d for d in d1])
                pe.append(e)
                pxi.append(xi)
                psf.append(sf)
                break
            e += 1
    e = elementsCountIn
    eOut = elementsCountOut
    xi = 1.0
    d1 = nd1[e]
    sf = nodeDerivativeMagnitudes[eOut] / vector.magnitude(d1)
    px.append(nx[e])
    pd1.append([sf * d for d in d1])
    pe.append(e - 1)
    pxi.append(xi)
    psf.append(sf)
    return px, pd1, pe, pxi, psf
Exemplo n.º 28
0
def sampleCubicHermiteCurves(nx,
                             nd1,
                             elementsCountOut,
                             addLengthStart=0.0,
                             addLengthEnd=0.0,
                             lengthFractionStart=1.0,
                             lengthFractionEnd=1.0,
                             elementLengthStartEndRatio=1.0,
                             arcLengthDerivatives=False):
    """
    Get systematically spaced points and derivatives over cubic Hermite interpolated
    curves with nodes nx and derivatives nd1. The first element uses the first two nodes.
    :param nx: Coordinates of nodes along curves.
    :param nd1: Derivatives of nodes along curves.
    :param addLengthStart, addLengthEnd: Extra length to add to start and end elements.
    :param lengthFractionStart, lengthFractionEnd: Fraction of mid element length for
        start and end elements. Can use in addition to AddLengths: If LengthFraction
        is 0.5 and AddLength is derivative/2.0 can blend into known derivative at start
        or end.
    :param elementLengthStartEndRatio: Start/end element length ratio, with lengths
        smoothly varying in between. Requires at least 2 elements. Applied in proportion
        to lengthFractionStart, lengthFractionEnd.
    :param arcLengthDerivatives: If True each cubic section is rescaled to arc length.
    If False (default), derivatives and distances are used as supplied.
    :return: px[], pd1[], pe[], pxi[], psf[], where pe[] and pxi[] are lists of element indices and
    and xi locations in the 'in' elements to pass to partner interpolateSample functions. psf[] is
    a list of scale factors for converting derivatives from old to new xi coordinates: dxi(old)/dxi(new).
    """
    elementsCountIn = len(nx) - 1
    assert (elementsCountIn > 0) and (len(nd1) == (elementsCountIn + 1)) and \
        (elementsCountOut > 0), 'sampleCubicHermiteCurves.  Invalid arguments'
    lengths = [0.0]
    nd1a = []
    nd1b = []
    length = 0.0
    for e in range(elementsCountIn):
        if arcLengthDerivatives:
            arcLength = computeCubicHermiteArcLength(nx[e],
                                                     nd1[e],
                                                     nx[e + 1],
                                                     nd1[e + 1],
                                                     rescaleDerivatives=True)
            nd1a.append(vector.setMagnitude(nd1[e], arcLength))
            nd1b.append(vector.setMagnitude(nd1[e + 1], arcLength))
        else:
            arcLength = getCubicHermiteArcLength(nx[e], nd1[e], nx[e + 1],
                                                 nd1[e + 1])
        length += arcLength
        lengths.append(length)
    proportionEnd = 2.0 / (elementLengthStartEndRatio + 1)
    proportionStart = elementLengthStartEndRatio * proportionEnd
    if elementsCountOut == 1:
        elementLengthMid = length
    else:
        elementLengthMid = (length - addLengthStart - addLengthEnd) / \
            (elementsCountOut - 2.0 + proportionStart*lengthFractionStart + proportionEnd*lengthFractionEnd)
    elementLengthProportionStart = proportionStart * lengthFractionStart * elementLengthMid
    elementLengthProportionEnd = proportionEnd * lengthFractionEnd * elementLengthMid
    # get smoothly varying element lengths, not accounting for start and end
    if (elementsCountOut == 1) or (elementLengthStartEndRatio == 1.0):
        elementLengths = [elementLengthMid] * elementsCountOut
    else:
        elementLengths = []
        for eOut in range(elementsCountOut):
            xi = eOut / (elementsCountOut - 1)
            elementLengths.append(
                ((1.0 - xi) * proportionStart + xi * proportionEnd) *
                elementLengthMid)
    # get middle derivative magnitudes
    nodeDerivativeMagnitudes = [None] * (elementsCountOut + 1
                                         )  # start and end determined below
    for n in range(1, elementsCountOut):
        nodeDerivativeMagnitudes[n] = 0.5 * (elementLengths[n - 1] +
                                             elementLengths[n])
    # fix end lengths:
    elementLengths[0] = addLengthStart + elementLengthProportionStart
    elementLengths[-1] = addLengthEnd + elementLengthProportionEnd
    #print('\nsampleCubicHermiteCurves:')
    #print('  elementLengths', elementLengths, 'addLengthStart', addLengthStart, 'addLengthEnd', addLengthEnd)
    #print('  sum lengths', sum(elementLengths), 'vs. length', length, 'diff', sum(elementLengths) - length)
    # set end derivatives:
    if elementsCountOut == 1:
        nodeDerivativeMagnitudes[0] = nodeDerivativeMagnitudes[
            1] = elementLengths[0]
    else:
        nodeDerivativeMagnitudes[
            0] = elementLengths[0] * 2.0 - nodeDerivativeMagnitudes[1]
        nodeDerivativeMagnitudes[
            -1] = elementLengths[-1] * 2.0 - nodeDerivativeMagnitudes[-2]

    px = []
    pd1 = []
    pe = []
    pxi = []
    psf = []
    distance = 0.0
    e = 0
    for eOut in range(elementsCountOut):
        while e < elementsCountIn:
            if distance < lengths[e + 1]:
                partDistance = distance - lengths[e]
                if arcLengthDerivatives:
                    xi = partDistance / (lengths[e + 1] - lengths[e])
                    x = interpolateCubicHermite(nx[e], nd1a[e], nx[e + 1],
                                                nd1b[e], xi)
                    d1 = interpolateCubicHermiteDerivative(
                        nx[e], nd1a[e], nx[e + 1], nd1b[e], xi)
                else:
                    x, d1, _eIn, xi = getCubicHermiteCurvesPointAtArcDistance(
                        nx[e:e + 2], nd1[e:e + 2], partDistance)
                sf = nodeDerivativeMagnitudes[eOut] / vector.magnitude(d1)
                px.append(x)
                pd1.append([sf * d for d in d1])
                pe.append(e)
                pxi.append(xi)
                psf.append(sf)
                break
            e += 1
        distance += elementLengths[eOut]
    e = elementsCountIn
    eOut = elementsCountOut
    xi = 1.0
    d1 = nd1[e]
    sf = nodeDerivativeMagnitudes[eOut] / vector.magnitude(d1)
    px.append(nx[e])
    pd1.append([sf * d for d in d1])
    pe.append(e - 1)
    pxi.append(xi)
    psf.append(sf)
    return px, pd1, pe, pxi, psf
    def generateBaseMesh(cls, region, options):
        """
        Generate the base tricubic Hermite mesh. See also generateMesh().
        :param region: Zinc region to define model in. Must be empty.
        :param options: Dict containing options. See getDefaultOptions().
        :return: annotationGroups
        """
        centralPath = options['Central path']
        elementsCountAround = options['Number of elements around']
        elementsCountAlong = options['Number of elements along']
        elementsCountThroughWall = options['Number of elements through wall']
        wallThickness = options['Wall thickness']
        mucosaRelThickness = options['Mucosa relative thickness']
        submucosaRelThickness = options['Submucosa relative thickness']
        circularRelThickness = options[
            'Circular muscle layer relative thickness']
        longitudinalRelThickness = options[
            'Longitudinal muscle layer relative thickness']
        useCrossDerivatives = options['Use cross derivatives']
        useCubicHermiteThroughWall = not (options['Use linear through wall'])

        firstNodeIdentifier = 1
        firstElementIdentifier = 1

        # Central path
        esophagusTermsAlong = [
            None, 'cervical part of esophagus', 'thoracic part of esophagus',
            'abdominal part of esophagus'
        ]
        arcLengthOfGroupsAlong = []
        for i in range(len(esophagusTermsAlong)):
            tmpRegion = region.createRegion()
            centralPath.generate(tmpRegion)
            cxGroup, cd1Group, cd2Group, cd3Group, cd12Group, cd13Group = \
                extractPathParametersFromRegion(tmpRegion, [Node.VALUE_LABEL_VALUE, Node.VALUE_LABEL_D_DS1,
                                                            Node.VALUE_LABEL_D_DS2, Node.VALUE_LABEL_D_DS3,
                                                            Node.VALUE_LABEL_D2_DS1DS2, Node.VALUE_LABEL_D2_DS1DS3],
                                                groupName=esophagusTermsAlong[i])
            arcLength = 0.0
            for e in range(len(cxGroup) - 1):
                arcLength += interp.getCubicHermiteArcLength(
                    cxGroup[e], cd1Group[e], cxGroup[e + 1], cd1Group[e + 1])
            arcLengthOfGroupsAlong.append(arcLength)

            if i == 0:
                cx = cxGroup
                cd1 = cd1Group
                cd2 = cd2Group
                cd3 = cd3Group
                cd12 = cd12Group
                cd13 = cd13Group

            del tmpRegion

        # Sample central path
        sx, sd1, se, sxi, ssf = interp.sampleCubicHermiteCurves(
            cx, cd1, elementsCountAlong)
        sd2, sd12 = interp.interpolateSampleCubicHermite(
            cd2, cd12, se, sxi, ssf)
        sd3, sd13 = interp.interpolateSampleCubicHermite(
            cd3, cd13, se, sxi, ssf)

        centralPathLength = arcLengthOfGroupsAlong[0]
        elementAlongLength = centralPathLength / elementsCountAlong

        elementsCountAlongGroups = []
        groupLength = 0.0
        e = 0
        elementsCount = 1
        length = elementAlongLength
        for i in range(1, len(esophagusTermsAlong)):
            groupLength += arcLengthOfGroupsAlong[i]
            if e == elementsCountAlong - 2:
                elementsCount += 1
                elementsCountAlongGroups.append(elementsCount)
            else:
                while length < groupLength:
                    elementsCount += 1
                    e += 1
                    length += elementAlongLength

                # check which end is grouplength closer to
                distToUpperEnd = abs(length - groupLength)
                distToLowerEnd = abs(groupLength -
                                     (length - elementsCountAlong))
                if distToLowerEnd < distToUpperEnd:
                    elementsCount -= 1
                    elementsCountAlongGroups.append(elementsCount)
                    e -= 1
                    length -= elementAlongLength
                else:
                    elementsCountAlongGroups.append(elementsCount)
            elementsCount = 0

        majorRadiusElementList = sd2
        minorRadiusElementList = sd3

        # Create annotation groups along esophagus
        esophagusGroup = AnnotationGroup(region,
                                         get_esophagus_term("esophagus"))
        cervicalGroup = AnnotationGroup(
            region, get_esophagus_term("cervical part of esophagus"))
        thoracicGroup = AnnotationGroup(
            region, get_esophagus_term("thoracic part of esophagus"))
        abdominalGroup = AnnotationGroup(
            region, get_esophagus_term("abdominal part of esophagus"))

        annotationGroupAlong = [[esophagusGroup, cervicalGroup],
                                [esophagusGroup, thoracicGroup],
                                [esophagusGroup, abdominalGroup]]

        annotationGroupsAlong = []
        for i in range(len(elementsCountAlongGroups)):
            elementsCount = elementsCountAlongGroups[i]
            for n in range(elementsCount):
                annotationGroupsAlong.append(annotationGroupAlong[i])

        annotationGroupsAround = []
        for i in range(elementsCountAround):
            annotationGroupsAround.append([])

        # Groups through wall
        longitudinalMuscleGroup = AnnotationGroup(
            region,
            get_esophagus_term("esophagus smooth muscle longitudinal layer"))
        circularMuscleGroup = AnnotationGroup(
            region,
            get_esophagus_term("esophagus smooth muscle circular layer"))
        submucosaGroup = AnnotationGroup(
            region, get_esophagus_term("submucosa of esophagus"))
        mucosaGroup = AnnotationGroup(region,
                                      get_esophagus_term("esophagus mucosa"))

        if elementsCountThroughWall == 1:
            relativeThicknessList = [1.0]
            annotationGroupsThroughWall = [[]]
        else:
            relativeThicknessList = [
                mucosaRelThickness, submucosaRelThickness,
                circularRelThickness, longitudinalRelThickness
            ]
            annotationGroupsThroughWall = [[mucosaGroup], [submucosaGroup],
                                           [circularMuscleGroup],
                                           [longitudinalMuscleGroup]]

        xToSample = []
        d1ToSample = []
        for n2 in range(elementsCountAlong + 1):
            # Create inner points
            cx = [0.0, 0.0, elementAlongLength * n2]
            axis1 = [vector.magnitude(majorRadiusElementList[n2]), 0.0, 0.0]
            axis2 = [0.0, vector.magnitude(minorRadiusElementList[n2]), 0.0]
            xInner, d1Inner = geometry.createEllipsePoints(cx,
                                                           2 * math.pi,
                                                           axis1,
                                                           axis2,
                                                           elementsCountAround,
                                                           startRadians=0.0)
            xToSample += xInner
            d1ToSample += d1Inner

        d2ToSample = [[0.0, 0.0, elementAlongLength]
                      ] * (elementsCountAround * (elementsCountAlong + 1))

        # Sample along length
        xInnerRaw = []
        d2InnerRaw = []
        xToWarp = []
        d1ToWarp = []
        d2ToWarp = []
        flatWidthList = []
        xiList = []

        for n1 in range(elementsCountAround):
            xForSamplingAlong = []
            d2ForSamplingAlong = []
            for n2 in range(elementsCountAlong + 1):
                idx = n2 * elementsCountAround + n1
                xForSamplingAlong.append(xToSample[idx])
                d2ForSamplingAlong.append(d2ToSample[idx])
            xSampled, d2Sampled = interp.sampleCubicHermiteCurves(
                xForSamplingAlong,
                d2ForSamplingAlong,
                elementsCountAlong,
                arcLengthDerivatives=True)[0:2]
            xInnerRaw.append(xSampled)
            d2InnerRaw.append(d2Sampled)

        # Re-arrange sample order & calculate dx_ds1 and dx_ds3 from dx_ds2
        for n2 in range(elementsCountAlong + 1):
            xAround = []
            d2Around = []

            for n1 in range(elementsCountAround):
                x = xInnerRaw[n1][n2]
                d2 = d2InnerRaw[n1][n2]
                xAround.append(x)
                d2Around.append(d2)

            d1Around = []
            for n1 in range(elementsCountAround):
                v1 = xAround[n1]
                v2 = xAround[(n1 + 1) % elementsCountAround]
                d1 = d2 = [v2[c] - v1[c] for c in range(3)]
                arcLengthAround = interp.computeCubicHermiteArcLength(
                    v1, d1, v2, d2, True)
                dx_ds1 = [c * arcLengthAround for c in vector.normalise(d1)]
                d1Around.append(dx_ds1)
            d1Smoothed = interp.smoothCubicHermiteDerivativesLoop(
                xAround, d1Around)

            xToWarp += xAround
            d1ToWarp += d1Smoothed
            d2ToWarp += d2Around

            # Flat width and xi
            flatWidth = 0.0
            xiFace = []
            for n1 in range(elementsCountAround):
                v1 = xAround[n1]
                d1 = d1Smoothed[n1]
                v2 = xAround[(n1 + 1) % elementsCountAround]
                d2 = d1Smoothed[(n1 + 1) % elementsCountAround]
                flatWidth += interp.getCubicHermiteArcLength(v1, d1, v2, d2)
            flatWidthList.append(flatWidth)

            for n1 in range(elementsCountAround + 1):
                xi = 1.0 / elementsCountAround * n1
                xiFace.append(xi)
            xiList.append(xiFace)

        # Project reference point for warping onto central path
        sxRefList, sd1RefList, sd2ProjectedListRef, zRefList = \
            tubemesh.getPlaneProjectionOnCentralPath(xToWarp, elementsCountAround, elementsCountAlong,
                                                     centralPathLength, sx, sd1, sd2, sd12)

        # Warp points
        segmentAxis = [0.0, 0.0, 1.0]
        closedProximalEnd = False

        innerRadiusAlong = []
        for n2 in range(elementsCountAlong + 1):
            firstNodeAlong = xToWarp[n2 * elementsCountAround]
            midptSegmentAxis = [0.0, 0.0, elementAlongLength * n2]
            radius = vector.magnitude(firstNodeAlong[c] - midptSegmentAxis[c]
                                      for c in range(3))
            innerRadiusAlong.append(radius)

        xWarpedList, d1WarpedList, d2WarpedList, d3WarpedUnitList = \
            tubemesh.warpSegmentPoints(xToWarp, d1ToWarp, d2ToWarp, segmentAxis, sxRefList, sd1RefList,
                                       sd2ProjectedListRef, elementsCountAround, elementsCountAlong,
                                       zRefList, innerRadiusAlong, closedProximalEnd)

        # Create coordinates and derivatives
        transitElementList = [0] * elementsCountAround
        xList, d1List, d2List, d3List, curvatureList = \
            tubemesh.getCoordinatesFromInner(xWarpedList, d1WarpedList, d2WarpedList, d3WarpedUnitList,
                                             [wallThickness]*(elementsCountAlong+1), relativeThicknessList,
                                             elementsCountAround, elementsCountAlong, elementsCountThroughWall,
                                             transitElementList)

        # Create flat coordinates
        xFlat, d1Flat, d2Flat = tubemesh.createFlatCoordinates(
            xiList, flatWidthList, length, wallThickness,
            relativeThicknessList, elementsCountAround, elementsCountAlong,
            elementsCountThroughWall, transitElementList)

        # Create nodes and elements
        xOrgan = []
        d1Organ = []
        d2Organ = []
        nodeIdentifier, elementIdentifier, annotationGroups = \
            tubemesh.createNodesAndElements(region, xList, d1List, d2List, d3List, xFlat, d1Flat, d2Flat,
                                            xOrgan, d1Organ, d2Organ, None, elementsCountAround, elementsCountAlong,
                                            elementsCountThroughWall, annotationGroupsAround, annotationGroupsAlong,
                                            annotationGroupsThroughWall, firstNodeIdentifier, firstElementIdentifier,
                                            useCubicHermiteThroughWall, useCrossDerivatives, closedProximalEnd)

        # annotation fiducial points
        fm = region.getFieldmodule()
        fm.beginChange()
        mesh = fm.findMeshByDimension(3)
        cache = fm.createFieldcache()

        markerGroup = findOrCreateFieldGroup(fm, "marker")
        markerName = findOrCreateFieldStoredString(fm, name="marker_name")
        markerLocation = findOrCreateFieldStoredMeshLocation(
            fm, mesh, name="marker_location")

        nodes = fm.findNodesetByFieldDomainType(Field.DOMAIN_TYPE_NODES)
        markerPoints = findOrCreateFieldNodeGroup(markerGroup,
                                                  nodes).getNodesetGroup()
        markerTemplateInternal = nodes.createNodetemplate()
        markerTemplateInternal.defineField(markerName)
        markerTemplateInternal.defineField(markerLocation)

        markerNames = [
            "proximodorsal midpoint on serosa of upper esophageal sphincter",
            "proximoventral midpoint on serosa of upper esophageal sphincter",
            "distal point of lower esophageal sphincter serosa on the greater curvature of stomach",
            "distal point of lower esophageal sphincter serosa on the lesser curvature of stomach"
        ]

        totalElements = elementIdentifier
        radPerElementAround = math.pi * 2.0 / elementsCountAround
        elementAroundHalfPi = int(0.25 * elementsCountAround)
        xi1HalfPi = (math.pi * 0.5 - radPerElementAround *
                     elementAroundHalfPi) / radPerElementAround
        elementAroundPi = int(0.5 * elementsCountAround)
        xi1Pi = (math.pi -
                 radPerElementAround * elementAroundPi) / radPerElementAround

        markerElementIdentifiers = [
            elementsCountAround * elementsCountThroughWall -
            elementAroundHalfPi, elementAroundHalfPi + 1 +
            elementsCountAround * (elementsCountThroughWall - 1),
            totalElements - elementsCountAround,
            totalElements - elementsCountAround + elementAroundPi
        ]

        markerXis = [[1.0 - xi1HalfPi, 0.0, 1.0], [xi1HalfPi, 0.0, 1.0],
                     [0.0, 1.0, 1.0], [xi1Pi, 1.0, 1.0]]

        for n in range(len(markerNames)):
            markerGroup = findOrCreateAnnotationGroupForTerm(
                annotationGroups, region, get_esophagus_term(markerNames[n]))
            markerElement = mesh.findElementByIdentifier(
                markerElementIdentifiers[n])
            markerXi = markerXis[n]
            cache.setMeshLocation(markerElement, markerXi)
            markerPoint = markerPoints.createNode(nodeIdentifier,
                                                  markerTemplateInternal)
            nodeIdentifier += 1
            cache.setNode(markerPoint)
            markerName.assignString(cache, markerGroup.getName())
            markerLocation.assignMeshLocation(cache, markerElement, markerXi)
            for group in [esophagusGroup, markerGroup]:
                group.getNodesetGroup(nodes).addNode(markerPoint)

        fm.endChange()

        return annotationGroups
Exemplo n.º 30
0
    def createCylinderMesh3d(self, fieldModule, coordinates):
        """
        Create an extruded shape (ellipse/circle) mesh. Currently limited to ellipse or circle base with the alongAxis
        perpendicular to the base.
        :param fieldModule: Zinc fieldModule to create elements in.
        :param coordinates: Coordinate field to define.
        :return: Final values of nextNodeIdentifier, nextElementIdentifier.
        """
        assert (self._elementsCountAlong >
                0), 'createCylinderMesh3d:  Invalid number of along elements'
        assert (self._elementsCountAcrossMinor >
                3), 'createCylinderMesh3d: Invalid number of across elements'
        assert (self._elementsCountAcrossMinor % 2 == 0), 'createCylinderMesh3d: number of across elements' \
                                                          ' is not an even number'
        assert (self._elementsCountAcrossMajor >
                1), 'createCylinderMesh3d: Invalid number of up elements'
        assert (self._cylinderShape in [self._cylinderShape.CYLINDER_SHAPE_FULL,
                                        self._cylinderShape.CYLINDER_SHAPE_LOWER_HALF]), \
            'createCylinderMesh3d: Invalid cylinder mode.'

        nodes = fieldModule.findNodesetByFieldDomainType(
            Field.DOMAIN_TYPE_NODES)
        mesh = fieldModule.findMeshByDimension(3)

        elementsCountRim = self._elementsCountAcrossRim

        shieldMode = ShieldShape2D.SHIELD_SHAPE_FULL if self._cylinderShape is self._cylinderShape.CYLINDER_SHAPE_FULL \
            else ShieldShape2D.SHIELD_SHAPE_LOWER_HALF
        ellipseShape = EllipseShape.Ellipse_SHAPE_FULL \
            if self._cylinderShape is self._cylinderShape.CYLINDER_SHAPE_FULL else EllipseShape.Ellipse_SHAPE_LOWER_HALF
        self._shield = ShieldMesh2D(self._elementsCountAcrossMinor,
                                    self._elementsCountAcrossMajor,
                                    elementsCountRim,
                                    None,
                                    self._elementsCountAlong,
                                    shieldMode,
                                    shieldType=ShieldRimDerivativeMode.
                                    SHIELD_RIM_DERIVATIVE_MODE_AROUND)

        # generate ellipses mesh along cylinder axis
        n3Count = 0 if self._cylinderType == CylinderType.CYLINDER_STRAIGHT else self._elementsCountAlong
        self._ellipses = []
        for n3 in range(n3Count + 1):
            ellipse = Ellipse2D(self._centres[n3],
                                self._majorAxis[n3],
                                self._minorAxis[n3],
                                self._elementsCountAcrossMajor,
                                self._elementsCountAcrossMinor,
                                self._elementsCountAcrossShell,
                                self._elementsCountAcrossTransition,
                                self._shellProportion,
                                self._coreMajorRadii[n3],
                                self._coreMinorRadii[n3],
                                ellipseShape=ellipseShape)
            self._ellipses.append(ellipse)
            self.copyEllipsesNodesToShieldNodes(n3)

        for n3 in range(n3Count + 1):
            self.calculateD2Derivatives(n3, n3Count)

        if self._cylinderType == CylinderType.CYLINDER_TAPERED:
            self.smoothd2Derivatives()

        # The other ellipses for a straight cylinder.
        if self._cylinderType == CylinderType.CYLINDER_STRAIGHT:
            arcLengthAlong = vector.magnitude(
                self._base._alongAxis) / self._elementsCountAlong
            for n2 in range(self._elementsCountUp + 1):
                for n3 in range(self._elementsCountAlong + 1):
                    for n1 in range(self._elementsCountAcrossMinor + 1):
                        if self._shield.px[0][n2][n1]:
                            temx = [
                                self._shield.px[0][n2][n1][c] +
                                n3 * arcLengthAlong *
                                vector.normalise(self._base._alongAxis)[c]
                                for c in range(3)
                            ]
                            self._shield.px[n3][n2][n1] = temx
                            self._shield.pd1[n3][n2][n1] = self._shield.pd1[0][
                                n2][n1]
                            self._shield.pd2[n3][n2][n1] = self._shield.pd2[0][
                                n2][n1]
                            self._shield.pd3[n3][n2][n1] = self._shield.pd3[0][
                                n2][n1]

        self.generateNodes(nodes, fieldModule, coordinates)
        self.generateElements(mesh, fieldModule, coordinates)

        if self._end is None:
            self._end = CylinderEnds(
                self._elementsCountAcrossMajor, self._elementsCountAcrossMinor,
                self._elementsCountAcrossShell,
                self._elementsCountAcrossTransition, self._shellProportion,
                self._centres[-1], self._shield.pd2[-1][0][1],
                vector.setMagnitude(self._base._majorAxis,
                                    self._majorRadii[-1]),
                self._minorRadii[-1])
        self.setEndsNodes()