Ejemplo n.º 1
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
Ejemplo n.º 2
0
    def generateBaseMesh(cls, region, options):
        """
        Generate the base tricubic Hermite or bicubic Hermite linear mesh.
        :param region: Zinc region to define model in. Must be empty.
        :param options: Dict containing options. See getDefaultOptions().
        :return: [] empty list of AnnotationGroup
        """
        elementsCountAround = options['Number of elements around']
        elementsCountUp = options['Number of elements up']
        elementsCountThroughWall = options['Number of elements through wall']
        useCrossDerivatives = options['Use cross derivatives']
        useCubicHermiteThroughWall = not (options['Use linear through wall'])
        excludeBottomRows = options['Exclude bottom rows']
        excludeTopRows = options['Exclude top rows']
        wallThickness = options['Wall thickness']
        wallThicknessRatioApex = options['Wall thickness ratio apex']
        lengthRatio = options['Length ratio']
        elementLengthRatioEquatorApex = options[
            'Element length ratio equator/apex']

        fm = region.getFieldmodule()
        fm.beginChange()
        coordinates = findOrCreateFieldCoordinates(fm)

        nodes = fm.findNodesetByFieldDomainType(Field.DOMAIN_TYPE_NODES)
        nodetemplateApex = nodes.createNodetemplate()
        nodetemplateApex.defineField(coordinates)
        nodetemplateApex.setValueNumberOfVersions(coordinates, -1,
                                                  Node.VALUE_LABEL_VALUE, 1)
        nodetemplateApex.setValueNumberOfVersions(coordinates, -1,
                                                  Node.VALUE_LABEL_D_DS1, 1)
        nodetemplateApex.setValueNumberOfVersions(coordinates, -1,
                                                  Node.VALUE_LABEL_D_DS2, 1)
        if useCubicHermiteThroughWall:
            nodetemplateApex.setValueNumberOfVersions(coordinates, -1,
                                                      Node.VALUE_LABEL_D_DS3,
                                                      1)
        if useCrossDerivatives:
            nodetemplate = nodes.createNodetemplate()
            nodetemplate.defineField(coordinates)
            nodetemplate.setValueNumberOfVersions(coordinates, -1,
                                                  Node.VALUE_LABEL_VALUE, 1)
            nodetemplate.setValueNumberOfVersions(coordinates, -1,
                                                  Node.VALUE_LABEL_D_DS1, 1)
            nodetemplate.setValueNumberOfVersions(coordinates, -1,
                                                  Node.VALUE_LABEL_D_DS2, 1)
            nodetemplate.setValueNumberOfVersions(coordinates, -1,
                                                  Node.VALUE_LABEL_D2_DS1DS2,
                                                  1)
            if useCubicHermiteThroughWall:
                nodetemplate.setValueNumberOfVersions(coordinates, -1,
                                                      Node.VALUE_LABEL_D_DS3,
                                                      1)
                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)
        else:
            nodetemplate = nodetemplateApex

        mesh = fm.findMeshByDimension(3)

        if useCubicHermiteThroughWall:
            eftfactory = eftfactory_tricubichermite(mesh, useCrossDerivatives)
        else:
            eftfactory = eftfactory_bicubichermitelinear(
                mesh, useCrossDerivatives)
        eft = eftfactory.createEftBasic()

        elementtemplate = mesh.createElementtemplate()
        elementtemplate.setElementShapeType(Element.SHAPE_TYPE_CUBE)
        elementtemplate.defineField(coordinates, -1, eft)

        cache = fm.createFieldcache()

        # create nodes
        nodeIdentifier = 1
        radiansPerElementAround = 2.0 * math.pi / elementsCountAround
        x = [0.0, 0.0, 0.0]
        dx_ds1 = [0.0, 0.0, 0.0]
        dx_ds2 = [0.0, 0.0, 0.0]
        dx_ds3 = [0.0, 0.0, 0.0]
        zero = [0.0, 0.0, 0.0]

        # pre-calculate positions and tangent/normal vectors up (elementsCountUp + 1) node layers
        outerWidth = 0.5
        outerLength = outerWidth * lengthRatio
        bOuter = 2.0 / (1.0 + elementLengthRatioEquatorApex / lengthRatio)
        aOuter = 1.0 - bOuter

        innerWidth = outerWidth - wallThickness
        innerLength = outerLength - wallThickness * wallThicknessRatioApex
        lengthRatioInner = innerLength / innerWidth if (
            innerWidth > 0.0) else lengthRatio
        bInner = 2.0 / (1.0 + elementLengthRatioEquatorApex / lengthRatio)
        aInner = 1.0 - bInner

        positionOuterArray = [(0, 0)] * (elementsCountUp + 1)
        positionInnerArray = [(0, 0)] * (elementsCountUp + 1)
        radiansUpOuterArray = [0] * (elementsCountUp + 1)
        radiansUpInnerArray = [0] * (elementsCountUp + 1)
        vector2OuterArray = [(0, 0)] * (elementsCountUp + 1)
        vector2InnerArray = [(0, 0)] * (elementsCountUp + 1)
        for n2 in range(elementsCountUp + 1):
            if n2 * 2 <= elementsCountUp:
                xi = n2 * 2 / elementsCountUp
            else:
                xi = 2.0 - (n2 * 2 / elementsCountUp)

            nxiOuter = aOuter * xi * xi + bOuter * xi
            dnxiOuter = 2.0 * aOuter * xi + bOuter
            radiansUpOuterArray[
                n2] = radiansUpOuter = nxiOuter * math.pi * 0.5 if (
                    n2 * 2 <= elementsCountUp) else (math.pi -
                                                     nxiOuter * math.pi * 0.5)
            dRadiansUpOuter = dnxiOuter * math.pi / elementsCountUp
            cosRadiansUpOuter = math.cos(radiansUpOuter)
            sinRadiansUpOuter = math.sin(radiansUpOuter)
            positionOuterArray[n2] = positionOuter = (outerWidth *
                                                      sinRadiansUpOuter,
                                                      -outerLength *
                                                      cosRadiansUpOuter)
            vector2OuterArray[n2] = (outerWidth * cosRadiansUpOuter *
                                     dRadiansUpOuter, outerLength *
                                     sinRadiansUpOuter * dRadiansUpOuter)

            nxiInner = aInner * xi * xi + bInner * xi
            dnxiInner = 2.0 * aInner * xi + bInner
            radiansUpInnerArray[
                n2] = radiansUpInner = nxiInner * math.pi * 0.5 if (
                    n2 * 2 <= elementsCountUp) else (math.pi -
                                                     nxiInner * math.pi * 0.5)
            dRadiansUpInner = dnxiInner * math.pi / elementsCountUp
            cosRadiansUpInner = math.cos(radiansUpInner)
            sinRadiansUpInner = math.sin(radiansUpInner)
            positionInnerArray[n2] = positionInner = (innerWidth *
                                                      sinRadiansUpInner,
                                                      -innerLength *
                                                      cosRadiansUpInner)
            vector2InnerArray[n2] = (innerWidth * cosRadiansUpInner *
                                     dRadiansUpInner, innerLength *
                                     sinRadiansUpInner * dRadiansUpInner)

        # now create the nodes
        for n3 in range(elementsCountThroughWall + 1):

            n3_fraction = n3 / elementsCountThroughWall

            for n2 in range(excludeBottomRows,
                            elementsCountUp + 1 - excludeTopRows):

                positionOuter = positionOuterArray[n2]
                positionInner = positionInnerArray[n2]
                position = (positionOuter[0] * n3_fraction + positionInner[0] *
                            (1.0 - n3_fraction),
                            positionOuter[1] * n3_fraction + positionInner[1] *
                            (1.0 - n3_fraction))

                radiansUpOuter = radiansUpOuterArray[n2]
                sinRadiansUpOuter = math.sin(radiansUpOuter)
                radiansUpInner = radiansUpInnerArray[n2]
                sinRadiansUpInner = math.sin(radiansUpInner)

                vector2Outer = vector2OuterArray[n2]
                vector2Inner = vector2InnerArray[n2]
                vector2 = (vector2Outer[0] * n3_fraction + vector2Inner[0] *
                           (1.0 - n3_fraction), vector2Outer[1] * n3_fraction +
                           vector2Inner[1] * (1.0 - n3_fraction))

                vector3 = ((positionOuter[0] - positionInner[0]) /
                           elementsCountThroughWall,
                           (positionOuter[1] - positionInner[1]) /
                           elementsCountThroughWall)

                if n2 == 0:
                    # create bottom apex node
                    node = nodes.createNode(nodeIdentifier, nodetemplateApex)
                    cache.setNode(node)
                    coordinates.setNodeParameters(cache, -1,
                                                  Node.VALUE_LABEL_VALUE, 1,
                                                  [0.0, 0.0, position[1]])
                    coordinates.setNodeParameters(cache, -1,
                                                  Node.VALUE_LABEL_D_DS1, 1,
                                                  [0.0, vector2[0], 0.0])
                    coordinates.setNodeParameters(cache, -1,
                                                  Node.VALUE_LABEL_D_DS2, 1,
                                                  [vector2[0], 0.0, 0.0])
                    if useCubicHermiteThroughWall:
                        coordinates.setNodeParameters(cache, -1,
                                                      Node.VALUE_LABEL_D_DS3,
                                                      1,
                                                      [0.0, 0.0, vector3[1]])
                    nodeIdentifier = nodeIdentifier + 1

                elif n2 < elementsCountUp:
                    # create regular rows between apexes
                    for n1 in range(elementsCountAround):
                        radiansAround = n1 * radiansPerElementAround
                        cosRadiansAround = math.cos(radiansAround)
                        sinRadiansAround = math.sin(radiansAround)
                        x = [
                            position[0] * cosRadiansAround,
                            position[0] * sinRadiansAround, position[1]
                        ]
                        dx_ds1 = [
                            position[0] * -sinRadiansAround *
                            radiansPerElementAround, position[0] *
                            cosRadiansAround * radiansPerElementAround, 0.0
                        ]
                        dx_ds2 = [
                            vector2[0] * cosRadiansAround,
                            vector2[0] * sinRadiansAround, vector2[1]
                        ]
                        dx_ds3 = [
                            vector3[0] * cosRadiansAround,
                            vector3[0] * sinRadiansAround, vector3[1]
                        ]
                        node = nodes.createNode(nodeIdentifier, nodetemplate)
                        cache.setNode(node)
                        coordinates.setNodeParameters(cache, -1,
                                                      Node.VALUE_LABEL_VALUE,
                                                      1, x)
                        coordinates.setNodeParameters(cache, -1,
                                                      Node.VALUE_LABEL_D_DS1,
                                                      1, dx_ds1)
                        coordinates.setNodeParameters(cache, -1,
                                                      Node.VALUE_LABEL_D_DS2,
                                                      1, dx_ds2)
                        if useCubicHermiteThroughWall:
                            coordinates.setNodeParameters(
                                cache, -1, Node.VALUE_LABEL_D_DS3, 1, dx_ds3)
                        if useCrossDerivatives:
                            coordinates.setNodeParameters(
                                cache, -1, Node.VALUE_LABEL_D2_DS1DS2, 1, zero)
                            if useCubicHermiteThroughWall:
                                coordinates.setNodeParameters(
                                    cache, -1, Node.VALUE_LABEL_D2_DS1DS3, 1,
                                    zero)
                                coordinates.setNodeParameters(
                                    cache, -1, Node.VALUE_LABEL_D2_DS2DS3, 1,
                                    zero)
                                coordinates.setNodeParameters(
                                    cache, -1, Node.VALUE_LABEL_D3_DS1DS2DS3,
                                    1, zero)
                        nodeIdentifier = nodeIdentifier + 1

                else:
                    # create top apex node
                    node = nodes.createNode(nodeIdentifier, nodetemplateApex)
                    cache.setNode(node)
                    coordinates.setNodeParameters(cache, -1,
                                                  Node.VALUE_LABEL_VALUE, 1,
                                                  [0.0, 0.0, position[1]])
                    coordinates.setNodeParameters(cache, -1,
                                                  Node.VALUE_LABEL_D_DS1, 1,
                                                  [0.0, vector2[0], 0.0])
                    coordinates.setNodeParameters(cache, -1,
                                                  Node.VALUE_LABEL_D_DS2, 1,
                                                  [-vector2[0], 0.0, 0.0])
                    if useCubicHermiteThroughWall:
                        coordinates.setNodeParameters(cache, -1,
                                                      Node.VALUE_LABEL_D_DS3,
                                                      1,
                                                      [0.0, 0.0, vector3[1]])
                    nodeIdentifier = nodeIdentifier + 1

        # create elements
        elementIdentifier = 1
        # now (node offset through wall) varies with number of excluded rows
        now = 0
        if excludeBottomRows == 0:
            now += 1  # bottom apex node
        if excludeTopRows == 0:
            now += 1  # top apex node
        fullNodeRows = elementsCountUp - 1
        if excludeBottomRows > 1:
            fullNodeRows -= (excludeBottomRows - 1)
        if excludeTopRows > 1:
            fullNodeRows -= (excludeTopRows - 1)
        if fullNodeRows > 0:
            now += fullNodeRows * elementsCountAround
        row2NodeOffset = 2 if (excludeBottomRows == 0) else 1
        for e3 in range(elementsCountThroughWall):
            no = e3 * now

            if excludeBottomRows == 0:
                # create bottom apex elements, editing eft scale factor identifiers around apex
                # scale factor identifiers follow convention of offsetting by 100 for each 'version'
                elementtemplate1 = mesh.createElementtemplate()
                elementtemplate1.setElementShapeType(Element.SHAPE_TYPE_CUBE)
                for e1 in range(elementsCountAround):
                    va = e1
                    vb = (e1 + 1) % elementsCountAround
                    eft1 = eftfactory.createEftShellPoleBottom(
                        va * 100, vb * 100)
                    elementtemplate1.defineField(coordinates, -1, eft1)
                    element = mesh.createElement(elementIdentifier,
                                                 elementtemplate1)
                    bni1 = no + 1
                    bni2 = no + e1 + 2
                    bni3 = no + (e1 + 1) % elementsCountAround + 2
                    nodeIdentifiers = [
                        bni1, bni2, bni3, bni1 + now, bni2 + now, bni3 + now
                    ]
                    element.setNodesByIdentifier(eft1, nodeIdentifiers)
                    # set general linear map coefficients
                    radiansAround = e1 * radiansPerElementAround
                    radiansAroundNext = (
                        (e1 + 1) %
                        elementsCountAround) * radiansPerElementAround
                    scalefactors = [
                        -1.0,
                        math.sin(radiansAround),
                        math.cos(radiansAround), radiansPerElementAround,
                        math.sin(radiansAroundNext),
                        math.cos(radiansAroundNext), radiansPerElementAround,
                        math.sin(radiansAround),
                        math.cos(radiansAround), radiansPerElementAround,
                        math.sin(radiansAroundNext),
                        math.cos(radiansAroundNext), radiansPerElementAround
                    ]
                    result = element.setScaleFactors(eft1, scalefactors)
                    elementIdentifier = elementIdentifier + 1

            # create regular rows between apexes
            rowLimit = (elementsCountUp - 2)
            if excludeBottomRows > 1:
                rowLimit -= (excludeBottomRows - 1)
            if excludeTopRows > 1:
                rowLimit -= (excludeTopRows - 1)
            for e2 in range(0, rowLimit):
                for e1 in range(elementsCountAround):
                    element = mesh.createElement(elementIdentifier,
                                                 elementtemplate)
                    bni11 = no + e2 * elementsCountAround + e1 + row2NodeOffset
                    bni12 = no + e2 * elementsCountAround + (
                        e1 + 1) % elementsCountAround + row2NodeOffset
                    bni21 = no + (
                        e2 + 1) * elementsCountAround + e1 + row2NodeOffset
                    bni22 = no + (e2 + 1) * elementsCountAround + (
                        e1 + 1) % elementsCountAround + row2NodeOffset
                    nodeIdentifiers = [
                        bni11, bni12, bni21, bni22, bni11 + now, bni12 + now,
                        bni21 + now, bni22 + now
                    ]
                    result = element.setNodesByIdentifier(eft, nodeIdentifiers)
                    elementIdentifier = elementIdentifier + 1

            if excludeTopRows == 0:
                # create top apex elements, editing eft scale factor identifiers around apex
                # scale factor identifiers follow convention of offsetting by 100 for each 'version'
                elementtemplate1 = mesh.createElementtemplate()
                elementtemplate1.setElementShapeType(Element.SHAPE_TYPE_CUBE)
                for e1 in range(elementsCountAround):
                    va = e1
                    vb = (e1 + 1) % elementsCountAround
                    eft1 = eftfactory.createEftShellPoleTop(va * 100, vb * 100)
                    elementtemplate1.defineField(coordinates, -1, eft1)
                    element = mesh.createElement(elementIdentifier,
                                                 elementtemplate1)
                    bni3 = no + now
                    bni1 = bni3 - elementsCountAround + e1
                    bni2 = bni3 - elementsCountAround + (
                        e1 + 1) % elementsCountAround
                    nodeIdentifiers = [
                        bni1, bni2, bni3, bni1 + now, bni2 + now, bni3 + now
                    ]
                    element.setNodesByIdentifier(eft1, nodeIdentifiers)
                    # set general linear map coefficients
                    radiansAround = math.pi + e1 * radiansPerElementAround
                    radiansAroundNext = math.pi + (
                        (e1 + 1) %
                        elementsCountAround) * radiansPerElementAround
                    scalefactors = [
                        -1.0, -math.sin(radiansAround),
                        math.cos(radiansAround), radiansPerElementAround,
                        -math.sin(radiansAroundNext),
                        math.cos(radiansAroundNext), radiansPerElementAround,
                        -math.sin(radiansAround),
                        math.cos(radiansAround), radiansPerElementAround,
                        -math.sin(radiansAroundNext),
                        math.cos(radiansAroundNext), radiansPerElementAround
                    ]
                    result = element.setScaleFactors(eft1, scalefactors)
                    elementIdentifier = elementIdentifier + 1

        if False:  # (wallThickness < 0.5):  # and (wallThicknessRatioApex != 1.0):
            r = fm.createFieldMagnitude(coordinates)
            const05 = fm.createFieldConstant([0.5])
            d = fm.createFieldSubtract(const05, r)
            d_r = fm.createFieldDivide(d, r)
            rRatio = 1.0 - wallThicknessRatioApex
            rScale = fm.createFieldConstant([rRatio])
            zScale = fm.createFieldMultiply(d_r, rScale)
            one = fm.createFieldConstant([1.0])
            one_plus_zScale = fm.createFieldAdd(one, zScale)
            scale = fm.createFieldConcatenate([one, one, one_plus_zScale])
            newCoordinates = fm.createFieldMultiply(coordinates, scale)
            fieldassignment = coordinates.createFieldassignment(newCoordinates)
            fieldassignment.assign()

        fm.endChange()
        return []
Ejemplo n.º 3
0
    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: None
        """
        parameterSetName = options['Base parameter set']
        isDefault = 'Default' in parameterSetName
        isMouse = 'Mouse' in parameterSetName
        isMean = 'mean' in parameterSetName

        fm = region.getFieldmodule()
        nodes = fm.findNodesetByFieldDomainType(Field.DOMAIN_TYPE_NODES)
        coordinates = findOrCreateFieldCoordinates(fm)
        mesh = fm.findMeshByDimension(3)
        cache = fm.createFieldcache()
        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)

        armCount = 3
        elementLengthCentral = options['Element width central']
        elementLengths = [
            options['Element length along arm'],
            options['Element width across arm'], options['Element thickness']
        ]
        elementsCountsAlongArms = options['Numbers of elements along arms']
        elementsCount2 = 2
        elementsCount3 = 1
        useCrossDerivatives = False
        # arm group annotations for user
        armTerms, _ = getAutomaticArmFaceTerms(armCount)
        armGroups = [AnnotationGroup(region, armTerm) for armTerm in armTerms]
        stellateTerm = get_stellate_term(
            "cervicothoracic ganglion") if isMouse else ("stellate", None)
        stellateGroup = AnnotationGroup(region, stellateTerm)
        annotationGroups = [stellateGroup] + armGroups
        armMeshGroups = [a.getMeshGroup(mesh) for a in armGroups]
        stellateMeshGroup = stellateGroup.getMeshGroup(mesh)

        # markers with element number and xi position
        allMarkers = {}
        if isMouse:
            xProportion = {}
            xProportion['ICN'] = 0.9
            xProportion['VA'] = 0.9
            xProportion['DA'] = 0.9
            xProportion['C8'] = 0.9
            xProportion['T1'] = 0.25
            xProportion['T2'] = 0.5
            xProportion['T3'] = 0.75
            xProportion['TST'] = 1
            armNumber = {}
            armNumber['ICN'] = 2
            armNumber['VA'] = 2
            armNumber['DA'] = 3
            armNumber['C8'] = 3
            armNumber['T1'] = 1
            armNumber['T2'] = 1
            armNumber['T3'] = 1
            armNumber['TST'] = 1
            nerveAbbrev = list(xProportion.keys())
            elementIndex = {}
            xi1 = {}
            for nerve in nerveAbbrev:
                elementIndex[nerve] = int(
                    xProportion[nerve] *
                    elementsCountsAlongArms[armNumber[nerve] - 1])
                xi1[nerve] = 1 if xProportion[nerve] == 1 else xProportion[
                    nerve] * elementsCountsAlongArms[armNumber[nerve] -
                                                     1] - elementIndex[nerve]
                elementIndex[nerve] += 1 if xProportion[nerve] < 1 else 0

            allMarkers = {
                "Inferior cardiac nerve": {
                    "elementID":
                    elementIndex['ICN'] + 2 * elementsCountsAlongArms[0],
                    "xi": [xi1['ICN'], 0.0, 0.5]
                },
                "Ventral ansa subclavia": {
                    "elementID":
                    elementIndex['VA'] + 2 * elementsCountsAlongArms[0] +
                    elementsCountsAlongArms[1],
                    "xi": [xi1['VA'], 1.0, 0.5]
                },
                "Dorsal ansa subclavia": {
                    "elementID":
                    elementIndex['DA'] + 2 *
                    (elementsCountsAlongArms[0] + elementsCountsAlongArms[1]),
                    "xi": [xi1['DA'], 0.0, 0.5]
                },
                "Cervical spinal nerve 8": {
                    "elementID":
                    elementIndex['C8'] + 2 *
                    (elementsCountsAlongArms[0] + elementsCountsAlongArms[1]) +
                    elementsCountsAlongArms[2],
                    "xi": [xi1['C8'], 1.0, 0.5]
                },
                "Thoracic spinal nerve 1": {
                    "elementID": elementIndex['T1'],
                    "xi": [xi1['T1'], 0.0, 0.5]
                },
                "Thoracic spinal nerve 2": {
                    "elementID": elementIndex['T2'],
                    "xi": [xi1['T2'], 0.0, 0.5]
                },
                "Thoracic spinal nerve 3": {
                    "elementID": elementIndex['T3'],
                    "xi": [xi1['T3'], 0.0, 0.5]
                },
                "Thoracic sympathetic nerve trunk": {
                    "elementID": elementIndex['TST'],
                    "xi": [xi1['TST'], 1.0, 0.5]
                },
            }
            markerGroup = findOrCreateFieldGroup(fm, "marker")
            markerName = findOrCreateFieldStoredString(fm, name="marker_name")
            markerLocation = findOrCreateFieldStoredMeshLocation(
                fm, mesh, name="marker_location")

            markerPoints = findOrCreateFieldNodeGroup(markerGroup,
                                                      nodes).getNodesetGroup()
            markerTemplateInternal = nodes.createNodetemplate()
            markerTemplateInternal.defineField(markerName)
            markerTemplateInternal.defineField(markerLocation)

        # Create nodes
        nodeIdentifier = 1
        minArmAngle = 2 * math.pi / armCount
        halfArmArcAngleRadians = minArmAngle / 2
        if not isMean:
            dipMultiplier = 1
            for na in range(armCount):
                elementsCount_i = [
                    elementsCountsAlongArms[na], elementsCount2, elementsCount3
                ]
                x, ds1, ds2, nWheelEdge = createArm(halfArmArcAngleRadians,
                                                    elementLengths,
                                                    elementLengthCentral,
                                                    elementsCount_i,
                                                    dipMultiplier, armCount,
                                                    na)
                for ix in range(len(x)):
                    if na == 0 or ix not in nWheelEdge:
                        node = nodes.createNode(nodeIdentifier, nodetemplate)
                        cache.setNode(node)
                        coordinates.setNodeParameters(cache, -1,
                                                      Node.VALUE_LABEL_VALUE,
                                                      1, x[ix])
                        coordinates.setNodeParameters(cache, -1,
                                                      Node.VALUE_LABEL_D_DS1,
                                                      1, ds1[ix])
                        coordinates.setNodeParameters(cache, -1,
                                                      Node.VALUE_LABEL_D_DS2,
                                                      1, ds2[ix])
                        nodeIdentifier += 1
        else:
            x_dx_all = cls.mouseMeanMesh['meshEdits']
            xyz_all = [x[0] for x in x_dx_all]
            dxyz = [[x[1], x[2]] for x in x_dx_all]
            nodeIdentifier = 1
            for i, nx in enumerate(xyz_all):
                node = nodes.createNode(nodeIdentifier, nodetemplate)
                cache.setNode(node)
                coordinates.setNodeParameters(cache, -1,
                                              Node.VALUE_LABEL_VALUE, 1, nx)
                coordinates.setNodeParameters(cache, -1,
                                              Node.VALUE_LABEL_D_DS1, 1,
                                              dxyz[i][0])
                coordinates.setNodeParameters(cache, -1,
                                              Node.VALUE_LABEL_D_DS2, 1,
                                              dxyz[i][1])
                nodeIdentifier += 1
        nodesCountsPerArm = [0] + [((elementsCount2 + 1) * e + 1) * 2
                                   for e in elementsCountsAlongArms]

        # Create elements
        bicubichermitelinear = eftfactory_bicubichermitelinear(
            mesh, useCrossDerivatives)
        eft = bicubichermitelinear.createEftNoCrossDerivatives(
        )  #createEftBasic()
        elementtemplate = mesh.createElementtemplate()
        elementtemplate.setElementShapeType(Element.SHAPE_TYPE_CUBE)
        elementtemplate.defineField(coordinates, -1, eft)
        elementtemplateX = mesh.createElementtemplate()
        elementtemplateX.setElementShapeType(Element.SHAPE_TYPE_CUBE)
        elementIdentifier = 1

        cumNodesCountsPerArm = [
            sum(nodesCountsPerArm[:i + 1])
            for i in range(len(nodesCountsPerArm))
        ]
        nCentre = [
            elementsCountsAlongArms[0] + 1,
            int(nodesCountsPerArm[1] / 2) + elementsCountsAlongArms[0] + 1
        ]
        for na in range(armCount):
            for e3 in range(elementsCount3):
                for e2 in range(elementsCount2):
                    for e1 in range(elementsCountsAlongArms[na]):
                        scalefactors = None
                        ### NODES ###
                        no2 = (elementsCountsAlongArms[na] + 1)
                        no3 = (elementsCount2 + 1) * no2 - 2
                        offset = (cumNodesCountsPerArm[na])
                        bni = e3 * no3 + e2 * no2 + e1 + 1 + offset
                        if e2 == 0:
                            if e1 == 0 and na > 0:  # and na < armCount -1: # wheelSouth
                                nWh = cumNodesCountsPerArm[na - 1] + (
                                    2 * elementsCountsAlongArms[na - 1]) + 2
                                nplUq = int(
                                    nodesCountsPerArm[na + 1] / 2
                                ) - elementsCountsAlongArms[
                                    na]  # unused nodes at centre and shared edge
                                npl = int(
                                    nodesCountsPerArm[na + 1] /
                                    2)  #  nodes at centre and shared edge
                                if na < armCount - 1:
                                    cn = cumNodesCountsPerArm[
                                        na] + elementsCountsAlongArms[na] - 2
                                    no2 = cumNodesCountsPerArm[na]
                                    em = elementsCountsAlongArms[na]
                                    nwPrev = [
                                        nWh,
                                        nWh + int(nodesCountsPerArm[na] / 2)
                                    ]  # previous arm's edge, depends on armCount.
                                    nodeIdentifiers = [
                                        nwPrev[0], no2 + 1, nCentre[0],
                                        no2 + em, nwPrev[1],
                                        no2 + em - 1 + nplUq, nCentre[1],
                                        bni + (4 * em) - 2
                                    ]
                                else:
                                    nplPrev = int(
                                        nodesCountsPerArm[na] / 2) - 2
                                    no2 = elementsCountsAlongArms[na] - 1
                                    no3 = int(
                                        nodesCountsPerArm[na + 1] / 2) - 3
                                    nwPrev = [
                                        cumNodesCountsPerArm[na - 1] + 2 *
                                        (elementsCountsAlongArms[na - 1]),
                                        cumNodesCountsPerArm[na - 1] + 2 *
                                        (elementsCountsAlongArms[na - 1]) +
                                        nplPrev
                                    ]
                                    start = cumNodesCountsPerArm[na] - 3
                                    nodeIdentifiers = [
                                        nwPrev[0], start, nCentre[0],
                                        start + no2, nwPrev[1], start + no3,
                                        nCentre[1], start + no2 + no3
                                    ]
                            elif e1 == elementsCountsAlongArms[
                                    na] - 1:  # armEnd, south
                                if na == 0:
                                    nodeIdentifiers = [
                                        bni, bni + no2 - 1, bni + no2,
                                        bni + no3, bni + no2 + no3 - 1,
                                        bni + no2 + no3
                                    ]
                                else:
                                    no3 = armCount * elementsCountsAlongArms[
                                        na] - 1
                                    no2 = elementsCountsAlongArms[na]
                                    if na > 1:
                                        bni -= 4
                                        no3 -= 1
                                    nodeIdentifiers = [
                                        bni - 1, bni + no2 - 2, bni + no2 - 1,
                                        bni + no3 - 1, bni + no2 - 2 + no3,
                                        bni + no2 + no3 - 1
                                    ]
                            elif na > 0 and e1 > 0:  #  [na=1+, e1=1+, e2=0] for len=3+
                                bni -= 1 + ((armCount + 1) * (na - 1))
                                no2 = elementsCountsAlongArms[na]
                                no3 = armCount * no2 - (na - 1) - 1
                                nodeIdentifiers = [
                                    bni, bni + 1, bni + no2 - 1, bni + no2,
                                    bni + no3, bni + no3 + 1,
                                    bni + no2 + no3 - 1, bni + no2 + no3
                                ]
                            else:
                                nodeIdentifiers = [
                                    bni, bni + 1, bni + no2 - 1, bni + no2,
                                    bni + no3, bni + no3 + 1,
                                    bni + no2 + no3 - 1, bni + no2 + no3
                                ]
                        else:
                            if e1 == 0 and na > 0:  # and na < armCount -1: # wheelNorth
                                if na < armCount - 1:
                                    bni -= armCount
                                    npl = int(
                                        nodesCountsPerArm[na + 1] / 2) - 2
                                    no2 = elementsCountsAlongArms[na]
                                    nodeIdentifiers = [
                                        nCentre[0], bni + 1, bni + no2 + 1,
                                        bni + no2 + 2, nCentre[1],
                                        bni + npl + 1, bni + npl + no2 + 1,
                                        bni + npl + no2 + 2
                                    ]
                                else:  # last arm
                                    bni = cumNodesCountsPerArm[na] - 2 - (
                                        armCount - elementsCountsAlongArms[na])
                                    nodeIdentifiers = [
                                        nCentre[0], bni + 1, 1, bni + no2,
                                        nCentre[1], bni + no3 - 2,
                                        int(nodesCountsPerArm[1] / 2) + 1,
                                        bni + no2 + no3 - armCount
                                    ]
                            elif e1 == elementsCountsAlongArms[
                                    na] - 1:  # armEnd north
                                if na > 0:
                                    no2 = elementsCountsAlongArms[na]
                                    nplUq = int(
                                        nodesCountsPerArm[na + 1] / 2) - 2
                                    if na > 1:
                                        adj = na - 1
                                        bni -= armCount * na + (
                                            armCount -
                                            elementsCountsAlongArms[na]) + 1
                                        if elementsCountsAlongArms[na] < 3:
                                            bni += 1
                                        if elementsCountsAlongArms[na] > 3:
                                            bni -= elementsCountsAlongArms[
                                                na] - 3
                                        no2 += 1 - adj
                                        no3 = nplUq - adj
                                        nodeIdentifiers = [
                                            bni, bni + 1, bni + no2, bni + no3,
                                            bni + no3 + 1, bni + no2 + no3
                                        ]
                                    else:
                                        bni -= armCount
                                        nodeIdentifiers = [
                                            bni, bni + 1, bni + no2 + 1,
                                            bni + nplUq, bni + nplUq + 1,
                                            bni + no2 + nplUq + 1
                                        ]
                                else:
                                    nodeIdentifiers = [
                                        bni - 1, bni, bni + no2 - 1,
                                        bni + no3 - 1, bni + no3,
                                        bni + no2 + no3 - 1
                                    ]

                            elif na > 0 and e1 > 0:  #  [na=1+, e1=1+, e2=1] for len=3+
                                adj = na - 1
                                bni -= armCount * na + adj
                                no2 -= adj
                                k = armCount * elementsCountsAlongArms[na] - na
                                nodeIdentifiers = [
                                    bni, bni + 1, bni + no2, bni + no2 + 1,
                                    bni + k, bni + k + 1, bni + no2 + k,
                                    bni + no2 + k + 1
                                ]
                            else:
                                nodeIdentifiers = [
                                    bni - 1, bni, bni + no2 - 1, bni + no2,
                                    bni + no3 - 1, bni + no3,
                                    bni + no2 + no3 - 1, bni + no2 + no3
                                ]

                        if e1 == 0:  # wheel
                            eft1 = bicubichermitelinear.createEftNoCrossDerivatives(
                            )
                            if armCount == 3:
                                if e2 == 0:
                                    setEftScaleFactorIds(eft1, [1], [])
                                    scalefactors = [-1.0]
                                    scaleEftNodeValueLabels(
                                        eft1, [1, 5], [
                                            Node.VALUE_LABEL_D_DS1,
                                            Node.VALUE_LABEL_D_DS2
                                        ], [1])
                                    ns = [3, 7]
                                else:
                                    ns = [1, 5]
                                if na == 0:
                                    remapEftNodeValueLabel(
                                        eft1, ns, Node.VALUE_LABEL_D_DS1,
                                        [(Node.VALUE_LABEL_D_DS1, []),
                                         (Node.VALUE_LABEL_D_DS2, [])])
                                    if e2 == 0:
                                        setEftScaleFactorIds(eft1, [1], [])
                                        scalefactors = [-1.0]
                                        remapEftNodeValueLabel(
                                            eft1, ns, Node.VALUE_LABEL_D_DS2,
                                            [(Node.VALUE_LABEL_D_DS1, [1])])
                                elif na == 1:
                                    setEftScaleFactorIds(eft1, [1], [])
                                    scalefactors = [-1.0]
                                    remapEftNodeValueLabel(
                                        eft1, ns, Node.VALUE_LABEL_D_DS1,
                                        [(Node.VALUE_LABEL_D_DS1, [1])])
                                    if e2 == 0:
                                        remapEftNodeValueLabel(
                                            eft1, ns, Node.VALUE_LABEL_D_DS2,
                                            [(Node.VALUE_LABEL_D_DS2, [1])])
                                    elif e2 == 1:
                                        remapEftNodeValueLabel(
                                            eft1, ns, Node.VALUE_LABEL_D_DS2,
                                            [(Node.VALUE_LABEL_D_DS1, [1]),
                                             (Node.VALUE_LABEL_D_DS2, [1])])
                                elif na == 2:
                                    setEftScaleFactorIds(eft1, [1], [])
                                    scalefactors = [-1.0]
                                    remapEftNodeValueLabel(
                                        eft1, ns, Node.VALUE_LABEL_D_DS1,
                                        [(Node.VALUE_LABEL_D_DS2, [1])])
                                    if e2 == 0:
                                        remapEftNodeValueLabel(
                                            eft1, ns, Node.VALUE_LABEL_D_DS2,
                                            [(Node.VALUE_LABEL_D_DS1, []),
                                             (Node.VALUE_LABEL_D_DS2, [])])
                                    elif e2 == 1:
                                        remapEftNodeValueLabel(
                                            eft1, ns, Node.VALUE_LABEL_D_DS2,
                                            [(Node.VALUE_LABEL_D_DS1, [])])
                            elif armCount == 4:
                                if e2 == 0:
                                    setEftScaleFactorIds(eft1, [1], [])
                                    scalefactors = [-1.0]
                                    scaleEftNodeValueLabels(
                                        eft1, [1, 5], [
                                            Node.VALUE_LABEL_D_DS1,
                                            Node.VALUE_LABEL_D_DS2
                                        ], [1])
                                    ns = [3, 7]
                                else:
                                    ns = [1, 5]
                                if na == 0:
                                    remapEftNodeValueLabel(
                                        eft1, ns, Node.VALUE_LABEL_D_DS1,
                                        [(Node.VALUE_LABEL_D_DS1, []),
                                         (Node.VALUE_LABEL_D_DS2, [])])
                                    if e2 == 0:
                                        setEftScaleFactorIds(eft1, [1], [])
                                        scalefactors = [-1.0]
                                        remapEftNodeValueLabel(
                                            eft1, ns, Node.VALUE_LABEL_D_DS2,
                                            [(Node.VALUE_LABEL_D_DS1, [1])])
                                elif na == 1:
                                    setEftScaleFactorIds(eft1, [1], [])
                                    scalefactors = [-1.0]
                                    remapEftNodeValueLabel(
                                        eft1, ns, Node.VALUE_LABEL_D_DS1,
                                        [(Node.VALUE_LABEL_D_DS1, [1]),
                                         (Node.VALUE_LABEL_D_DS2, [])])
                                    if e2 == 0:
                                        remapEftNodeValueLabel(
                                            eft1, ns, Node.VALUE_LABEL_D_DS2,
                                            [(Node.VALUE_LABEL_D_DS2, [1])])
                                    else:
                                        remapEftNodeValueLabel(
                                            eft1, ns, Node.VALUE_LABEL_D_DS2,
                                            [(Node.VALUE_LABEL_D_DS1, [1])])
                                elif na == 2:
                                    setEftScaleFactorIds(eft1, [1], [])
                                    scalefactors = [-1.0]
                                    remapEftNodeValueLabel(
                                        eft1, ns, Node.VALUE_LABEL_D_DS1,
                                        [(Node.VALUE_LABEL_D_DS1, [1]),
                                         (Node.VALUE_LABEL_D_DS2, [1])])
                                    if e2 == 0:
                                        remapEftNodeValueLabel(
                                            eft1, ns, Node.VALUE_LABEL_D_DS2,
                                            [(Node.VALUE_LABEL_D_DS1, [])])
                                    else:
                                        remapEftNodeValueLabel(
                                            eft1, ns, Node.VALUE_LABEL_D_DS2,
                                            [(Node.VALUE_LABEL_D_DS2, [1])])
                                elif na == 3:
                                    setEftScaleFactorIds(eft1, [1], [])
                                    scalefactors = [-1.0]
                                    remapEftNodeValueLabel(
                                        eft1, ns, Node.VALUE_LABEL_D_DS1,
                                        [(Node.VALUE_LABEL_D_DS1, []),
                                         (Node.VALUE_LABEL_D_DS2, [1])])
                                    if e2 == 0:
                                        remapEftNodeValueLabel(
                                            eft1, ns, Node.VALUE_LABEL_D_DS2,
                                            [(Node.VALUE_LABEL_D_DS2, [])])
                                    else:
                                        remapEftNodeValueLabel(
                                            eft1, ns, Node.VALUE_LABEL_D_DS2,
                                            [(Node.VALUE_LABEL_D_DS1, [])])

                        elif e1 < (elementsCountsAlongArms[na] - 1):
                            eft1 = eft
                            elementtemplate1 = elementtemplate
                        else:
                            # rounded ends of arms. Collapse xi2 at xi1 = 1
                            eft1 = bicubichermitelinear.createEftNoCrossDerivatives(
                            )
                            remapEftNodeValueLabel(eft1, [2, 4, 6, 8],
                                                   Node.VALUE_LABEL_D_DS2, [])
                            if e2 == 0:
                                remapEftNodeValueLabel(
                                    eft1, [2, 6], Node.VALUE_LABEL_D_DS1,
                                    [(Node.VALUE_LABEL_D_DS2, [])])
                                nodeIdentifiers = [
                                    nodeIdentifiers[0], nodeIdentifiers[2],
                                    nodeIdentifiers[1], nodeIdentifiers[3],
                                    nodeIdentifiers[5], nodeIdentifiers[4]
                                ]
                            else:  # e2 == 1
                                setEftScaleFactorIds(eft1, [1], [])
                                scalefactors = [-1.0]
                                remapEftNodeValueLabel(
                                    eft1, [4, 8], Node.VALUE_LABEL_D_DS1,
                                    [(Node.VALUE_LABEL_D_DS2, [1])])
                            ln_map = [1, 2, 3, 2, 4, 5, 6, 5]
                            remapEftLocalNodes(eft1, 6, ln_map)

                        if eft1 is not eft:
                            elementtemplateX.defineField(coordinates, -1, eft1)
                            elementtemplate1 = elementtemplateX

                        element = mesh.createElement(elementIdentifier,
                                                     elementtemplate1)
                        result = element.setNodesByIdentifier(
                            eft1, nodeIdentifiers)
                        result3 = element.setScaleFactors(
                            eft1, scalefactors) if scalefactors else None

                        # add to meshGroup
                        stellateMeshGroup.addElement(element)
                        armMeshGroups[na].addElement(element)

                        elementIdentifier += 1

        # annotation fiducial points
        if isMouse:
            for key in allMarkers:
                xi = allMarkers[key]["xi"]
                addMarker = {"name": key, "xi": allMarkers[key]["xi"]}

                markerPoint = markerPoints.createNode(nodeIdentifier,
                                                      markerTemplateInternal)
                nodeIdentifier += 1
                cache.setNode(markerPoint)
                markerName.assignString(cache, addMarker["name"])
                elementID = allMarkers[key]["elementID"]
                element = mesh.findElementByIdentifier(elementID)
                markerLocation.assignMeshLocation(cache, element,
                                                  addMarker["xi"])

        return annotationGroups
Ejemplo n.º 4
0
    def generateBaseMesh(cls, region, options):
        '''
        Generate the base bicubic Hermite mesh.
        :param region: Zinc region to define model in. Must be empty.
        :param options: Dict containing options. See getDefaultOptions().
        :return: list of AnnotationGroup
        '''
        elementsCountUpNeck = options['Number of elements up neck']
        elementsCountUpBody = options['Number of elements up body']
        elementsCountAround = options['Number of elements around']
        height = options['Height']
        majorDiameter = options['Major diameter']
        minorDiameter = options['Minor diameter']
        radius = 0.5 * options['Urethra diameter']
        bladderWallThickness = options['Bladder wall thickness']
        useCrossDerivatives = options['Use cross derivatives']
        elementsCountAroundOstium = options['Number of elements around ostium']
        elementsCountAnnulusRadially = options['Number of elements radially on annulus']
        ostiumPositionAround = options['Ostium position around']
        ostiumPositionUp = options['Ostium position up']

        ostiumOptions = options['Ureter']
        ostiumDefaultOptions = ostiumOptions.getScaffoldSettings()

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

        mesh = fm.findMeshByDimension(3)

        nodes = fm.findNodesetByFieldDomainType(Field.DOMAIN_TYPE_NODES)
        nodetemplateApex = nodes.createNodetemplate()
        nodetemplateApex.defineField(coordinates)
        nodetemplateApex.setValueNumberOfVersions(coordinates, -1, Node.VALUE_LABEL_VALUE, 1)
        nodetemplateApex.setValueNumberOfVersions(coordinates, -1, Node.VALUE_LABEL_D_DS1, 1)
        nodetemplateApex.setValueNumberOfVersions(coordinates, -1, Node.VALUE_LABEL_D_DS2, 1)
        if useCrossDerivatives:
            nodetemplate = nodes.createNodetemplate()
            nodetemplate.defineField(coordinates)
            nodetemplate.setValueNumberOfVersions(coordinates, -1, Node.VALUE_LABEL_VALUE, 1)
            nodetemplate.setValueNumberOfVersions(coordinates, -1, Node.VALUE_LABEL_D_DS1, 1)
            nodetemplate.setValueNumberOfVersions(coordinates, -1, Node.VALUE_LABEL_D_DS2, 1)
            nodetemplate.setValueNumberOfVersions(coordinates, -1, Node.VALUE_LABEL_D2_DS1DS2, 1)
        else:
            nodetemplate = nodetemplateApex

        eftfactory = eftfactory_bicubichermitelinear(mesh, useCrossDerivatives)
        eft = eftfactory.createEftBasic()

        elementtemplate = mesh.createElementtemplate()
        elementtemplate.setElementShapeType(Element.SHAPE_TYPE_CUBE)
        elementtemplate.defineField(coordinates, -1, eft)

        neckGroup = AnnotationGroup(region, get_bladder_term("neck of urinary bladder"))
        bodyGroup = AnnotationGroup(region, get_bladder_term("dome of the bladder"))
        urinaryBladderGroup = AnnotationGroup(region, get_bladder_term("urinary bladder"))
        annotationGroups = [neckGroup, bodyGroup, urinaryBladderGroup]

        neckMeshGroup = neckGroup.getMeshGroup(mesh)
        bodyMeshGroup = bodyGroup.getMeshGroup(mesh)
        urinaryBladderMeshGroup = urinaryBladderGroup.getMeshGroup(mesh)

        # create nodes
        # create neck of the bladder
        nodeIdentifier = 1
        radiansPerElementAround = 2.0*math.pi/elementsCountAround
        radiansPerElementUpNeck = (math.pi/4)/elementsCountUpNeck

        # create lower part of the ellipsoidal
        neckHeight = height - height * math.cos(math.pi / 4)
        ellipsoidal_x = []
        ellipsoidal_d1 = []
        ellipsoidal_d2 = []
        for n2 in range(0, elementsCountUpNeck+1):
            radiansUp = n2 * radiansPerElementUpNeck
            cosRadiansUp = math.cos(radiansUp)
            sinRadiansUp = math.sin(radiansUp)
            majorRadius = 0.5 * majorDiameter * sinRadiansUp
            minorRadius = 0.5 * minorDiameter * sinRadiansUp
            if n2 == 0:
                for n1 in range(elementsCountAround):
                    radiansAround = n1 * radiansPerElementAround
                    cosRadiansAround = math.cos(radiansAround)
                    sinRadiansAround = math.sin(radiansAround)
                    x = [
                        -majorRadius * sinRadiansAround,
                        minorRadius * cosRadiansAround,
                        -height - neckHeight
                    ]
                    dx_ds1 = [
                        -majorRadius * cosRadiansAround * radiansPerElementAround,
                        minorRadius * -sinRadiansAround * radiansPerElementAround,
                        0.0
                    ]
                    dx_ds2 = [
                        -0.5 * majorDiameter * sinRadiansAround * cosRadiansUp * radiansPerElementUpNeck,
                        0.5 * minorDiameter * cosRadiansAround * cosRadiansUp * radiansPerElementUpNeck,
                        height * sinRadiansUp * radiansPerElementUpNeck
                    ]
                    ellipsoidal_x.append(x)
                    ellipsoidal_d1.append(dx_ds1)
                    ellipsoidal_d2.append(dx_ds2)
            else:
                for n1 in range(elementsCountAround):
                    neckHeight = height - height * math.cos(math.pi/4)
                    radiansAround = n1 * radiansPerElementAround
                    cosRadiansAround = math.cos(radiansAround)
                    sinRadiansAround = math.sin(radiansAround)
                    x = [
                        -majorRadius * sinRadiansAround,
                        minorRadius * cosRadiansAround,
                        -height - neckHeight + n2 * 2 * neckHeight / elementsCountUpNeck
                    ]
                    dx_ds1 = [
                        -majorRadius * cosRadiansAround * radiansPerElementAround,
                        minorRadius * -sinRadiansAround * radiansPerElementAround,
                        0.0
                    ]
                    dx_ds2 = [
                        -0.5 * majorDiameter * sinRadiansAround * cosRadiansUp * radiansPerElementUpNeck,
                        0.5 * minorDiameter * cosRadiansAround * cosRadiansUp * radiansPerElementUpNeck,
                        height * sinRadiansUp * radiansPerElementUpNeck
                    ]
                    ellipsoidal_x.append(x)
                    ellipsoidal_d1.append(dx_ds1)
                    ellipsoidal_d2.append(dx_ds2)

        # create tube nodes
        radiansPerElementAround = 2.0 * math.pi / elementsCountAround
        tube_x = []
        tube_d1 = []
        tube_d2 = []
        for n2 in range(0, elementsCountUpNeck + 1):
            radiansUp = n2 * radiansPerElementUpNeck
            cosRadiansUp = math.cos(radiansUp)
            sinRadiansUp = math.sin(radiansUp)
            if n2 == 0:
                for n1 in range(elementsCountAround):
                    radiansAround = n1 * radiansPerElementAround
                    cosRadiansAround = math.cos(radiansAround)
                    sinRadiansAround = math.sin(radiansAround)
                    x = [
                        -radius * sinRadiansAround,
                        radius * cosRadiansAround,
                        -height - neckHeight
                    ]
                    dx_ds1 = [
                        -radiansPerElementAround * radius * cosRadiansAround,
                        radiansPerElementAround * radius * -sinRadiansAround,
                        0.0
                    ]
                    dx_ds2 = [0, 0, height / (2 * elementsCountUpNeck)]
                    tube_x.append(x)
                    tube_d1.append(dx_ds1)
                    tube_d2.append(dx_ds2)
            else:
                for n1 in range(elementsCountAround):
                    neckHeight = height - height* math.cos(math.pi/4)
                    radiansAround = n1 * radiansPerElementAround
                    cosRadiansAround = math.cos(radiansAround)
                    sinRadiansAround = math.sin(radiansAround)
                    x = [
                        -radius * sinRadiansAround,
                        radius * cosRadiansAround,
                        -height - neckHeight + n2 * 2 * neckHeight / elementsCountUpNeck
                    ]
                    dx_ds1 = [
                        -radiansPerElementAround * radius * cosRadiansAround,
                        radiansPerElementAround * radius * -sinRadiansAround,
                        0.0
                    ]
                    dx_ds2 = [0, 0, height / elementsCountUpNeck]
                    tube_x.append(x)
                    tube_d1.append(dx_ds1)
                    tube_d2.append(dx_ds2)

        # interpolation between the lower part of the ellipsoidal and the tube
        m1 = 0
        z_bottom = ellipsoidal_x[-1][2]
        z_top = ellipsoidal_x[0][2]
        delta_z = z_top - z_bottom
        interpolatedNodes = []
        interpolatedNodes_d1 = []
        interpolatedNodes_d2 = []
        for n2 in range(elementsCountUpNeck+1):
            xi = 1.0 - (ellipsoidal_x[m1][2] - z_bottom) / delta_z
            for n1 in range(elementsCountAround):
                phi_inner, _, phi_outer, _ = getCubicHermiteBasis(xi)
                x = [(phi_inner*tube_x[m1][c] + phi_outer*ellipsoidal_x[m1][c]) for c in range(3)]
                d1 = [(phi_inner*tube_d1[m1][c] + phi_outer*ellipsoidal_d1[m1][c]) for c in range(3)]
                d2 = [(phi_inner*tube_d2[m1][c] + phi_outer*ellipsoidal_d2[m1][c]) for c in range(3)]
                interpolatedNodes.append(x)
                interpolatedNodes_d1.append(d1)
                interpolatedNodes_d2.append(d2)
                m1 += 1

        # smoothing the derivatives
        sd2Raw = []
        for n1 in range(elementsCountAround):
            lineSmoothingNodes = []
            lineSmoothingNodes_d2 = []
            for n2 in range(elementsCountUpNeck+1):
                    lineSmoothingNodes.append(interpolatedNodes[n1 + n2 * elementsCountAround])
                    lineSmoothingNodes_d2.append(interpolatedNodes_d2[n1 + n2 * elementsCountAround])
            sd2 = smoothCubicHermiteDerivativesLine(lineSmoothingNodes, lineSmoothingNodes_d2,
                                                    fixAllDirections=False,
                                                    fixStartDerivative=True, fixEndDerivative=True,
                                                    fixStartDirection=False, fixEndDirection=False)
            sd2Raw.append(sd2)

        # re-arrange the derivatives order
        d2RearrangedList = []
        for n2 in range(elementsCountUpNeck+1):
            for n1 in range(elementsCountAround):
                d2 = sd2Raw[n1][n2]
                d2RearrangedList.append(d2)

        # create tracksurface at the outer layer of the neck
        nodesOnTrackSurface = []
        nodesOnTrackSurface_d1 = []
        nodesOnTrackSurface_d2 = []
        for n2 in range(elementsCountUpNeck+1):
            for n1 in range(elementsCountAround):
                if (n1 <= elementsCountAround / 2):
                    nodesOnTrackSurface.append(interpolatedNodes[n2 * elementsCountAround + n1])
                    nodesOnTrackSurface_d1.append(interpolatedNodes_d1[n2 * elementsCountAround + n1])
                    nodesOnTrackSurface_d2.append(d2RearrangedList[n2 * elementsCountAround + n1])

        # nodes and derivatives of the neck of the bladder
        listOuterNeck_x = []
        listOuterNeck_d1 = []
        listOuterNeck_d2 = []
        elementsCount1 = elementsCountAround // 2
        elementsCount2 = elementsCountUpNeck
        tracksurfaceOstium1 = TrackSurface(elementsCount1, elementsCount2, nodesOnTrackSurface, nodesOnTrackSurface_d1,
                                    nodesOnTrackSurface_d2)
        ostium1Position = tracksurfaceOstium1.createPositionProportion(ostiumPositionAround, ostiumPositionUp)
        ostium1Position.xi1 = 1.0
        ostium1Position.xi2 = 1.0
        ostiumElementPositionAround = ostium1Position.e1
        ostiumElementPositionUp = ostium1Position.e2
        for n2 in range(len(interpolatedNodes)):
            listOuterNeck_x.append(interpolatedNodes[n2])
            listOuterNeck_d1.append(interpolatedNodes_d1[n2])
            listOuterNeck_d2.append(d2RearrangedList[n2])

        # create body of the bladder
        radiansPerElementAround = 2.0 * math.pi / elementsCountAround
        radiansPerElementUpBody = (3 * math.pi / 4) / elementsCountUpBody
        # create regular rows
        listOuterBody_x = []
        listOuterBody_d1 = []
        listOuterBody_d2 = []
        for n2 in range(1, elementsCountUpBody):
            radiansUp = (math.pi / 4) + n2 * radiansPerElementUpBody
            cosRadiansUp = math.cos(radiansUp)
            sinRadiansUp = math.sin(radiansUp)
            majorRadius = 0.5 * majorDiameter * sinRadiansUp
            minorRadius = 0.5 * minorDiameter * sinRadiansUp
            for n1 in range(elementsCountAround):
                radiansAround = n1 * radiansPerElementAround
                cosRadiansAround = math.cos(radiansAround)
                sinRadiansAround = math.sin(radiansAround)
                x = [
                    -majorRadius * sinRadiansAround,
                    minorRadius * cosRadiansAround,
                    -height * cosRadiansUp
                ]
                dx_ds1 = [
                    -majorRadius * cosRadiansAround * radiansPerElementAround,
                    minorRadius * -sinRadiansAround * radiansPerElementAround,
                    0.0
                ]
                dx_ds2 = [
                    -0.5 * majorDiameter * sinRadiansAround * cosRadiansUp * radiansPerElementUpBody,
                    0.5 * minorDiameter * cosRadiansAround * cosRadiansUp * radiansPerElementUpBody,
                    height*sinRadiansUp * radiansPerElementUpBody
                ]
                listOuterBody_x.append(x)
                listOuterBody_d1.append(dx_ds1)
                listOuterBody_d2.append(dx_ds2)

        # create outer apex node
        outerApexNode_x = []
        outerApexNode_d1 = []
        outerApexNode_d2 = []
        x = [0.0, 0.0, height]
        dx_ds1 = [height*radiansPerElementUpBody/2, 0.0, 0.0]
        dx_ds2 = [0.0, height*radiansPerElementUpBody/2, 0.0]
        outerApexNode_x.append(x)
        outerApexNode_d1.append(dx_ds1)
        outerApexNode_d2.append(dx_ds2)

        # set nodes of outer layer of the bladder
        listTotalOuter_x = listOuterNeck_x + listOuterBody_x + outerApexNode_x
        listTotalOuter_d1 = listOuterNeck_d1 + listOuterBody_d1 + outerApexNode_d1
        listTotalOuter_d2 = listOuterNeck_d2 + listOuterBody_d2 + outerApexNode_d2

        outerLayer_x = []
        outerLayer_d1 = []
        outerLayer_d2 = []
        for n2 in range(len(listTotalOuter_x)):
            if (n2 != (ostiumElementPositionUp + 1) * elementsCountAround + ostiumElementPositionAround + 1) and\
                    (n2 != (ostiumElementPositionUp + 1) * elementsCountAround + elementsCountAround - ostiumElementPositionAround - 1):
                node = nodes.createNode(nodeIdentifier, nodetemplate)
                cache.setNode(node)
                coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_VALUE, 1, listTotalOuter_x[n2])
                coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS1, 1, listTotalOuter_d1[n2])
                coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS2, 1, listTotalOuter_d2[n2])
                nodeIdentifier += 1
                outerLayer_x.append(listTotalOuter_x[n2])
                outerLayer_d1.append(listTotalOuter_d1[n2])
                outerLayer_d2.append(listTotalOuter_d2[n2])

        # create and set nodes of inner layer of the bladder
        listTotalInner_x = []
        listTotalInner_d1 = []
        listTotalInner_d2 = []
        for n2 in range(elementsCountUpNeck + elementsCountUpBody):
            loop_x = [listTotalOuter_x[n2 * elementsCountAround + n1] for n1 in range(elementsCountAround)]
            loop_d1 = [listTotalOuter_d1[n2 * elementsCountAround + n1] for n1 in range(elementsCountAround)]
            loop_d2 = [listTotalOuter_d2[n2 * elementsCountAround + n1] for n1 in range(elementsCountAround)]
            for n1 in range(elementsCountAround):
                x, d1, _, _ = interp.projectHermiteCurvesThroughWall(loop_x, loop_d1, loop_d2, n1,
                                                                     -bladderWallThickness, loop=True)
                listTotalInner_x.append(x)
                listTotalInner_d1.append(d1)

        listInner_d2 = []
        for n2 in range(elementsCountAround):
            nx = [listTotalOuter_x[n1 * elementsCountAround + n2] for n1 in range(elementsCountUpNeck + elementsCountUpBody)]
            nd1 = [listTotalOuter_d1[n1 * elementsCountAround + n2] for n1 in range(elementsCountUpNeck + elementsCountUpBody)]
            nd2 = [listTotalOuter_d2[n1 * elementsCountAround + n2] for n1 in range(elementsCountUpNeck + elementsCountUpBody)]
            for n1 in range(elementsCountUpNeck + elementsCountUpBody):
                _, d2, _, _ = interp.projectHermiteCurvesThroughWall(nx, nd2, nd1, n1,
                                                                     bladderWallThickness, loop=False)
                listInner_d2.append(d2)

        # re-arrange the derivatives order
        for n2 in range(elementsCountUpNeck + elementsCountUpBody):
            for n1 in range(elementsCountAround):
                rearranged_d2 = listInner_d2[n1 * (elementsCountUpNeck + elementsCountUpBody) + n2]
                listTotalInner_d2.append(rearranged_d2)

        innerLayer_x = []
        innerLayer_d1 = []
        innerLayer_d2 = []
        for n2 in range(len(listTotalInner_x)):
            if (n2 != (ostiumElementPositionUp + 1) * elementsCountAround + ostiumElementPositionAround + 1) and \
                    (n2 != (ostiumElementPositionUp + 1) * elementsCountAround + elementsCountAround - ostiumElementPositionAround - 1):
                node = nodes.createNode(nodeIdentifier, nodetemplate)
                cache.setNode(node)
                coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_VALUE, 1, listTotalInner_x[n2])
                coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS1, 1, listTotalInner_d1[n2])
                coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS2, 1, listTotalInner_d2[n2])
                nodeIdentifier += 1

                innerLayer_x.append(listTotalInner_x[n2])
                innerLayer_d1.append(listTotalInner_d1[n2])
                innerLayer_d2.append(listTotalInner_d2[n2])

        # create inner apex node
        x = [0.0, 0.0, height - bladderWallThickness]
        dx_ds1 = [height*radiansPerElementUpBody/2, 0.0, 0.0]
        dx_ds2 = [0.0, height*radiansPerElementUpBody/2, 0.0]
        node = nodes.createNode(nodeIdentifier, nodetemplate)
        cache.setNode(node)
        coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_VALUE, 1, x)
        coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS1, 1, dx_ds1)
        coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS2, 1, dx_ds2)
        listTotalInner_x.append(x)
        listTotalInner_d1.append(dx_ds1)
        listTotalInner_d2.append(dx_ds2)
        innerLayer_x.append(x)
        innerLayer_d1.append(dx_ds1)
        innerLayer_d2.append(dx_ds2)
        nodeIdentifier += 1

        # create ureters on the surface
        elementIdentifier = 1
        # ureter 1
        centerUreter1_x, centerUreter1_d1, centerUreter1_d2 = tracksurfaceOstium1.evaluateCoordinates(ostium1Position, derivatives=True)
        td1, td2, td3 = calculate_surface_axes(centerUreter1_d1, centerUreter1_d2, [1.0, 0.0, 0.0])
        m1 = ostiumElementPositionUp * elementsCountAround + ostiumElementPositionAround
        ureter1StartCornerx = listOuterNeck_x[m1]
        v1 = [(ureter1StartCornerx[c] - centerUreter1_x[c]) for c in range(3)]
        ostium1Direction = vector.crossproduct3(td3, v1)
        nodeIdentifier, elementIdentifier, (o1_x, o1_d1, o1_d2, _, o1_NodeId, o1_Positions) = \
            generateOstiumMesh(region, ostiumDefaultOptions, tracksurfaceOstium1, ostium1Position, ostium1Direction,
            startNodeIdentifier=nodeIdentifier, startElementIdentifier=elementIdentifier)

        # ureter 2
        tracksurfaceOstium2 = tracksurfaceOstium1.createMirrorX()
        ostium2Position = TrackSurfacePosition(elementsCountAround - ostiumElementPositionAround, ostiumElementPositionUp - 1, 0.0, 1.0)
        centerUreter2_x, centerUreter2_d1, centerUreter2_d2 = tracksurfaceOstium2.evaluateCoordinates(ostium2Position, derivatives =True)
        ad1, ad2, ad3 = calculate_surface_axes(centerUreter2_d1, centerUreter2_d2, [1.0, 0.0, 0.0])
        if elementsCountAroundOstium == 4:
            m2 = ostiumElementPositionUp * elementsCountAround + elementsCountAround - ostiumElementPositionAround - 1
        else:
            m2 = ostiumElementPositionUp * elementsCountAround + elementsCountAround - ostiumElementPositionAround - 2
        ureter2StartCornerx = listOuterNeck_x[m2]
        v2 = [(ureter2StartCornerx[c] - centerUreter2_x[c]) for c in range(3)]
        ostium2Direction = vector.crossproduct3(ad3, v2)
        nodeIdentifier, elementIdentifier, (o2_x, o2_d1, o2_d2, _, o2_NodeId, o2_Positions) = \
            generateOstiumMesh(region, ostiumDefaultOptions, tracksurfaceOstium2, ostium2Position, ostium2Direction,
            startNodeIdentifier=nodeIdentifier, startElementIdentifier=elementIdentifier)

        # create annulus mesh around ostium
        endPoints1_x = [[None] * elementsCountAroundOstium, [None] * elementsCountAroundOstium]
        endPoints1_d1 = [[None] * elementsCountAroundOstium, [None] * elementsCountAroundOstium]
        endPoints1_d2 = [[None] * elementsCountAroundOstium, [None] * elementsCountAroundOstium]
        endNode1_Id = [[None] * elementsCountAroundOstium, [None] * elementsCountAroundOstium]
        endDerivativesMap = [[None] * elementsCountAroundOstium, [None] * elementsCountAroundOstium]
        endPoints2_x = [[None] * elementsCountAroundOstium, [None] * elementsCountAroundOstium]
        endPoints2_d1 = [[None] * elementsCountAroundOstium, [None] * elementsCountAroundOstium]
        endPoints2_d2 = [[None] * elementsCountAroundOstium, [None] * elementsCountAroundOstium]
        endNode2_Id = [[None] * elementsCountAroundOstium, [None] * elementsCountAroundOstium]

        nodeCountsEachWallLayer = (elementsCountUpNeck + elementsCountUpBody) * elementsCountAround - 1
        for n3 in range(2):
            n1 = 0
            endNode1_Id[n3][n1] = ((1 - n3) * nodeCountsEachWallLayer) + (ostiumElementPositionUp * elementsCountAround) + ostiumElementPositionAround + 1
            endNode1_Id[n3][n1 + 1] = endNode1_Id[n3][n1] + elementsCountAround
            endNode1_Id[n3][n1 + 2] = endNode1_Id[n3][n1 + 1] + elementsCountAround - 2
            endNode1_Id[n3][n1 + 3] = endNode1_Id[n3][n1 + 2] + 1
            endNode1_Id[n3][n1 + 4] = endNode1_Id[n3][n1 + 3] + 1
            endNode1_Id[n3][n1 + 5] = endNode1_Id[n3][n1 + 1] + 1
            endNode1_Id[n3][n1 + 6] = endNode1_Id[n3][n1] + 2
            endNode1_Id[n3][n1 + 7] = endNode1_Id[n3][n1] + 1
            if ostiumElementPositionAround == 0:
                endNode2_Id[n3][n1] = ((1 - n3) * nodeCountsEachWallLayer) + (ostiumElementPositionUp * elementsCountAround)\
                                        + elementsCountAround - ostiumElementPositionAround - 1
                endNode2_Id[n3][n1 + 1] = endNode2_Id[n3][n1] + elementsCountAround - 1
                endNode2_Id[n3][n1 + 2] = endNode2_Id[n3][n1 + 1] + elementsCountAround - 1
                endNode2_Id[n3][n1 + 3] = endNode2_Id[n3][n1 + 2] + 1
                endNode2_Id[n3][n1 + 4] = endNode2_Id[n3][n1 + 3] - elementsCountAround + 1
                endNode2_Id[n3][n1 + 5] = endNode2_Id[n3][n1 + 4] - elementsCountAround + 2
                endNode2_Id[n3][n1 + 6] = endNode2_Id[n3][n1 + 5] - elementsCountAround
                endNode2_Id[n3][n1 + 7] = endNode2_Id[n3][n1] + 1
            else:
                endNode2_Id[n3][n1] = ((1 - n3) * nodeCountsEachWallLayer) + (ostiumElementPositionUp * elementsCountAround)\
                                        + elementsCountAround - ostiumElementPositionAround - 1
                endNode2_Id[n3][n1 + 1] = endNode2_Id[n3][n1] + elementsCountAround - 1
                endNode2_Id[n3][n1 + 2] = endNode2_Id[n3][n1 + 1] + elementsCountAround - 1
                endNode2_Id[n3][n1 + 3] = endNode2_Id[n3][n1 + 2] + 1
                endNode2_Id[n3][n1 + 4] = endNode2_Id[n3][n1 + 3] + 1
                endNode2_Id[n3][n1 + 5] = endNode2_Id[n3][n1 + 1] + 1
                endNode2_Id[n3][n1 + 6] = endNode2_Id[n3][n1] + 2
                endNode2_Id[n3][n1 + 7] = endNode2_Id[n3][n1] + 1

        for n3 in range(2):
            for n1 in range(elementsCountAroundOstium):
                nc1 = endNode1_Id[n3][n1] - (1 - n3) * nodeCountsEachWallLayer - 1
                endPoints1_x[n3][n1] = innerLayer_x[nc1]
                endPoints1_d1[n3][n1] = innerLayer_d1[nc1]
                endPoints1_d2[n3][n1] = [innerLayer_d2[nc1][c] for c in range(3)]
                nc2 = endNode2_Id[n3][n1] - (1 - n3) * nodeCountsEachWallLayer - 1
                endPoints2_x[n3][n1] = innerLayer_x[nc2]
                endPoints2_d1[n3][n1] = innerLayer_d1[nc2]
                endPoints2_d2[n3][n1] = innerLayer_d2[nc2]

        for n1 in range(elementsCountAroundOstium):
            if n1 == 0:
                endDerivativesMap[0][n1] = ((-1, 0, 0), (-1, -1, 0), None, (0, 1, 0))
                endDerivativesMap[1][n1] = ((-1, 0, 0), (-1, -1, 0), None, (0, 1, 0))
            elif n1 == 1:
                endDerivativesMap[0][n1] = ((0, 1, 0), (-1, 0, 0), None)
                endDerivativesMap[1][n1] = ((0, 1, 0), (-1, 0, 0), None)
            elif n1 == 2:
                endDerivativesMap[0][n1] = ((0, 1, 0), (-1, 1, 0), None, (1, 0, 0))
                endDerivativesMap[1][n1] = ((0, 1, 0), (-1, 1, 0), None, (1, 0, 0))
            elif n1 == 3:
                endDerivativesMap[0][n1] = ((1, 0, 0), (0, 1, 0), None)
                endDerivativesMap[1][n1] = ((1, 0, 0), (0, 1, 0), None)
            elif n1 == 4:
                endDerivativesMap[0][n1] = ((1, 0, 0), (1, 1, 0), None, (0, -1, 0))
                endDerivativesMap[1][n1] = ((1, 0, 0), (1, 1, 0), None, (0, -1, 0))
            elif n1 == 5:
                endDerivativesMap[0][n1] = ((0, -1, 0), (1, 0, 0), None)
                endDerivativesMap[1][n1] = ((0, -1, 0), (1, 0, 0), None)
            elif n1 == 6:
                endDerivativesMap[0][n1] = ((0, -1, 0), (1, -1, 0), None, (-1, 0, 0))
                endDerivativesMap[1][n1] = ((0, -1, 0), (1, -1, 0), None, (-1, 0, 0))
            else:
                endDerivativesMap[0][n1] = ((-1, 0, 0), (0, -1, 0), None)
                endDerivativesMap[1][n1] = ((-1, 0, 0), (0, -1, 0), None)

        nodeIdentifier, elementIdentifier = createAnnulusMesh3d(
            nodes, mesh, nodeIdentifier, elementIdentifier,
            o1_x, o1_d1, o1_d2, None, o1_NodeId, None,
            endPoints1_x, endPoints1_d1, endPoints1_d2, None, endNode1_Id, endDerivativesMap,
            elementsCountRadial=elementsCountAnnulusRadially, meshGroups=[neckMeshGroup, urinaryBladderMeshGroup])

        nodeIdentifier, elementIdentifier = createAnnulusMesh3d(
            nodes, mesh, nodeIdentifier, elementIdentifier,
            o2_x, o2_d1, o2_d2, None, o2_NodeId, None,
            endPoints2_x, endPoints2_d1, endPoints2_d2, None, endNode2_Id, endDerivativesMap,
            elementsCountRadial=elementsCountAnnulusRadially, meshGroups=[neckMeshGroup, urinaryBladderMeshGroup])

        # create elements
        for e3 in range(1):
            newl = (e3 + 1) * ((elementsCountUpNeck + elementsCountUpBody) * elementsCountAround - 1)
            # create bladder neck elements
            for e2 in range(elementsCountUpNeck):
                for e1 in range(elementsCountAround):
                    if e2 == ostiumElementPositionUp:
                        if (e1 == ostiumElementPositionAround or e1 == ostiumElementPositionAround + 1):
                            pass
                        elif (e1 == elementsCountAround - ostiumElementPositionAround - 2 or e1 == elementsCountAround - 1 - ostiumElementPositionAround):
                            pass
                        else:
                            bni1 = e2 * elementsCountAround + e1 + 1
                            bni2 = e2 * elementsCountAround + (e1 + 1) % elementsCountAround + 1
                            if e1 < ostiumElementPositionAround:
                                bni3 = bni1 + elementsCountAround
                                bni4 = bni2 + elementsCountAround
                            elif (ostiumElementPositionAround + 1 < e1 < elementsCountAround - ostiumElementPositionAround - 2):
                                bni3 = bni1 + elementsCountAround - 1
                                bni4 = bni2 + elementsCountAround - 1
                            elif e1 > elementsCountAround - ostiumElementPositionAround - 1:
                                bni3 = bni1 + elementsCountAround - 2
                                if e1 == elementsCountAround - 1:
                                    bni4 = bni2 + elementsCountAround
                                else:
                                    bni4 = bni2 + elementsCountAround - 2
                            element = mesh.createElement(elementIdentifier, elementtemplate)
                            nodeIdentifiers = [bni1 + newl, bni2 + newl, bni3 + newl, bni4 + newl,
                                               bni1, bni2, bni3, bni4]
                            result = element.setNodesByIdentifier(eft, nodeIdentifiers)
                            neckMeshGroup.addElement(element)
                            urinaryBladderMeshGroup.addElement(element)
                            elementIdentifier += 1
                    elif e2 == ostiumElementPositionUp + 1:
                        if (e1 == ostiumElementPositionAround or e1 == ostiumElementPositionAround + 1):
                            pass
                        elif (e1 == elementsCountAround - ostiumElementPositionAround - 2 or e1 == elementsCountAround - 1 - ostiumElementPositionAround):
                            pass
                        else:
                            if e1 < ostiumElementPositionAround:
                                bni1 = e2 * elementsCountAround + e1 + 1
                                bni2 = e2 * elementsCountAround + (e1 + 1) % elementsCountAround + 1
                                bni3 = bni1 + elementsCountAround - 2
                                bni4 = bni2 + elementsCountAround - 2
                            elif (ostiumElementPositionAround + 1 < e1 < elementsCountAround - ostiumElementPositionAround - 2):
                                bni1 = e2 * elementsCountAround + e1
                                bni2 = e2 * elementsCountAround + (e1 + 1) % elementsCountAround
                                bni3 = bni1 + elementsCountAround - 1
                                bni4 = bni2 + elementsCountAround - 1
                            elif e1 > elementsCountAround - ostiumElementPositionAround - 1:
                                bni1 = e2 * elementsCountAround + e1 - 1
                                bni3 = bni1 + elementsCountAround
                                if e1 == elementsCountAround - 1:
                                    bni2 = e2 * elementsCountAround + (e1 + 1) % elementsCountAround + 1
                                    bni4 = bni2 + elementsCountAround - 2
                                else:
                                    bni2 = e2 * elementsCountAround + (e1 + 1) % elementsCountAround - 1
                                    bni4 = bni2 + elementsCountAround
                            element = mesh.createElement(elementIdentifier, elementtemplate)
                            nodeIdentifiers = [bni1 + newl, bni2 + newl, bni3 + newl, bni4 + newl,
                                               bni1, bni2, bni3, bni4]
                            result = element.setNodesByIdentifier(eft, nodeIdentifiers)
                            neckMeshGroup.addElement(element)
                            urinaryBladderMeshGroup.addElement(element)
                            elementIdentifier += 1
                    elif e2 > ostiumElementPositionUp + 1:
                        element = mesh.createElement(elementIdentifier, elementtemplate)
                        bni1 = e2 * elementsCountAround + e1 - 1
                        bni2 = e2 * elementsCountAround + (e1 + 1) % elementsCountAround - 1
                        bni3 = bni1 + elementsCountAround
                        bni4 = bni2 + elementsCountAround
                        nodeIdentifiers = [bni1 + newl, bni2 + newl, bni3 + newl, bni4 + newl,
                                           bni1, bni2, bni3, bni4]
                        result = element.setNodesByIdentifier(eft, nodeIdentifiers)
                        neckMeshGroup.addElement(element)
                        urinaryBladderMeshGroup.addElement(element)
                        elementIdentifier += 1
                    else:
                        element = mesh.createElement(elementIdentifier, elementtemplate)
                        bni1 = e2 * elementsCountAround + e1 + 1
                        bni2 = e2 * elementsCountAround + (e1 + 1) % elementsCountAround + 1
                        bni3 = bni1 + elementsCountAround
                        bni4 = bni2 + elementsCountAround
                        nodeIdentifiers = [bni1 + newl, bni2 + newl, bni3 + newl, bni4 + newl,
                                           bni1, bni2, bni3, bni4]
                        result = element.setNodesByIdentifier(eft, nodeIdentifiers)
                        neckMeshGroup.addElement(element)
                        urinaryBladderMeshGroup.addElement(element)
                        elementIdentifier += 1

            # create bladder body elements
            for e2 in range(elementsCountUpNeck, (elementsCountUpNeck + elementsCountUpBody - 1)):
                for e1 in range(elementsCountAround):
                    element = mesh.createElement(elementIdentifier, elementtemplate)
                    bni1 = e2 * elementsCountAround + e1 - 1
                    bni2 = e2 * elementsCountAround + (e1 + 1) % elementsCountAround - 1
                    bni3 = bni1 + elementsCountAround
                    bni4 = bni2 + elementsCountAround
                    nodeIdentifiers = [bni1 + newl, bni2 + newl, bni3 + newl, bni4 + newl,
                                       bni1, bni2, bni3, bni4]
                    result = element.setNodesByIdentifier(eft, nodeIdentifiers)
                    bodyMeshGroup.addElement(element)
                    urinaryBladderMeshGroup.addElement(element)
                    elementIdentifier += 1
            # create apex elements
            bni3 = (elementsCountUpNeck + elementsCountUpBody) * elementsCountAround - 1
            elementtemplateApex = mesh.createElementtemplate()
            elementtemplateApex.setElementShapeType(Element.SHAPE_TYPE_CUBE)
            for e1 in range(elementsCountAround):
                va = e1
                vb = (e1 + 1) % elementsCountAround
                eftApex = eftfactory.createEftShellPoleTop(va, vb)
                elementtemplateApex.defineField(coordinates, -1, eftApex)
                # redefine field in template for changes to eftApex:
                element = mesh.createElement(elementIdentifier, elementtemplateApex)
                bni1 = bni3 - elementsCountAround + e1
                bni2 = bni3 - elementsCountAround + (e1 + 1) % elementsCountAround
                nodeIdentifiers = [bni1 + newl, bni2 + newl, bni3 + newl, bni1, bni2, bni3]
                result = element.setNodesByIdentifier(eftApex, nodeIdentifiers)
                bodyMeshGroup.addElement(element)
                urinaryBladderMeshGroup.addElement(element)
                elementIdentifier += 1

        fm.endChange()
        return annotationGroups
Ejemplo n.º 5
0
    def generateBaseMesh(cls, region, options):
        """
        Generate the base tricubic Hermite mesh.
        :param region: Zinc region to define model in. Must be empty.
        :param options: Dict containing options. See getDefaultOptions().
        :return: list of AnnotationGroup
        """
        # set dependent outer diameter used in atria2
        options['Aorta outer plus diameter'] = options[
            'LV outlet inner diameter'] + 2.0 * options[
                'LV outlet wall thickness']
        elementsCountAroundAtrialSeptum = options[
            'Number of elements around atrial septum']
        elementsCountAroundLeftAtriumFreeWall = options[
            'Number of elements around left atrium free wall']
        elementsCountAroundLeftAtrium = elementsCountAroundLeftAtriumFreeWall + elementsCountAroundAtrialSeptum
        elementsCountAroundRightAtriumFreeWall = options[
            'Number of elements around right atrium free wall']
        elementsCountAroundRightAtrium = elementsCountAroundRightAtriumFreeWall + elementsCountAroundAtrialSeptum
        useCrossDerivatives = False

        fm = region.getFieldmodule()
        coordinates = findOrCreateFieldCoordinates(fm)
        cache = fm.createFieldcache()

        mesh = fm.findMeshByDimension(3)

        # generate heartventriclesbase1 model and put atria1 on it
        ventriclesAnnotationGroups = MeshType_3d_heartventriclesbase1.generateBaseMesh(
            region, options)
        atriaAnnotationGroups = MeshType_3d_heartatria1.generateBaseMesh(
            region, options)
        annotationGroups = mergeAnnotationGroups(ventriclesAnnotationGroups,
                                                 atriaAnnotationGroups)
        lFibrousRingGroup = findOrCreateAnnotationGroupForTerm(
            annotationGroups, region, get_heart_term("left fibrous ring"))
        rFibrousRingGroup = findOrCreateAnnotationGroupForTerm(
            annotationGroups, region, get_heart_term("right fibrous ring"))

        # annotation fiducial points
        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)

        ##############
        # Create nodes
        ##############

        nodeIdentifier = max(1, getMaximumNodeIdentifier(nodes) + 1)

        # discover left and right fibrous ring nodes from ventricles and atria
        # because nodes are iterated in identifier order, the lowest and first are on the lv outlet cfb, right and left on lower outer layers
        # left fibrous ring
        lavNodeId = [[[], []], [[], []]]  # [n3][n2][n1]
        iter = lFibrousRingGroup.getNodesetGroup(nodes).createNodeiterator()
        # left fibrous ring, bottom row
        cfbNodeId = iter.next().getIdentifier()
        cfbLeftNodeId = iter.next().getIdentifier()
        for n1 in range(elementsCountAroundLeftAtrium):
            lavNodeId[0][0].append(iter.next().getIdentifier())
        lavNodeId[1][0].append(cfbNodeId)
        lavNodeId[1][0].append(cfbLeftNodeId)
        for n1 in range(elementsCountAroundLeftAtriumFreeWall - 1):
            lavNodeId[1][0].append(iter.next().getIdentifier())
        for n1 in range(elementsCountAroundAtrialSeptum - 1):
            lavNodeId[1][0].append(None)  # no outer node on interatrial septum
        # left fibrous ring, top row
        for n1 in range(elementsCountAroundLeftAtrium):
            lavNodeId[0][1].append(iter.next().getIdentifier())
        for n1 in range(elementsCountAroundLeftAtriumFreeWall + 1):
            lavNodeId[1][1].append(iter.next().getIdentifier())
        for n1 in range(elementsCountAroundAtrialSeptum - 1):
            lavNodeId[1][1].append(None)  # no outer node on interatrial septum
        # right fibrous ring
        ravNodeId = [[[], []], [[], []]]  # [n3][n2][n1]
        iter = rFibrousRingGroup.getNodesetGroup(nodes).createNodeiterator()
        cfbNodeId = iter.next().getIdentifier()
        cfbRightNodeId = iter.next().getIdentifier()
        # right fibrous ring, bottom row
        for n1 in range(elementsCountAroundRightAtrium):
            ravNodeId[0][0].append(iter.next().getIdentifier())
        for n1 in range(elementsCountAroundRightAtriumFreeWall - 1):
            ravNodeId[1][0].append(iter.next().getIdentifier())
        ravNodeId[1][0].append(cfbRightNodeId)
        ravNodeId[1][0].append(cfbNodeId)
        for n1 in range(elementsCountAroundAtrialSeptum - 1):
            ravNodeId[1][0].append(None)  # no outer node on interatrial septum
        # right fibrous ring, top row
        for n1 in range(elementsCountAroundRightAtrium):
            ravNodeId[0][1].append(iter.next().getIdentifier())
        cfbUpperNodeId = iter.next().getIdentifier(
        )  # cfb from left will be first
        for n1 in range(elementsCountAroundRightAtriumFreeWall):
            ravNodeId[1][1].append(iter.next().getIdentifier())
        ravNodeId[1][1].append(cfbUpperNodeId)
        for n1 in range(elementsCountAroundAtrialSeptum - 1):
            ravNodeId[1][1].append(None)  # no outer node on interatrial septum

        #for n2 in range(2):
        #    print('n2', n2)
        #    print('lavNodeId[0]', lavNodeId[0][n2])
        #    print('lavNodeId[1]', lavNodeId[1][n2])
        #    print('ravNodeId[0]', ravNodeId[0][n2])
        #    print('ravNodeId[1]', ravNodeId[1][n2])

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

        lFibrousRingMeshGroup = lFibrousRingGroup.getMeshGroup(mesh)
        rFibrousRingMeshGroup = rFibrousRingGroup.getMeshGroup(mesh)

        elementIdentifier = getMaximumElementIdentifier(mesh) + 1

        elementtemplate1 = mesh.createElementtemplate()
        elementtemplate1.setElementShapeType(Element.SHAPE_TYPE_CUBE)

        # create fibrous ring elements

        bicubichermitelinear = eftfactory_bicubichermitelinear(
            mesh,
            useCrossDerivatives,
            linearAxis=2,
            d_ds1=Node.VALUE_LABEL_D_DS1,
            d_ds2=Node.VALUE_LABEL_D_DS3)
        eftFibrousRing = bicubichermitelinear.createEftBasic()

        # left fibrous ring, starting at crux / collapsed posterior interatrial sulcus
        cruxElementId = None
        for e in range(-1, elementsCountAroundLeftAtriumFreeWall):
            eft1 = eftFibrousRing
            n1 = e
            nids = [
                lavNodeId[0][0][n1], lavNodeId[0][0][n1 + 1],
                lavNodeId[0][1][n1], lavNodeId[0][1][n1 + 1],
                lavNodeId[1][0][n1], lavNodeId[1][0][n1 + 1],
                lavNodeId[1][1][n1], lavNodeId[1][1][n1 + 1]
            ]
            scalefactors = None
            meshGroups = [lFibrousRingMeshGroup]

            if e == -1:
                # interatrial groove straddles left and right atria, collapsed to 6 node wedge
                nids[0] = ravNodeId[0][0][
                    elementsCountAroundRightAtriumFreeWall]
                nids[2] = ravNodeId[0][1][
                    elementsCountAroundRightAtriumFreeWall]
                nids.pop(6)
                nids.pop(4)
                eft1 = bicubichermitelinear.createEftNoCrossDerivatives()
                setEftScaleFactorIds(eft1, [1], [])
                scalefactors = [-1.0]
                remapEftNodeValueLabel(eft1, [1, 3], Node.VALUE_LABEL_D_DS1,
                                       [(Node.VALUE_LABEL_D_DS1, []),
                                        (Node.VALUE_LABEL_D_DS3, [])])
                remapEftNodeValueLabel(eft1, [2, 4], Node.VALUE_LABEL_D_DS1,
                                       [(Node.VALUE_LABEL_D_DS1, []),
                                        (Node.VALUE_LABEL_D_DS3, [1])])
                remapEftNodeValueLabel(eft1, [5, 6, 7, 8],
                                       Node.VALUE_LABEL_D_DS1, [])
                # reverse d3 on cfb:
                remapEftNodeValueLabel(eft1, [5], Node.VALUE_LABEL_D_DS3,
                                       [(Node.VALUE_LABEL_D_DS1, [1]),
                                        (Node.VALUE_LABEL_D_DS3, [1])])
                remapEftNodeValueLabel(eft1, [6], Node.VALUE_LABEL_D_DS3,
                                       [(Node.VALUE_LABEL_D_DS1, [0]),
                                        (Node.VALUE_LABEL_D_DS3, [1])])
                remapEftNodeValueLabel(eft1, [7], Node.VALUE_LABEL_D_DS3,
                                       [(Node.VALUE_LABEL_D_DS1, []),
                                        (Node.VALUE_LABEL_D_DS3, [])])
                remapEftNodeValueLabel(eft1, [8], Node.VALUE_LABEL_D_DS3,
                                       [(Node.VALUE_LABEL_D_DS1, [1]),
                                        (Node.VALUE_LABEL_D_DS3, [])])
                ln_map = [1, 2, 3, 4, 5, 5, 6, 6]
                remapEftLocalNodes(eft1, 6, ln_map)
                meshGroups += [rFibrousRingMeshGroup]
            elif e == 0:
                # general linear map d3 adjacent to collapsed sulcus
                eft1 = bicubichermitelinear.createEftNoCrossDerivatives()
                setEftScaleFactorIds(eft1, [1], [])
                scalefactors = [-1.0]
                # reverse d1, d3 on cfb, left cfb:
                scaleEftNodeValueLabels(
                    eft1, [6],
                    [Node.VALUE_LABEL_D_DS1, Node.VALUE_LABEL_D_DS3], [1])
                remapEftNodeValueLabel(eft1, [5], Node.VALUE_LABEL_D_DS1,
                                       [(Node.VALUE_LABEL_D_DS1, [1])])
                remapEftNodeValueLabel(eft1, [5], Node.VALUE_LABEL_D_DS3,
                                       [(Node.VALUE_LABEL_D_DS1, []),
                                        (Node.VALUE_LABEL_D_DS3, [1])])
                remapEftNodeValueLabel(eft1, [7], Node.VALUE_LABEL_D_DS3,
                                       [(Node.VALUE_LABEL_D_DS1, [1]),
                                        (Node.VALUE_LABEL_D_DS3, [])])
            elif e == 1:
                # reverse d1, d3 on left cfb:
                eft1 = bicubichermitelinear.createEftNoCrossDerivatives()
                setEftScaleFactorIds(eft1, [1], [])
                scalefactors = [-1.0]
                remapEftNodeValueLabel(eft1, [5], Node.VALUE_LABEL_D_DS1,
                                       [(Node.VALUE_LABEL_D_DS1, [1]),
                                        (Node.VALUE_LABEL_D_DS3, [])])
                remapEftNodeValueLabel(eft1, [5], Node.VALUE_LABEL_D_DS3,
                                       [(Node.VALUE_LABEL_D_DS3, [1])])
            elif e == (elementsCountAroundLeftAtriumFreeWall - 1):
                # general linear map d3 adjacent to collapsed sulcus
                eft1 = bicubichermitelinear.createEftNoCrossDerivatives()
                setEftScaleFactorIds(eft1, [1], [])
                scalefactors = [-1.0]
                remapEftNodeValueLabel(eft1, [6, 8], Node.VALUE_LABEL_D_DS3,
                                       [(Node.VALUE_LABEL_D_DS1, []),
                                        (Node.VALUE_LABEL_D_DS3, [])])

            result = elementtemplate1.defineField(coordinates, -1, eft1)
            element = mesh.createElement(elementIdentifier, elementtemplate1)
            result2 = element.setNodesByIdentifier(eft1, nids)
            result3 = element.setScaleFactors(
                eft1, scalefactors) if scalefactors else None
            #print('create element fibrous ring left', elementIdentifier, result, result2, result3, nids)
            elementIdentifier += 1

            for meshGroup in meshGroups:
                meshGroup.addElement(element)

        # right fibrous ring, starting at crux / collapsed posterior interatrial sulcus
        for e in range(-1, elementsCountAroundRightAtriumFreeWall):
            eft1 = eftFibrousRing
            n1 = e
            nids = [
                ravNodeId[0][0][n1], ravNodeId[0][0][n1 + 1],
                ravNodeId[0][1][n1], ravNodeId[0][1][n1 + 1],
                ravNodeId[1][0][n1], ravNodeId[1][0][n1 + 1],
                ravNodeId[1][1][n1], ravNodeId[1][1][n1 + 1]
            ]
            scalefactors = None
            meshGroups = [rFibrousRingMeshGroup]

            if e == -1:
                # interatrial groove straddles left and right atria, collapsed to 6 node wedge
                nids[0] = lavNodeId[0][0][
                    elementsCountAroundLeftAtriumFreeWall]
                nids[2] = lavNodeId[0][1][
                    elementsCountAroundLeftAtriumFreeWall]
                nids.pop(6)
                nids.pop(4)
                eft1 = bicubichermitelinear.createEftNoCrossDerivatives()
                setEftScaleFactorIds(eft1, [1], [])
                scalefactors = [-1.0]
                remapEftNodeValueLabel(eft1, [1, 3], Node.VALUE_LABEL_D_DS1,
                                       [(Node.VALUE_LABEL_D_DS1, []),
                                        (Node.VALUE_LABEL_D_DS3, [])])
                remapEftNodeValueLabel(eft1, [2, 4], Node.VALUE_LABEL_D_DS1,
                                       [(Node.VALUE_LABEL_D_DS1, []),
                                        (Node.VALUE_LABEL_D_DS3, [1])])
                remapEftNodeValueLabel(eft1, [5, 6, 7, 8],
                                       Node.VALUE_LABEL_D_DS1, [])
                remapEftNodeValueLabel(eft1, [5, 7], Node.VALUE_LABEL_D_DS3,
                                       [(Node.VALUE_LABEL_D_DS1, []),
                                        (Node.VALUE_LABEL_D_DS3, [])])
                remapEftNodeValueLabel(eft1, [6, 8], Node.VALUE_LABEL_D_DS3,
                                       [(Node.VALUE_LABEL_D_DS1, [1]),
                                        (Node.VALUE_LABEL_D_DS3, [])])
                ln_map = [1, 2, 3, 4, 5, 5, 6, 6]
                remapEftLocalNodes(eft1, 6, ln_map)
                meshGroups += [lFibrousRingMeshGroup]
                cruxElementId = elementIdentifier
            elif e == 0:
                # general linear map d3 adjacent to collapsed crux/posterior sulcus
                eft1 = bicubichermitelinear.createEftNoCrossDerivatives()
                setEftScaleFactorIds(eft1, [1], [])
                scalefactors = [-1.0]
                remapEftNodeValueLabel(eft1, [5, 7], Node.VALUE_LABEL_D_DS3,
                                       [(Node.VALUE_LABEL_D_DS1, [1]),
                                        (Node.VALUE_LABEL_D_DS3, [])])
            elif e == (elementsCountAroundRightAtriumFreeWall - 2):
                # reverse d1, d3 on right cfb:
                eft1 = bicubichermitelinear.createEftNoCrossDerivatives()
                setEftScaleFactorIds(eft1, [1], [])
                scalefactors = [-1.0]
                remapEftNodeValueLabel(eft1, [6], Node.VALUE_LABEL_D_DS1,
                                       [(Node.VALUE_LABEL_D_DS1, [1]),
                                        (Node.VALUE_LABEL_D_DS3, [1])])
                remapEftNodeValueLabel(eft1, [6], Node.VALUE_LABEL_D_DS3,
                                       [(Node.VALUE_LABEL_D_DS3, [1])])
            elif e == (elementsCountAroundRightAtriumFreeWall - 1):
                # general linear map d3 adjacent to collapsed cfb/anterior sulcus
                eft1 = bicubichermitelinear.createEftNoCrossDerivatives()
                setEftScaleFactorIds(eft1, [1], [])
                scalefactors = [-1.0]
                # reverse d1, d3 on right cfb, cfb:
                scaleEftNodeValueLabels(
                    eft1, [5],
                    [Node.VALUE_LABEL_D_DS1, Node.VALUE_LABEL_D_DS3], [1])
                remapEftNodeValueLabel(eft1, [6], Node.VALUE_LABEL_D_DS1,
                                       [(Node.VALUE_LABEL_D_DS1, [1])])
                remapEftNodeValueLabel(eft1, [6], Node.VALUE_LABEL_D_DS3,
                                       [(Node.VALUE_LABEL_D_DS1, [1]),
                                        (Node.VALUE_LABEL_D_DS3, [1])])
                remapEftNodeValueLabel(eft1, [8], Node.VALUE_LABEL_D_DS3,
                                       [(Node.VALUE_LABEL_D_DS1, []),
                                        (Node.VALUE_LABEL_D_DS3, [])])

            result = elementtemplate1.defineField(coordinates, -1, eft1)
            element = mesh.createElement(elementIdentifier, elementtemplate1)
            result2 = element.setNodesByIdentifier(eft1, nids)
            result3 = element.setScaleFactors(
                eft1, scalefactors) if scalefactors else None
            #print('create element fibrous ring right', elementIdentifier, result, result2, result3, nids)
            elementIdentifier += 1

            for meshGroup in meshGroups:
                meshGroup.addElement(element)

        # fibrous ring septum:
        meshGroups = [lFibrousRingMeshGroup, rFibrousRingMeshGroup]
        for e in range(elementsCountAroundAtrialSeptum):
            eft1 = eftFibrousRing
            nlm = e - elementsCountAroundAtrialSeptum
            nlp = nlm + 1
            nrm = -e
            nrp = nrm - 1
            nids = [
                lavNodeId[0][0][nlm], lavNodeId[0][0][nlp],
                lavNodeId[0][1][nlm], lavNodeId[0][1][nlp],
                ravNodeId[0][0][nrm], ravNodeId[0][0][nrp],
                ravNodeId[0][1][nrm], ravNodeId[0][1][nrp]
            ]

            eft1 = bicubichermitelinear.createEftNoCrossDerivatives()
            setEftScaleFactorIds(eft1, [1], [])
            scalefactors = [-1.0]
            if e == 0:
                # general linear map d3 adjacent to collapsed posterior interventricular sulcus
                scaleEftNodeValueLabels(eft1, [5, 6, 7, 8],
                                        [Node.VALUE_LABEL_D_DS1], [1])
                scaleEftNodeValueLabels(eft1, [6, 8], [Node.VALUE_LABEL_D_DS3],
                                        [1])
                remapEftNodeValueLabel(eft1, [1, 3], Node.VALUE_LABEL_D_DS3,
                                       [(Node.VALUE_LABEL_D_DS1, []),
                                        (Node.VALUE_LABEL_D_DS3, [])])
                remapEftNodeValueLabel(eft1, [5, 7], Node.VALUE_LABEL_D_DS3,
                                       [(Node.VALUE_LABEL_D_DS1, []),
                                        (Node.VALUE_LABEL_D_DS3, [1])])
            elif e == (elementsCountAroundAtrialSeptum - 1):
                # general linear map d3 adjacent to cfb
                scaleEftNodeValueLabels(eft1, [5, 6, 7, 8],
                                        [Node.VALUE_LABEL_D_DS1], [1])
                scaleEftNodeValueLabels(eft1, [5, 7], [Node.VALUE_LABEL_D_DS3],
                                        [1])
                remapEftNodeValueLabel(eft1, [2, 4], Node.VALUE_LABEL_D_DS3,
                                       [(Node.VALUE_LABEL_D_DS1, [1]),
                                        (Node.VALUE_LABEL_D_DS3, [])])
                remapEftNodeValueLabel(eft1, [6, 8], Node.VALUE_LABEL_D_DS3,
                                       [(Node.VALUE_LABEL_D_DS1, [1]),
                                        (Node.VALUE_LABEL_D_DS3, [1])])
            else:
                scaleEftNodeValueLabels(
                    eft1, [5, 6, 7, 8],
                    [Node.VALUE_LABEL_D_DS1, Node.VALUE_LABEL_D_DS3], [1])

            result = elementtemplate1.defineField(coordinates, -1, eft1)
            element = mesh.createElement(elementIdentifier, elementtemplate1)
            result2 = element.setNodesByIdentifier(eft1, nids)
            result3 = element.setScaleFactors(
                eft1, scalefactors) if scalefactors else None
            #print('create element fibrous ring septum', elementIdentifier, result, result2, result3, nids)
            elementIdentifier += 1

            for meshGroup in meshGroups:
                meshGroup.addElement(element)

        # annotation fiducial points
        cruxElement = mesh.findElementByIdentifier(cruxElementId)
        cruxXi = [0.5, 0.5, 1.0]
        cache.setMeshLocation(cruxElement, cruxXi)
        result, cruxCoordinates = coordinates.evaluateReal(cache, 3)
        markerPoint = markerPoints.createNode(nodeIdentifier,
                                              markerTemplateInternal)
        nodeIdentifier += 1
        cache.setNode(markerPoint)
        markerName.assignString(cache, "crux of heart")
        markerLocation.assignMeshLocation(cache, cruxElement, cruxXi)

        return annotationGroups
    def generateBaseMesh(cls,
                         region,
                         options,
                         baseCentre=[0.0, 0.0, 0.0],
                         axisSide1=[0.0, -1.0, 0.0],
                         axisUp=[0.0, 0.0, 1.0]):
        """
        Generate the base bicubic-linear Hermite mesh. See also generateMesh().
        Optional extra parameters allow centre and axes to be set.
        :param region: Zinc region to define model in. Must be empty.
        :param options: Dict containing options. See getDefaultOptions().
        :param baseCentre: Centre of valve on ventriculo-arterial junction.
        :param axisSide: Unit vector in first side direction where angle around starts.
        :param axisUp: Unit vector in outflow direction of valve.
        :return: list of AnnotationGroup
         """
        unitScale = options['Unit scale']
        outerHeight = unitScale * options['Outer height']
        innerDepth = unitScale * options['Inner depth']
        cuspHeight = unitScale * options['Cusp height']
        innerRadius = unitScale * 0.5 * options['Inner diameter']
        sinusRadialDisplacement = unitScale * options[
            'Sinus radial displacement']
        wallThickness = unitScale * options['Wall thickness']
        cuspThickness = unitScale * options['Cusp thickness']
        aorticNotPulmonary = options['Aortic not pulmonary']
        useCrossDerivatives = False

        fm = region.getFieldmodule()
        fm.beginChange()
        coordinates = zinc_utils.getOrCreateCoordinateField(fm)
        cache = fm.createFieldcache()

        if aorticNotPulmonary:
            arterialRootGroup = AnnotationGroup(region,
                                                'root of aorta',
                                                FMANumber=3740,
                                                lyphID='Lyph ID unknown')
            cuspGroups = [
                AnnotationGroup(region,
                                'posterior cusp of aortic valve',
                                FMANumber=7253,
                                lyphID='Lyph ID unknown'),
                AnnotationGroup(region,
                                'right cusp of aortic valve',
                                FMANumber=7252,
                                lyphID='Lyph ID unknown'),
                AnnotationGroup(region,
                                'left cusp of aortic valve',
                                FMANumber=7251,
                                lyphID='Lyph ID unknown')
            ]
        else:
            arterialRootGroup = AnnotationGroup(region,
                                                'root of pulmonary trunk',
                                                FMANumber=8612,
                                                lyphID='Lyph ID unknown')
            cuspGroups = [
                AnnotationGroup(region,
                                'right cusp of pulmonary valve',
                                FMANumber=7250,
                                lyphID='Lyph ID unknown'),
                AnnotationGroup(region,
                                'anterior cusp of pulmonary valve',
                                FMANumber=7249,
                                lyphID='Lyph ID unknown'),
                AnnotationGroup(region,
                                'left cusp of pulmonary valve',
                                FMANumber=7247,
                                lyphID='Lyph ID unknown')
            ]

        allGroups = [arterialRootGroup
                     ]  # groups that all elements in scaffold will go in
        annotationGroups = allGroups + cuspGroups

        # annotation fiducial points
        fiducialGroup = zinc_utils.getOrCreateGroupField(fm, 'fiducial')
        fiducialCoordinates = zinc_utils.getOrCreateCoordinateField(
            fm, 'fiducial_coordinates')
        fiducialLabel = zinc_utils.getOrCreateLabelField(fm, 'fiducial_label')
        #fiducialElementXi = zinc_utils.getOrCreateElementXiField(fm, 'fiducial_element_xi')

        datapoints = fm.findNodesetByFieldDomainType(
            Field.DOMAIN_TYPE_DATAPOINTS)
        fiducialPoints = zinc_utils.getOrCreateNodesetGroup(
            fiducialGroup, datapoints)
        datapointTemplateExternal = datapoints.createNodetemplate()
        datapointTemplateExternal.defineField(fiducialCoordinates)
        datapointTemplateExternal.defineField(fiducialLabel)

        #################
        # Create nodes
        #################

        nodes = fm.findNodesetByFieldDomainType(Field.DOMAIN_TYPE_NODES)

        nodetemplate = nodes.createNodetemplate()
        nodetemplate.defineField(coordinates)
        nodetemplate.setValueNumberOfVersions(coordinates, -1,
                                              Node.VALUE_LABEL_VALUE, 1)
        nodetemplate.setValueNumberOfVersions(coordinates, -1,
                                              Node.VALUE_LABEL_D_DS1, 1)
        nodetemplate.setValueNumberOfVersions(coordinates, -1,
                                              Node.VALUE_LABEL_D_DS2, 1)
        nodetemplate.setValueNumberOfVersions(coordinates, -1,
                                              Node.VALUE_LABEL_D_DS3, 1)
        # most nodes in this scaffold do not have a DS3 derivative
        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)
        # several only have a DS1 derivative
        nodetemplateLinearS2S3 = nodes.createNodetemplate()
        nodetemplateLinearS2S3.defineField(coordinates)
        nodetemplateLinearS2S3.setValueNumberOfVersions(
            coordinates, -1, Node.VALUE_LABEL_VALUE, 1)
        nodetemplateLinearS2S3.setValueNumberOfVersions(
            coordinates, -1, Node.VALUE_LABEL_D_DS1, 1)

        nodeIdentifier = max(1, zinc_utils.getMaximumNodeIdentifier(nodes) + 1)

        elementsCountAround = 6
        radiansPerElementAround = 2.0 * math.pi / elementsCountAround
        axisSide2 = vector.crossproduct3(axisUp, axisSide1)
        outerRadius = innerRadius + wallThickness
        cuspOuterLength2 = 0.5 * getApproximateEllipsePerimeter(
            innerRadius, cuspHeight)
        cuspOuterWallArcLength = cuspOuterLength2 * innerRadius / (
            innerRadius + cuspHeight)
        noduleOuterAxialArcLength = cuspOuterLength2 - cuspOuterWallArcLength
        noduleOuterRadialArcLength = innerRadius
        cuspOuterWalld1 = interp.interpolateLagrangeHermiteDerivative(
            [innerRadius, outerHeight + innerDepth - cuspHeight], [0.0, 0.0],
            [-innerRadius, 0.0], 0.0)

        sin60 = math.sin(math.pi / 3.0)
        cuspThicknessLowerFactor = 4.5  # GRC fudge factor
        cuspInnerLength2 = 0.5 * getApproximateEllipsePerimeter(
            innerRadius - cuspThickness / sin60,
            cuspHeight - cuspThicknessLowerFactor * cuspThickness)

        noduleInnerAxialArcLength = cuspInnerLength2 * (
            cuspHeight - cuspThicknessLowerFactor * cuspThickness) / (
                innerRadius - cuspThickness / sin60 + cuspHeight -
                cuspThicknessLowerFactor * cuspThickness)
        noduleInnerRadialArcLength = innerRadius - cuspThickness / math.tan(
            math.pi / 3.0)
        nMidCusp = 0 if aorticNotPulmonary else 1

        # lower points
        ix, id1 = createCirclePoints(
            [(baseCentre[c] - axisUp[c] * innerDepth) for c in range(3)],
            [axisSide1[c] * innerRadius for c in range(3)],
            [axisSide2[c] * innerRadius
             for c in range(3)], elementsCountAround)
        ox, od1 = getSemilunarValveSinusPoints(baseCentre,
                                               axisSide1,
                                               axisSide2,
                                               outerRadius,
                                               sinusRadialDisplacement,
                                               startMidCusp=aorticNotPulmonary)
        lowerx, lowerd1 = [ix, ox], [id1, od1]

        # upper points
        topCentre = [(baseCentre[c] + axisUp[c] * outerHeight)
                     for c in range(3)]
        # twice as many on inner:
        ix, id1 = createCirclePoints(
            topCentre, [axisSide1[c] * innerRadius for c in range(3)],
            [axisSide2[c] * innerRadius
             for c in range(3)], elementsCountAround * 2)
        # tweak inner points so elements attached to cusps are narrower
        cuspRadiansFactor = 0.25  # GRC fudge factor
        midDerivativeFactor = 1.0 + 0.5 * (1.0 - cuspRadiansFactor
                                           )  # GRC test compromise
        cuspAttachmentRadians = cuspRadiansFactor * radiansPerElementAround
        cuspAttachmentRadialDisplacement = wallThickness * 0.333  # GRC fudge factor
        cuspAttachmentRadius = innerRadius - cuspAttachmentRadialDisplacement
        for cusp in range(3):
            n1 = cusp * 2 - 1 + nMidCusp
            n2 = n1 * 2
            id1[n2 + 2] = [2.0 * d for d in id1[n2 + 2]]
            # side 1
            radiansAround = n1 * radiansPerElementAround + cuspAttachmentRadians
            rcosRadiansAround = cuspAttachmentRadius * math.cos(radiansAround)
            rsinRadiansAround = cuspAttachmentRadius * math.sin(radiansAround)
            ix[n2 + 1] = [(topCentre[c] + rcosRadiansAround * axisSide1[c] +
                           rsinRadiansAround * axisSide2[c]) for c in range(3)]
            id1[n2 + 1] = interp.interpolateLagrangeHermiteDerivative(
                ix[n2 + 1], ix[n2 + 2], id1[n2 + 2], 0.0)
            # side 2
            n1 = ((cusp + 1) * 2 - 1 + nMidCusp) % elementsCountAround
            n2 = n1 * 2
            radiansAround = n1 * radiansPerElementAround - cuspAttachmentRadians
            rcosRadiansAround = cuspAttachmentRadius * math.cos(radiansAround)
            rsinRadiansAround = cuspAttachmentRadius * math.sin(radiansAround)
            ix[n2 - 1] = [(topCentre[c] + rcosRadiansAround * axisSide1[c] +
                           rsinRadiansAround * axisSide2[c]) for c in range(3)]
            id1[n2 - 1] = interp.interpolateHermiteLagrangeDerivative(
                ix[n2 - 2], id1[n2 - 2], ix[n2 - 1], 1.0)
        ox, od1 = createCirclePoints(
            topCentre, [axisSide1[c] * outerRadius for c in range(3)],
            [axisSide2[c] * outerRadius
             for c in range(3)], elementsCountAround)
        upperx, upperd1 = [ix, ox], [id1, od1]

        # get lower and upper derivative 2
        zero = [0.0, 0.0, 0.0]
        upperd2factor = outerHeight
        upd2 = [d * upperd2factor for d in axisUp]
        lowerOuterd2 = interp.smoothCubicHermiteDerivativesLine(
            [lowerx[1][nMidCusp], upperx[1][nMidCusp]], [upd2, upd2],
            fixStartDirection=True,
            fixEndDerivative=True)[0]
        lowerd2factor = 2.0 * (outerHeight + innerDepth) - upperd2factor
        lowerInnerd2 = [d * lowerd2factor for d in axisUp]
        lowerd2 = [[lowerInnerd2] * elementsCountAround,
                   [lowerOuterd2] * elementsCountAround
                   ]  # some lowerd2[0] to be fitted below
        upperd2 = [[upd2] * (elementsCountAround * 2),
                   [upd2] * elementsCountAround]

        # get lower and upper derivative 1 or 2 pointing to/from cusps
        for n1 in range(elementsCountAround):
            radiansAround = n1 * radiansPerElementAround
            cosRadiansAround = math.cos(radiansAround)
            sinRadiansAround = math.sin(radiansAround)
            if (n1 % 2) == nMidCusp:
                lowerd2[0][n1] = [
                    -cuspOuterWallArcLength *
                    (cosRadiansAround * axisSide1[c] +
                     sinRadiansAround * axisSide2[c]) for c in range(3)
                ]
            else:
                upperd1[0][n1 * 2] = [
                    (cuspOuterWalld1[0] * (cosRadiansAround * axisSide1[c] +
                                           sinRadiansAround * axisSide2[c]) +
                     cuspOuterWalld1[1] * axisUp[c]) for c in range(3)
                ]

        # inner wall and mid sinus points; only every second one is used
        sinusDepth = innerDepth - cuspThicknessLowerFactor * cuspThickness  # GRC test
        sinusCentre = [(baseCentre[c] - sinusDepth * axisUp[c])
                       for c in range(3)]
        sinusx, sinusd1 = createCirclePoints(
            sinusCentre, [axisSide1[c] * innerRadius for c in range(3)],
            [axisSide2[c] * innerRadius
             for c in range(3)], elementsCountAround)
        # get sinusd2, parallel to lower inclined lines
        sd2 = interp.smoothCubicHermiteDerivativesLine(
            [[innerRadius, -sinusDepth], [innerRadius, outerHeight]],
            [[wallThickness + sinusRadialDisplacement, innerDepth],
             [0.0, upperd2factor]],
            fixStartDirection=True,
            fixEndDerivative=True)[0]
        sinusd2 = [None] * elementsCountAround
        for cusp in range(3):
            n1 = cusp * 2 + nMidCusp
            radiansAround = n1 * radiansPerElementAround
            cosRadiansAround = math.cos(radiansAround)
            sinRadiansAround = math.sin(radiansAround)
            sinusd2[n1] = [(sd2[0] * (cosRadiansAround * axisSide1[c] +
                                      sinRadiansAround * axisSide2[c]) +
                            sd2[1] * axisUp[c]) for c in range(3)]

        # get points on arc between mid sinus and upper cusp points
        arcx = []
        arcd1 = []
        scaled1 = 2.5  # GRC fudge factor
        for cusp in range(3):
            n1 = cusp * 2 + nMidCusp
            n1m = n1 - 1
            n1p = (n1 + 1) % elementsCountAround
            n2m = n1m * 2 + 1
            n2p = n1p * 2 - 1
            ax, ad1 = interp.sampleCubicHermiteCurves(
                [upperx[0][n2m], sinusx[n1]],
                [[-scaled1 * d for d in upperd2[0][n2m]],
                 [scaled1 * d for d in sinusd1[n1]]],
                elementsCountOut=2,
                addLengthStart=0.5 * vector.magnitude(upperd2[0][n2m]),
                lengthFractionStart=0.5,
                addLengthEnd=0.5 * vector.magnitude(sinusd1[n1]),
                lengthFractionEnd=0.5,
                arcLengthDerivatives=False)[0:2]
            arcx.append(ax[1])
            arcd1.append(ad1[1])
            ax, ad1 = interp.sampleCubicHermiteCurves(
                [
                    sinusx[n1],
                    upperx[0][n2p],
                ], [[scaled1 * d for d in sinusd1[n1]],
                    [scaled1 * d for d in upperd2[0][n2p]]],
                elementsCountOut=2,
                addLengthStart=0.5 * vector.magnitude(sinusd1[n1]),
                lengthFractionStart=0.5,
                addLengthEnd=0.5 * vector.magnitude(upperd2[0][n2p]),
                lengthFractionEnd=0.5,
                arcLengthDerivatives=False)[0:2]
            arcx.append(ax[1])
            arcd1.append(ad1[1])
        if nMidCusp == 0:
            arcx.append(arcx.pop(0))
            arcd1.append(arcd1.pop(0))

        # cusp nodule points
        noduleCentre = [(baseCentre[c] + axisUp[c] * (cuspHeight - innerDepth))
                        for c in range(3)]
        nodulex = [[], []]
        noduled1 = [[], []]
        noduled2 = [[], []]
        noduled3 = [[], []]
        cuspRadialThickness = cuspThickness / sin60
        for i in range(3):
            nodulex[0].append(noduleCentre)
            n1 = i * 2 + nMidCusp
            radiansAround = n1 * radiansPerElementAround
            cosRadiansAround = math.cos(radiansAround)
            sinRadiansAround = math.sin(radiansAround)
            nodulex[1].append([(noduleCentre[c] + cuspRadialThickness *
                                (cosRadiansAround * axisSide1[c] +
                                 sinRadiansAround * axisSide2[c]))
                               for c in range(3)])
            n1 = i * 2 - 1 + nMidCusp
            radiansAround = n1 * radiansPerElementAround
            cosRadiansAround = math.cos(radiansAround)
            sinRadiansAround = math.sin(radiansAround)
            noduled1[0].append([
                noduleOuterRadialArcLength * (cosRadiansAround * axisSide1[c] +
                                              sinRadiansAround * axisSide2[c])
                for c in range(3)
            ])
            noduled1[1].append(
                vector.setMagnitude(noduled1[0][i],
                                    noduleInnerRadialArcLength))
            n1 = i * 2 + 1 + nMidCusp
            radiansAround = n1 * radiansPerElementAround
            cosRadiansAround = math.cos(radiansAround)
            sinRadiansAround = math.sin(radiansAround)
            noduled2[0].append([
                noduleOuterRadialArcLength * (cosRadiansAround * axisSide1[c] +
                                              sinRadiansAround * axisSide2[c])
                for c in range(3)
            ])
            noduled2[1].append(
                vector.setMagnitude(noduled2[0][i],
                                    noduleInnerRadialArcLength))
            noduled3[0].append(
                [noduleOuterAxialArcLength * axisUp[c] for c in range(3)])
            noduled3[1].append(
                [noduleInnerAxialArcLength * axisUp[c] for c in range(3)])

        # Create nodes

        lowerNodeId = [[], []]
        for n3 in range(2):
            for n1 in range(elementsCountAround):
                node = nodes.createNode(nodeIdentifier, nodetemplateLinearS3)
                cache.setNode(node)
                coordinates.setNodeParameters(cache, -1,
                                              Node.VALUE_LABEL_VALUE, 1,
                                              lowerx[n3][n1])
                coordinates.setNodeParameters(cache, -1,
                                              Node.VALUE_LABEL_D_DS1, 1,
                                              lowerd1[n3][n1])
                coordinates.setNodeParameters(cache, -1,
                                              Node.VALUE_LABEL_D_DS2, 1,
                                              lowerd2[n3][n1])
                lowerNodeId[n3].append(nodeIdentifier)
                nodeIdentifier += 1

        sinusNodeId = []
        for n1 in range(elementsCountAround):
            if (n1 % 2) != nMidCusp:
                sinusNodeId.append(None)
                continue
            node = nodes.createNode(nodeIdentifier, nodetemplateLinearS3)
            cache.setNode(node)
            coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_VALUE, 1,
                                          sinusx[n1])
            coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS1, 1,
                                          sinusd1[n1])
            coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS2, 1,
                                          sinusd2[n1])
            sinusNodeId.append(nodeIdentifier)
            nodeIdentifier += 1

        arcNodeId = []
        for n1 in range(elementsCountAround):
            node = nodes.createNode(nodeIdentifier, nodetemplateLinearS2S3)
            cache.setNode(node)
            coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_VALUE, 1,
                                          arcx[n1])
            coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS1, 1,
                                          arcd1[n1])
            arcNodeId.append(nodeIdentifier)
            nodeIdentifier += 1

        noduleNodeId = [[], []]
        for n3 in range(2):
            for n1 in range(3):
                node = nodes.createNode(nodeIdentifier, nodetemplate)
                cache.setNode(node)
                coordinates.setNodeParameters(cache, -1,
                                              Node.VALUE_LABEL_VALUE, 1,
                                              nodulex[n3][n1])
                coordinates.setNodeParameters(cache, -1,
                                              Node.VALUE_LABEL_D_DS1, 1,
                                              noduled1[n3][n1])
                coordinates.setNodeParameters(cache, -1,
                                              Node.VALUE_LABEL_D_DS2, 1,
                                              noduled2[n3][n1])
                coordinates.setNodeParameters(cache, -1,
                                              Node.VALUE_LABEL_D_DS3, 1,
                                              noduled3[n3][n1])
                noduleNodeId[n3].append(nodeIdentifier)
                nodeIdentifier += 1

        upperNodeId = [[], []]
        for n3 in range(2):
            for n1 in range(len(upperx[n3])):
                node = nodes.createNode(nodeIdentifier, nodetemplateLinearS3)
                cache.setNode(node)
                coordinates.setNodeParameters(cache, -1,
                                              Node.VALUE_LABEL_VALUE, 1,
                                              upperx[n3][n1])
                coordinates.setNodeParameters(cache, -1,
                                              Node.VALUE_LABEL_D_DS1, 1,
                                              upperd1[n3][n1])
                coordinates.setNodeParameters(cache, -1,
                                              Node.VALUE_LABEL_D_DS2, 1,
                                              upperd2[n3][n1])
                upperNodeId[n3].append(nodeIdentifier)
                nodeIdentifier += 1

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

        mesh = fm.findMeshByDimension(3)

        allMeshGroups = [allGroup.getMeshGroup(mesh) for allGroup in allGroups]
        cuspMeshGroups = [
            cuspGroup.getMeshGroup(mesh) for cuspGroup in cuspGroups
        ]

        linearHermiteLinearBasis = fm.createElementbasis(
            3, Elementbasis.FUNCTION_TYPE_LINEAR_LAGRANGE)
        linearHermiteLinearBasis.setFunctionType(
            2, Elementbasis.FUNCTION_TYPE_CUBIC_HERMITE)

        hermiteLinearLinearBasis = fm.createElementbasis(
            3, Elementbasis.FUNCTION_TYPE_LINEAR_LAGRANGE)
        hermiteLinearLinearBasis.setFunctionType(
            1, Elementbasis.FUNCTION_TYPE_CUBIC_HERMITE)

        bicubichermitelinear = eftfactory_bicubichermitelinear(
            mesh, useCrossDerivatives)
        eftDefault = bicubichermitelinear.createEftNoCrossDerivatives()

        elementIdentifier = max(
            1,
            zinc_utils.getMaximumElementIdentifier(mesh) + 1)

        elementtemplate1 = mesh.createElementtemplate()
        elementtemplate1.setElementShapeType(Element.SHAPE_TYPE_CUBE)

        # wall elements
        for cusp in range(3):
            n1 = cusp * 2 - 1 + nMidCusp
            n2 = n1 * 2
            for e in range(6):
                eft1 = None
                scalefactors = None

                if (e == 0) or (e == 5):
                    # 6 node linear-hermite-linear collapsed wedge element expanding from zero width on outer wall of root, attaching to vertical part of cusp
                    eft1 = mesh.createElementfieldtemplate(
                        linearHermiteLinearBasis)
                    # switch mappings to use DS2 instead of default DS1
                    remapEftNodeValueLabel(eft1, [1, 2, 3, 4, 5, 6, 7, 8],
                                           Node.VALUE_LABEL_D_DS1,
                                           [(Node.VALUE_LABEL_D_DS2, [])])
                    if e == 0:
                        nids = [
                            lowerNodeId[0][n1], arcNodeId[n1],
                            upperNodeId[0][n2], upperNodeId[0][n2 + 1],
                            lowerNodeId[1][n1], upperNodeId[1][n1]
                        ]
                        setEftScaleFactorIds(eft1, [1], [])
                        scalefactors = [-1.0]
                        remapEftNodeValueLabel(eft1, [2],
                                               Node.VALUE_LABEL_D_DS2,
                                               [(Node.VALUE_LABEL_D_DS1, [1])])
                    else:
                        nids = [
                            arcNodeId[n1 + 1], lowerNodeId[0][n1 - 4],
                            upperNodeId[0][n2 + 3], upperNodeId[0][n2 - 8],
                            lowerNodeId[1][n1 - 4], upperNodeId[1][n1 - 4]
                        ]
                        remapEftNodeValueLabel(eft1, [1],
                                               Node.VALUE_LABEL_D_DS2,
                                               [(Node.VALUE_LABEL_D_DS1, [])])
                    ln_map = [1, 2, 3, 4, 5, 5, 6, 6]
                    remapEftLocalNodes(eft1, 6, ln_map)
                elif (e == 1) or (e == 4):
                    # 6 node hermite-linear-linear collapsed wedge element on lower wall
                    eft1 = mesh.createElementfieldtemplate(
                        hermiteLinearLinearBasis)
                    if e == 1:
                        nids = [
                            lowerNodeId[0][n1], lowerNodeId[0][n1 + 1],
                            arcNodeId[n1], sinusNodeId[n1 + 1],
                            lowerNodeId[1][n1], lowerNodeId[1][n1 + 1]
                        ]
                    else:
                        nids = [
                            lowerNodeId[0][n1 + 1], lowerNodeId[0][n1 - 4],
                            sinusNodeId[n1 + 1], arcNodeId[n1 + 1],
                            lowerNodeId[1][n1 + 1], lowerNodeId[1][n1 - 4]
                        ]
                    ln_map = [1, 2, 3, 4, 5, 6, 5, 6]
                    remapEftLocalNodes(eft1, 6, ln_map)
                else:
                    # 8 node elements with wedges on two sides
                    if e == 2:
                        eft1 = bicubichermitelinear.createEftNoCrossDerivatives(
                        )
                        setEftScaleFactorIds(eft1, [1], [])
                        scalefactors = [-1.0]
                        nids = [
                            arcNodeId[n1], sinusNodeId[n1 + 1],
                            upperNodeId[0][n2 + 1], upperNodeId[0][n2 + 2],
                            lowerNodeId[1][n1], lowerNodeId[1][n1 + 1],
                            upperNodeId[1][n1], upperNodeId[1][n1 + 1]
                        ]
                        remapEftNodeValueLabel(eft1, [1],
                                               Node.VALUE_LABEL_D_DS2,
                                               [(Node.VALUE_LABEL_D_DS1, [1])])
                    else:
                        eft1 = eftDefault
                        nids = [
                            sinusNodeId[n1 + 1], arcNodeId[n1 + 1],
                            upperNodeId[0][n2 + 2], upperNodeId[0][n2 + 3],
                            lowerNodeId[1][n1 + 1], lowerNodeId[1][n1 - 4],
                            upperNodeId[1][n1 + 1], upperNodeId[1][n1 - 4]
                        ]
                        remapEftNodeValueLabel(eft1, [2],
                                               Node.VALUE_LABEL_D_DS2,
                                               [(Node.VALUE_LABEL_D_DS1, [])])

                result = elementtemplate1.defineField(coordinates, -1, eft1)
                element = mesh.createElement(elementIdentifier,
                                             elementtemplate1)
                result2 = element.setNodesByIdentifier(eft1, nids)
                if scalefactors:
                    result3 = element.setScaleFactors(eft1, scalefactors)
                else:
                    result3 = 7
                #print('create arterial root wall', cusp, e, 'element',elementIdentifier, result, result2, result3, nids)
                elementIdentifier += 1

                for meshGroup in allMeshGroups:
                    meshGroup.addElement(element)

        # cusps (leaflets)
        for cusp in range(3):
            n1 = cusp * 2 - 1 + nMidCusp
            n2 = n1 * 2
            meshGroups = allMeshGroups + [cuspMeshGroups[cusp]]
            for e in range(2):
                eft1 = bicubichermitelinear.createEftNoCrossDerivatives()
                setEftScaleFactorIds(eft1, [1], [])
                scalefactors = [-1.0]

                if e == 0:
                    nids = [
                        lowerNodeId[0][n1], lowerNodeId[0][n1 + 1],
                        upperNodeId[0][n2], noduleNodeId[0][cusp],
                        arcNodeId[n1], sinusNodeId[n1 + 1],
                        upperNodeId[0][n2 + 1], noduleNodeId[1][cusp]
                    ]
                    remapEftNodeValueLabel(eft1, [4, 8],
                                           Node.VALUE_LABEL_D_DS1,
                                           [(Node.VALUE_LABEL_D_DS1, [1])])
                    remapEftNodeValueLabel(eft1, [4, 8],
                                           Node.VALUE_LABEL_D_DS2,
                                           [(Node.VALUE_LABEL_D_DS3, [])])
                    remapEftNodeValueLabel(eft1, [5], Node.VALUE_LABEL_D_DS2,
                                           [(Node.VALUE_LABEL_D_DS1, [1])])
                    remapEftNodeValueLabel(eft1, [6], Node.VALUE_LABEL_D_DS2,
                                           [(Node.VALUE_LABEL_D_DS2, [1])])
                    remapEftNodeValueLabel(eft1, [7], Node.VALUE_LABEL_D_DS1,
                                           [(Node.VALUE_LABEL_D_DS1, [1])])
                else:
                    nids = [
                        lowerNodeId[0][n1 + 1], lowerNodeId[0][n1 - 4],
                        noduleNodeId[0][cusp], upperNodeId[0][n2 - 8],
                        sinusNodeId[n1 + 1], arcNodeId[n1 + 1],
                        noduleNodeId[1][cusp], upperNodeId[0][n2 + 3]
                    ]
                    remapEftNodeValueLabel(eft1, [3, 7],
                                           Node.VALUE_LABEL_D_DS2,
                                           [(Node.VALUE_LABEL_D_DS3, [])])
                    remapEftNodeValueLabel(eft1, [3, 7],
                                           Node.VALUE_LABEL_D_DS1,
                                           [(Node.VALUE_LABEL_D_DS2, [])])
                    remapEftNodeValueLabel(eft1, [4, 8],
                                           Node.VALUE_LABEL_D_DS1,
                                           [(Node.VALUE_LABEL_D_DS1, [1])])
                    remapEftNodeValueLabel(eft1, [5], Node.VALUE_LABEL_D_DS2,
                                           [(Node.VALUE_LABEL_D_DS2, [1])])
                    remapEftNodeValueLabel(eft1, [6], Node.VALUE_LABEL_D_DS2,
                                           [(Node.VALUE_LABEL_D_DS1, [])])

                result = elementtemplate1.defineField(coordinates, -1, eft1)
                element = mesh.createElement(elementIdentifier,
                                             elementtemplate1)
                result2 = element.setNodesByIdentifier(eft1, nids)
                if scalefactors:
                    result3 = element.setScaleFactors(eft1, scalefactors)
                else:
                    result3 = 7
                #print('create semilunar cusp', cusp, e, 'element',elementIdentifier, result, result2, result3, nids)
                elementIdentifier += 1

                for meshGroup in meshGroups:
                    meshGroup.addElement(element)

        # create annotation points

        datapoint = fiducialPoints.createNode(-1, datapointTemplateExternal)
        cache.setNode(datapoint)
        fiducialCoordinates.setNodeParameters(cache, -1,
                                              Node.VALUE_LABEL_VALUE, 1,
                                              noduleCentre)
        fiducialLabel.assignString(
            cache, 'aortic valve ctr'
            if aorticNotPulmonary else 'pulmonary valve ctr')

        fm.endChange()
        return annotationGroups
Ejemplo n.º 7
0
def createNodesAndElements(region, x, d1, d2, d3, xFlat, d1Flat, d2Flat,
                           xOrgan, d1Organ, d2Organ, organCoordinateFieldName,
                           elementsCountAround, elementsCountAlong,
                           elementsCountThroughWall, annotationGroupsAround,
                           annotationGroupsAlong, annotationGroupsThroughWall,
                           firstNodeIdentifier, firstElementIdentifier,
                           useCubicHermiteThroughWall, useCrossDerivatives,
                           closedProximalEnd):
    """
    Create nodes and elements for the coordinates and flat coordinates fields.
    :param x, d1, d2, d3: coordinates and derivatives of coordinates field.
    :param xFlat, d1Flat, d2Flat, d3Flat: coordinates and derivatives of
    flat coordinates field.
    :param xOrgan, d1Organ, d2Organ, d3Organ, organCoordinateFieldName: coordinates, derivatives and name of organ
    coordinates field.
    :param elementsCountAround: Number of elements around tube.
    :param elementsCountAlong: Number of elements along tube.
    :param elementsCountThroughWall: Number of elements through wall.
    :param annotationGroupsAround: Annotation groups of elements around.
    :param annotationGroupsAlong: Annotation groups of elements along.
    :param annotationGroupsThroughWall: Annotation groups of elements through wall.
    :param firstNodeIdentifier, firstElementIdentifier: first node and
    element identifier to use.
    :param useCubicHermiteThroughWall: use linear when false
    :param useCrossDerivatives: use cross derivatives when true
    :return nodeIdentifier, elementIdentifier, allAnnotationGroups
    """

    nodeIdentifier = firstNodeIdentifier
    elementIdentifier = firstElementIdentifier
    zero = [0.0, 0.0, 0.0]

    fm = region.getFieldmodule()
    fm.beginChange()
    cache = fm.createFieldcache()

    # Coordinates field
    coordinates = findOrCreateFieldCoordinates(fm)
    nodes = fm.findNodesetByFieldDomainType(Field.DOMAIN_TYPE_NODES)
    nodetemplate = nodes.createNodetemplate()
    nodetemplate.defineField(coordinates)
    nodetemplate.setValueNumberOfVersions(coordinates, -1,
                                          Node.VALUE_LABEL_VALUE, 1)
    nodetemplate.setValueNumberOfVersions(coordinates, -1,
                                          Node.VALUE_LABEL_D_DS1, 1)
    nodetemplate.setValueNumberOfVersions(coordinates, -1,
                                          Node.VALUE_LABEL_D_DS2, 1)
    if useCrossDerivatives:
        nodetemplate.setValueNumberOfVersions(coordinates, -1,
                                              Node.VALUE_LABEL_D2_DS1DS2, 1)
    if useCubicHermiteThroughWall:
        nodetemplate.setValueNumberOfVersions(coordinates, -1,
                                              Node.VALUE_LABEL_D_DS3, 1)
        if useCrossDerivatives:
            nodetemplate.setValueNumberOfVersions(coordinates, -1,
                                                  Node.VALUE_LABEL_D2_DS1DS3,
                                                  1)
            nodetemplate.setValueNumberOfVersions(coordinates, -1,
                                                  Node.VALUE_LABEL_D2_DS2DS3,
                                                  1)
            nodetemplate.setValueNumberOfVersions(
                coordinates, -1, Node.VALUE_LABEL_D3_DS1DS2DS3, 1)

    mesh = fm.findMeshByDimension(3)

    if useCubicHermiteThroughWall:
        eftfactory = eftfactory_tricubichermite(mesh, useCrossDerivatives)
    else:
        eftfactory = eftfactory_bicubichermitelinear(mesh, useCrossDerivatives)
    eft = eftfactory.createEftBasic()

    elementtemplate = mesh.createElementtemplate()
    elementtemplate.setElementShapeType(Element.SHAPE_TYPE_CUBE)
    result = elementtemplate.defineField(coordinates, -1, eft)

    if xFlat:
        # Flat coordinates field
        bicubichermitelinear = eftfactory_bicubichermitelinear(
            mesh, useCrossDerivatives)
        eftFlat1 = bicubichermitelinear.createEftBasic()
        eftFlat2 = bicubichermitelinear.createEftOpenTube()

        flatCoordinates = findOrCreateFieldCoordinates(fm,
                                                       name="flat coordinates")
        flatNodetemplate1 = nodes.createNodetemplate()
        flatNodetemplate1.defineField(flatCoordinates)
        flatNodetemplate1.setValueNumberOfVersions(flatCoordinates, -1,
                                                   Node.VALUE_LABEL_VALUE, 1)
        flatNodetemplate1.setValueNumberOfVersions(flatCoordinates, -1,
                                                   Node.VALUE_LABEL_D_DS1, 1)
        flatNodetemplate1.setValueNumberOfVersions(flatCoordinates, -1,
                                                   Node.VALUE_LABEL_D_DS2, 1)
        if useCrossDerivatives:
            flatNodetemplate1.setValueNumberOfVersions(
                flatCoordinates, -1, Node.VALUE_LABEL_D2_DS1DS2, 1)

        flatNodetemplate2 = nodes.createNodetemplate()
        flatNodetemplate2.defineField(flatCoordinates)
        flatNodetemplate2.setValueNumberOfVersions(flatCoordinates, -1,
                                                   Node.VALUE_LABEL_VALUE, 2)
        flatNodetemplate2.setValueNumberOfVersions(flatCoordinates, -1,
                                                   Node.VALUE_LABEL_D_DS1, 2)
        flatNodetemplate2.setValueNumberOfVersions(flatCoordinates, -1,
                                                   Node.VALUE_LABEL_D_DS2, 2)
        if useCrossDerivatives:
            flatNodetemplate2.setValueNumberOfVersions(
                flatCoordinates, -1, Node.VALUE_LABEL_D2_DS1DS2, 2)

        flatElementtemplate1 = mesh.createElementtemplate()
        flatElementtemplate1.setElementShapeType(Element.SHAPE_TYPE_CUBE)
        flatElementtemplate1.defineField(flatCoordinates, -1, eftFlat1)

        flatElementtemplate2 = mesh.createElementtemplate()
        flatElementtemplate2.setElementShapeType(Element.SHAPE_TYPE_CUBE)
        flatElementtemplate2.defineField(flatCoordinates, -1, eftFlat2)

    if xOrgan:
        # Organ coordinates field
        bicubichermitelinear = eftfactory_bicubichermitelinear(
            mesh, useCrossDerivatives)
        eftOrgan = bicubichermitelinear.createEftBasic()

        organCoordinates = findOrCreateFieldCoordinates(
            fm, name=organCoordinateFieldName)
        organNodetemplate = nodes.createNodetemplate()
        organNodetemplate.defineField(organCoordinates)
        organNodetemplate.setValueNumberOfVersions(organCoordinates, -1,
                                                   Node.VALUE_LABEL_VALUE, 1)
        organNodetemplate.setValueNumberOfVersions(organCoordinates, -1,
                                                   Node.VALUE_LABEL_D_DS1, 1)
        organNodetemplate.setValueNumberOfVersions(organCoordinates, -1,
                                                   Node.VALUE_LABEL_D_DS2, 1)
        if useCrossDerivatives:
            organNodetemplate.setValueNumberOfVersions(
                organCoordinates, -1, Node.VALUE_LABEL_D2_DS1DS2, 1)

        organElementtemplate = mesh.createElementtemplate()
        organElementtemplate.setElementShapeType(Element.SHAPE_TYPE_CUBE)
        organElementtemplate.defineField(organCoordinates, -1, eftOrgan)

    # Create nodes
    # Coordinates field
    for n in range(len(x)):
        node = nodes.createNode(nodeIdentifier, nodetemplate)
        cache.setNode(node)
        coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_VALUE, 1,
                                      x[n])
        coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS1, 1,
                                      d1[n])
        coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS2, 1,
                                      d2[n])
        coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS3, 1,
                                      d3[n])
        if useCrossDerivatives:
            coordinates.setNodeParameters(cache, -1,
                                          Node.VALUE_LABEL_D2_DS1DS2, 1, zero)
            coordinates.setNodeParameters(cache, -1,
                                          Node.VALUE_LABEL_D2_DS1DS3, 1, zero)
            coordinates.setNodeParameters(cache, -1,
                                          Node.VALUE_LABEL_D2_DS2DS3, 1, zero)
            coordinates.setNodeParameters(cache, -1,
                                          Node.VALUE_LABEL_D3_DS1DS2DS3, 1,
                                          zero)
        # print('NodeIdentifier = ', nodeIdentifier, x[n], d1[n], d2[n])
        nodeIdentifier = nodeIdentifier + 1

    # Flat coordinates field
    if xFlat:
        nodeIdentifier = firstNodeIdentifier
        for n2 in range(elementsCountAlong + 1):
            for n3 in range(elementsCountThroughWall + 1):
                for n1 in range(elementsCountAround):
                    i = n2 * (elementsCountAround +
                              1) * (elementsCountThroughWall +
                                    1) + (elementsCountAround + 1) * n3 + n1
                    node = nodes.findNodeByIdentifier(nodeIdentifier)
                    node.merge(flatNodetemplate2 if n1 ==
                               0 else flatNodetemplate1)
                    cache.setNode(node)
                    # print('NodeIdentifier', nodeIdentifier, 'version 1, xList Index =', i+1)
                    flatCoordinates.setNodeParameters(cache, -1,
                                                      Node.VALUE_LABEL_VALUE,
                                                      1, xFlat[i])
                    flatCoordinates.setNodeParameters(cache, -1,
                                                      Node.VALUE_LABEL_D_DS1,
                                                      1, d1Flat[i])
                    flatCoordinates.setNodeParameters(cache, -1,
                                                      Node.VALUE_LABEL_D_DS2,
                                                      1, d2Flat[i])
                    if useCrossDerivatives:
                        flatCoordinates.setNodeParameters(
                            cache, -1, Node.VALUE_LABEL_D2_DS1DS2, 1, zero)
                    if n1 == 0:
                        # print('NodeIdentifier', nodeIdentifier, 'version 2, xList Index =', i+elementsCountAround+1)
                        flatCoordinates.setNodeParameters(
                            cache, -1, Node.VALUE_LABEL_VALUE, 2,
                            xFlat[i + elementsCountAround])
                        flatCoordinates.setNodeParameters(
                            cache, -1, Node.VALUE_LABEL_D_DS1, 2,
                            d1Flat[i + elementsCountAround])
                        flatCoordinates.setNodeParameters(
                            cache, -1, Node.VALUE_LABEL_D_DS2, 2,
                            d2Flat[i + elementsCountAround])
                        if useCrossDerivatives:
                            flatCoordinates.setNodeParameters(
                                cache, -1, Node.VALUE_LABEL_D2_DS1DS2, 2, zero)
                    nodeIdentifier = nodeIdentifier + 1

    # Organ coordinates field
    if xOrgan:
        nodeIdentifier = firstNodeIdentifier
        for n in range(len(xOrgan)):
            node = nodes.findNodeByIdentifier(nodeIdentifier)
            node.merge(organNodetemplate)
            cache.setNode(node)
            organCoordinates.setNodeParameters(cache, -1,
                                               Node.VALUE_LABEL_VALUE, 1,
                                               xOrgan[n])
            organCoordinates.setNodeParameters(cache, -1,
                                               Node.VALUE_LABEL_D_DS1, 1,
                                               d1Organ[n])
            organCoordinates.setNodeParameters(cache, -1,
                                               Node.VALUE_LABEL_D_DS2, 1,
                                               d2Organ[n])
            if useCrossDerivatives:
                organCoordinates.setNodeParameters(cache, -1,
                                                   Node.VALUE_LABEL_D2_DS1DS2,
                                                   1, zero)
            nodeIdentifier = nodeIdentifier + 1

    # create elements
    elementtemplate3 = mesh.createElementtemplate()
    elementtemplate3.setElementShapeType(Element.SHAPE_TYPE_CUBE)
    radiansPerElementAround = math.pi * 2.0 / elementsCountAround

    allAnnotationGroups = []

    if closedProximalEnd:
        # Create apex
        for e3 in range(elementsCountThroughWall):
            for e1 in range(elementsCountAround):
                va = e1
                vb = (e1 + 1) % elementsCountAround
                eft1 = eftfactory.createEftShellPoleBottom(va * 100, vb * 100)
                elementtemplate3.defineField(coordinates, -1, eft1)
                element = mesh.createElement(elementIdentifier,
                                             elementtemplate3)
                bni1 = e3 + 1
                bni2 = elementsCountThroughWall + 1 + elementsCountAround * e3 + e1 + 1
                bni3 = elementsCountThroughWall + 1 + elementsCountAround * e3 + (
                    e1 + 1) % elementsCountAround + 1
                nodeIdentifiers = [
                    bni1, bni2, bni3, bni1 + 1, bni2 + elementsCountAround,
                    bni3 + elementsCountAround
                ]
                element.setNodesByIdentifier(eft1, nodeIdentifiers)
                # set general linear map coefficients
                radiansAround = e1 * radiansPerElementAround
                radiansAroundNext = (
                    (e1 + 1) % elementsCountAround) * radiansPerElementAround
                scalefactors = [
                    -1.0,
                    math.sin(radiansAround),
                    math.cos(radiansAround), radiansPerElementAround,
                    math.sin(radiansAroundNext),
                    math.cos(radiansAroundNext), radiansPerElementAround,
                    math.sin(radiansAround),
                    math.cos(radiansAround), radiansPerElementAround,
                    math.sin(radiansAroundNext),
                    math.cos(radiansAroundNext), radiansPerElementAround
                ]
                result = element.setScaleFactors(eft1, scalefactors)
                elementIdentifier = elementIdentifier + 1
                annotationGroups = annotationGroupsAround[e1] + annotationGroupsAlong[0] + \
                                   annotationGroupsThroughWall[e3]
                if annotationGroups:
                    allAnnotationGroups = mergeAnnotationGroups(
                        allAnnotationGroups, annotationGroups)
                    for annotationGroup in annotationGroups:
                        meshGroup = annotationGroup.getMeshGroup(mesh)
                        meshGroup.addElement(element)

    # Create regular elements
    now = elementsCountAround * (elementsCountThroughWall + 1)
    for e2 in range(1 if closedProximalEnd else 0, elementsCountAlong):
        for e3 in range(elementsCountThroughWall):
            for e1 in range(elementsCountAround):
                if closedProximalEnd:
                    bni11 = (e2 -
                             1) * now + e3 * elementsCountAround + e1 + 1 + (
                                 elementsCountThroughWall + 1)
                    bni12 = (e2-1) * now + e3 * elementsCountAround + (e1 + 1) % elementsCountAround + 1 + \
                            (elementsCountThroughWall + 1)
                    bni21 = (e2 - 1) * now + (
                        e3 + 1) * elementsCountAround + e1 + 1 + (
                            elementsCountThroughWall + 1)
                    bni22 = (e2-1) * now + (e3 + 1) * elementsCountAround + (e1 + 1) % elementsCountAround + 1 + \
                            (elementsCountThroughWall + 1)
                else:
                    bni11 = e2 * now + e3 * elementsCountAround + e1 + 1
                    bni12 = e2 * now + e3 * elementsCountAround + (
                        e1 + 1) % elementsCountAround + 1
                    bni21 = e2 * now + (e3 + 1) * elementsCountAround + e1 + 1
                    bni22 = e2 * now + (e3 + 1) * elementsCountAround + (
                        e1 + 1) % elementsCountAround + 1
                nodeIdentifiers = [
                    bni11, bni12, bni11 + now, bni12 + now, bni21, bni22,
                    bni21 + now, bni22 + now
                ]
                onOpening = e1 > elementsCountAround - 2
                element = mesh.createElement(elementIdentifier,
                                             elementtemplate)
                element.setNodesByIdentifier(eft, nodeIdentifiers)
                if xFlat:
                    element.merge(flatElementtemplate2
                                  if onOpening else flatElementtemplate1)
                    element.setNodesByIdentifier(
                        eftFlat2 if onOpening else eftFlat1, nodeIdentifiers)
                if xOrgan:
                    element.merge(organElementtemplate)
                    element.setNodesByIdentifier(eftOrgan, nodeIdentifiers)
                elementIdentifier = elementIdentifier + 1

                annotationGroups = annotationGroupsAround[e1] + annotationGroupsAlong[e2] + \
                                   annotationGroupsThroughWall[e3]
                if annotationGroups:
                    allAnnotationGroups = mergeAnnotationGroups(
                        allAnnotationGroups, annotationGroups)
                    for annotationGroup in annotationGroups:
                        meshGroup = annotationGroup.getMeshGroup(mesh)
                        meshGroup.addElement(element)

    fm.endChange()

    return nodeIdentifier, elementIdentifier, allAnnotationGroups
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 = []):
    '''
    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 nodetemplate: Full tricubic Hermite node template, can omit cross derivatives.
    :param forceStartLinearXi3, forceMidLinearXi3, forceEndLinearXi3: Force start, middle or
        end elements to be linear through the wall, even if d3 is supplied at either end.
        Can only use forceMidLinearXi3 only if at least one end is linear in d3.
    :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 list of Zinc MeshGroup for adding new elements to.
    :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'
    elementsCountWall = 1
    nodesCountWall = elementsCountWall + 1
    assert (len(startPointsx) == nodesCountWall) and (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'

    fm = mesh.getFieldmodule()
    fm.beginChange()
    cache = fm.createFieldcache()
    coordinates = zinc_utils.getOrCreateCoordinateField(fm)

    # Build arrays of points from start to end
    px  = [ [], [] ]
    pd1 = [ [], [] ]
    pd2 = [ [], [] ]
    pd3 = [ [], [] ]
    for n3 in range(2):
        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 ]
    if elementsCountRadial > 1:
        # add in-between points
        startPointsd = [ startPointsd1, startPointsd2, startPointsd3 ]
        startPointsdslimit = 2 if (startPointsd3 is None) else 3
        endPointsd = [ endPointsd1, endPointsd2, endPointsd3 ]
        endPointsdslimit = 2 if (endPointsd3 is None) else 3
        for n3 in range(2):
            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)
        # compute on outside / n3 = 1, then map to inside using thickness
        thicknesses = []
        thicknesses.append([ vector.magnitude([ (startPointsx[1][n1][c] - startPointsx[0][n1][c]) for c in range(3) ]) for n1 in range(nodesCountAround) ])
        if maxStartThickness:
            for n1 in range(nodesCountAround):
                thicknesses[0][n1] = min(thicknesses[0][n1], maxStartThickness)
        for n2 in range(1, elementsCountRadial):
            thicknesses.append([ None ]*nodesCountAround)
        thicknesses.append([ vector.magnitude([ (endPointsx[1][n1][c] - endPointsx[0][n1][c]) for c in range(3) ]) for n1 in range(nodesCountAround) ])
        if maxEndThickness:
            for n1 in range(nodesCountAround):
                thicknesses[-1][n1] = min(thicknesses[-1][n1], maxEndThickness)
        n3 == 1
        for n1 in range(nodesCountAround):
            ax  = startPointsx [n3][n1]
            if (startDerivativesMap is None) or (startDerivativesMap[n3][n1][0] is None):
                ad1 = startPointsd1[n3][n1]
            else:
                derivativesMap = startDerivativesMap[n3][n1][0]
                ad1 = [ 0.0, 0.0, 0.0 ]
                for ds in range(startPointsdslimit):
                    if derivativesMap[ds] != 0.0:
                        for c in range(3):
                            ad1[c] += derivativesMap[ds]*startPointsd[ds][n3][n1][c]
                if len(startDerivativesMap[n3][n1]) > 3:
                    # average with d1 map for other side
                    derivativesMap = startDerivativesMap[n3][n1][3]
                    ad1 = [ 0.5*d for d in ad1 ]
                    if not derivativesMap:
                        for c in range(3):
                            ad1[c] += 0.5*startPointsd[0][n3][n1][c]
                    else:
                        for ds in range(startPointsdslimit):
                            if derivativesMap[ds] != 0.0:
                                for c in range(3):
                                    ad1[c] += 0.5*derivativesMap[ds]*startPointsd[ds][n3][n1][c]
            if (startDerivativesMap is None) or (startDerivativesMap[n3][n1][1] is None):
                ad2 = startPointsd2[n3][n1]
            else:
                derivativesMap = startDerivativesMap[n3][n1][1]
                ad2 = [ 0.0, 0.0, 0.0 ]
                for ds in range(startPointsdslimit):
                    if derivativesMap[ds] != 0.0:
                        for c in range(3):
                            ad2[c] += derivativesMap[ds]*startPointsd[ds][n3][n1][c]

            bx  = endPointsx [n3][n1]
            if (endDerivativesMap is None) or (endDerivativesMap[n3][n1][0] is None):
                bd1 = endPointsd1[n3][n1]
            else:
                derivativesMap = endDerivativesMap[n3][n1][0]
                bd1 = [ 0.0, 0.0, 0.0 ]
                for ds in range(endPointsdslimit):
                    if derivativesMap[ds] != 0.0:
                        for c in range(3):
                            bd1[c] += derivativesMap[ds]*endPointsd[ds][n3][n1][c]
                if len(endDerivativesMap[n3][n1]) > 3:
                    # average with d1 map for other side
                    derivativesMap = endDerivativesMap[n3][n1][3]
                    bd1 = [ 0.5*d for d in bd1 ]
                    if not derivativesMap:
                        for c in range(3):
                            bd1[c] += 0.5*endPointsd[0][n3][n1][c]
                    else:
                        for ds in range(endPointsdslimit):
                            if derivativesMap[ds] != 0.0:
                                for c in range(3):
                                    bd1[c] += 0.5*derivativesMap[ds]*endPointsd[ds][n3][n1][c]
            if (endDerivativesMap is None) or (endDerivativesMap[n3][n1][1] is None):
                bd2 = endPointsd2[n3][n1]
            else:
                derivativesMap = endDerivativesMap[n3][n1][1]
                bd2 = [ 0.0, 0.0, 0.0 ]
                for ds in range(endPointsdslimit):
                    if derivativesMap[ds] != 0.0:
                        for c in range(3):
                            bd2[c] += derivativesMap[ds]*endPointsd[ds][n3][n1][c]

            # scaling end derivatives to arc length gives even curvature along the curve
            arcLength = interp.computeCubicHermiteArcLength(ax, ad2, bx, bd2, rescaleDerivatives = False)
            scaledDerivatives = [ vector.setMagnitude(d2, arcLength) for d2 in [ ad2, bd2 ]]
            mx, md2, me, mxi = interp.sampleCubicHermiteCurvesSmooth([ ax, bx ], scaledDerivatives, elementsCountRadial,
                derivativeMagnitudeStart = vector.magnitude(ad2),
                derivativeMagnitudeEnd = vector.magnitude(bd2))[0:4]
            md1 = interp.interpolateSampleLinear([ ad1, bd1 ], me, mxi)
            thi = interp.interpolateSampleLinear([ thicknesses[0][n1], thicknesses[-1][n1] ], me, mxi)
            #md2 = interp.smoothCubicHermiteDerivativesLine(mx, md2, fixStartDerivative = True, fixEndDerivative = True)
            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]

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

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

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

        for n3 in range(0, 1):  # was (0, nodesCountWall)
            # smooth derivative 2 radially/along annulus
            for n1 in range(nodesCountAround):
                sd2 = interp.smoothCubicHermiteDerivativesLine(
                    [ px [n3][n2][n1] for n2 in range(elementsCountRadial + 1) ],
                    [ pd2[n3][n2][n1] for n2 in range(elementsCountRadial + 1) ],
                    fixAllDirections = True, fixStartDerivative = True, fixEndDerivative = True,
                    magnitudeScalingMode = interp.DerivativeScalingMode.HARMONIC_MEAN)
                for n2 in range(elementsCountRadial + 1):
                    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(2):
        for n2 in range(elementsCountRadial + 1):
            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)

    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 is not None)
        mapStartLinearDerivativeXi3 = nonlinearXi3 and rowLinearXi3[e2]
        mapEndDerivatives = (e2 == (elementsCountRadial - 1)) and (endDerivativesMap is not None)
        mapEndLinearDerivativeXi3 = nonlinearXi3 and rowLinearXi3[e2 + 1]
        mapDerivatives = mapStartDerivatives or mapStartLinearDerivativeXi3 or mapEndDerivatives or mapEndLinearDerivativeXi3
        for e1 in range(elementsCountAround):
            en = (e1 + 1)%elementsCountAround
            nids = [ nodeId[0][e2][e1], nodeId[0][e2][en], nodeId[0][e2 + 1][e1], nodeId[0][e2 + 1][en],
                     nodeId[1][e2][e1], nodeId[1][e2][en], nodeId[1][e2 + 1][e1], nodeId[1][e2 + 1][en] ]

            if mapDerivatives:
                eft1 = eftFactory.createEftNoCrossDerivatives()
                setEftScaleFactorIds(eft1, [1], [])
                if mapStartLinearDerivativeXi3:
                    eftFactory.setEftLinearDerivative(eft1, [ 1, 5 ], Node.VALUE_LABEL_D_DS3, 1, 5, 1)
                    eftFactory.setEftLinearDerivative(eft1, [ 2, 6 ], Node.VALUE_LABEL_D_DS3, 2, 6, 1)
                if mapStartDerivatives:
                    for i in range(2):
                        lns = [ 1, 5 ] if (i == 0) else [ 2, 6 ]
                        for n3 in range(2):
                            derivativesMap = startDerivativesMap[n3][e1] if (i == 0) else startDerivativesMap[n3][en]
                            # handle different d1 on each side of node
                            d1Map = derivativesMap[0] if ((i == 1) or (len(derivativesMap) < 4)) else derivativesMap[3]
                            d2Map = derivativesMap[1]
                            d3Map = derivativesMap[2]
                            # use temporary to safely swap DS1 and DS2:
                            ln = [ lns[n3] ]
                            if d1Map is not None:
                                remapEftNodeValueLabel(eft1, ln, Node.VALUE_LABEL_D_DS1, [ ( Node.VALUE_LABEL_D2_DS1DS2, [] ) ])
                            if d3Map is not None:
                                remapEftNodeValueLabel(eft1, ln, Node.VALUE_LABEL_D_DS3, [ ( Node.VALUE_LABEL_D2_DS2DS3, [] ) ])
                            if d2Map is not None:
                                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))
                            if d1Map is not None:
                                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 is not None:
                                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.setEftLinearDerivative(eft1, [ 3, 7 ], Node.VALUE_LABEL_D_DS3, 3, 7, 1)
                    eftFactory.setEftLinearDerivative(eft1, [ 4, 8 ], Node.VALUE_LABEL_D_DS3, 4, 8, 1)
                if mapEndDerivatives:
                    for i in range(2):
                        lns = [ 3, 7 ] if (i == 0) else [ 4, 8 ]
                        for n3 in range(2):
                            derivativesMap = endDerivativesMap[n3][e1] if (i == 0) else endDerivativesMap[n3][en]
                            # handle different d1 on each side of node
                            d1Map = derivativesMap[0] if ((i == 1) or (len(derivativesMap) < 4)) else derivativesMap[3]
                            d2Map = derivativesMap[1]
                            d3Map = derivativesMap[2]
                            # use temporary to safely swap DS1 and DS2:
                            ln = [ lns[n3] ]
                            if d1Map is not None:
                                remapEftNodeValueLabel(eft1, ln, Node.VALUE_LABEL_D_DS1, [ ( Node.VALUE_LABEL_D2_DS1DS2, [] ) ])
                            if d3Map is not None:
                                remapEftNodeValueLabel(eft1, ln, Node.VALUE_LABEL_D_DS3, [ ( Node.VALUE_LABEL_D2_DS2DS3, [] ) ])
                            if d2Map is not None:
                                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))
                            if d1Map is not None:
                                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 is not None:
                                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 mapDerivatives:
                result3 = element.setScaleFactors(eft1, [ -1.0 ])
            #else:
            #    result3 = '-'
            #print('create element annulus', element.isValid(), elementIdentifier, result2, result3, nids)
            elementIdentifier += 1

            for meshGroup in meshGroups:
                meshGroup.addElement(element)

    fm.endChange()

    return nodeIdentifier, elementIdentifier
Ejemplo n.º 9
0
def createNodesAndElements(region, x, d1, d2, d3, xFlat, d1Flat, d2Flat,
                           xTexture, d1Texture, d2Texture, elementsCountAround,
                           elementsCountAlong, elementsCountThroughWall,
                           annotationGroups, annotationArray,
                           firstNodeIdentifier, firstElementIdentifier,
                           useCubicHermiteThroughWall, useCrossDerivatives):
    """
    Create nodes and elements for the coordinates, flat coordinates,
    and texture coordinates field.
    :param x, d1, d2, d3: coordinates and derivatives of coordinates field.
    :param xFlat, d1Flat, d2Flat, d3Flat: coordinates and derivatives of
    flat coordinates field.
    :param xTexture, d1Texture, d2Texture, d3Texture: coordinates and derivatives
    of texture coordinates field.
    :param elementsCountAround: Number of elements around tube.
    :param elementsCountAlong: Number of elements along tube.
    :param elementsCountThroughWall: Number of elements through wall.
    :param annotationGroups: stores information about annotation groups.
    :param annotationArray: stores annotation names of elements around.
    :param firstNodeIdentifier, firstElementIdentifier: first node and
    element identifier to use.
    :param useCubicHermiteThroughWall: use linear when false
    :param useCrossDerivatives: use cross derivatives when true
    :return nodeIdentifier, elementIdentifier, annotationGroups
    """

    nodeIdentifier = firstNodeIdentifier
    elementIdentifier = firstElementIdentifier
    zero = [0.0, 0.0, 0.0]

    fm = region.getFieldmodule()
    fm.beginChange()
    cache = fm.createFieldcache()

    # Coordinates field
    coordinates = findOrCreateFieldCoordinates(fm)
    nodes = fm.findNodesetByFieldDomainType(Field.DOMAIN_TYPE_NODES)
    nodetemplate = nodes.createNodetemplate()
    nodetemplate.defineField(coordinates)
    nodetemplate.setValueNumberOfVersions(coordinates, -1,
                                          Node.VALUE_LABEL_VALUE, 1)
    nodetemplate.setValueNumberOfVersions(coordinates, -1,
                                          Node.VALUE_LABEL_D_DS1, 1)
    nodetemplate.setValueNumberOfVersions(coordinates, -1,
                                          Node.VALUE_LABEL_D_DS2, 1)
    if useCrossDerivatives:
        nodetemplate.setValueNumberOfVersions(coordinates, -1,
                                              Node.VALUE_LABEL_D2_DS1DS2, 1)
    if useCubicHermiteThroughWall:
        nodetemplate.setValueNumberOfVersions(coordinates, -1,
                                              Node.VALUE_LABEL_D_DS3, 1)
        if useCrossDerivatives:
            nodetemplate.setValueNumberOfVersions(coordinates, -1,
                                                  Node.VALUE_LABEL_D2_DS1DS3,
                                                  1)
            nodetemplate.setValueNumberOfVersions(coordinates, -1,
                                                  Node.VALUE_LABEL_D2_DS2DS3,
                                                  1)
            nodetemplate.setValueNumberOfVersions(
                coordinates, -1, Node.VALUE_LABEL_D3_DS1DS2DS3, 1)

    mesh = fm.findMeshByDimension(3)

    if useCubicHermiteThroughWall:
        eftfactory = eftfactory_tricubichermite(mesh, useCrossDerivatives)
    else:
        eftfactory = eftfactory_bicubichermitelinear(mesh, useCrossDerivatives)
    eft = eftfactory.createEftBasic()

    elementtemplate = mesh.createElementtemplate()
    elementtemplate.setElementShapeType(Element.SHAPE_TYPE_CUBE)
    result = elementtemplate.defineField(coordinates, -1, eft)

    # Flat coordinates field
    bicubichermitelinear = eftfactory_bicubichermitelinear(
        mesh, useCrossDerivatives)
    eftTexture1 = bicubichermitelinear.createEftBasic()
    eftTexture2 = bicubichermitelinear.createEftOpenTube()

    flatCoordinates = findOrCreateFieldCoordinates(fm, name="flat coordinates")
    flatNodetemplate1 = nodes.createNodetemplate()
    flatNodetemplate1.defineField(flatCoordinates)
    flatNodetemplate1.setValueNumberOfVersions(flatCoordinates, -1,
                                               Node.VALUE_LABEL_VALUE, 1)
    flatNodetemplate1.setValueNumberOfVersions(flatCoordinates, -1,
                                               Node.VALUE_LABEL_D_DS1, 1)
    flatNodetemplate1.setValueNumberOfVersions(flatCoordinates, -1,
                                               Node.VALUE_LABEL_D_DS2, 1)
    if useCrossDerivatives:
        flatNodetemplate1.setValueNumberOfVersions(flatCoordinates, -1,
                                                   Node.VALUE_LABEL_D2_DS1DS2,
                                                   1)

    flatNodetemplate2 = nodes.createNodetemplate()
    flatNodetemplate2.defineField(flatCoordinates)
    flatNodetemplate2.setValueNumberOfVersions(flatCoordinates, -1,
                                               Node.VALUE_LABEL_VALUE, 2)
    flatNodetemplate2.setValueNumberOfVersions(flatCoordinates, -1,
                                               Node.VALUE_LABEL_D_DS1, 2)
    flatNodetemplate2.setValueNumberOfVersions(flatCoordinates, -1,
                                               Node.VALUE_LABEL_D_DS2, 2)
    if useCrossDerivatives:
        flatNodetemplate2.setValueNumberOfVersions(flatCoordinates, -1,
                                                   Node.VALUE_LABEL_D2_DS1DS2,
                                                   2)

    flatElementtemplate1 = mesh.createElementtemplate()
    flatElementtemplate1.setElementShapeType(Element.SHAPE_TYPE_CUBE)
    flatElementtemplate1.defineField(flatCoordinates, -1, eftTexture1)

    flatElementtemplate2 = mesh.createElementtemplate()
    flatElementtemplate2.setElementShapeType(Element.SHAPE_TYPE_CUBE)
    flatElementtemplate2.defineField(flatCoordinates, -1, eftTexture2)

    # Texture coordinates field
    textureCoordinates = findOrCreateFieldTextureCoordinates(fm)
    textureNodetemplate1 = nodes.createNodetemplate()
    textureNodetemplate1.defineField(textureCoordinates)
    textureNodetemplate1.setValueNumberOfVersions(textureCoordinates, -1,
                                                  Node.VALUE_LABEL_VALUE, 1)
    textureNodetemplate1.setValueNumberOfVersions(textureCoordinates, -1,
                                                  Node.VALUE_LABEL_D_DS1, 1)
    textureNodetemplate1.setValueNumberOfVersions(textureCoordinates, -1,
                                                  Node.VALUE_LABEL_D_DS2, 1)
    if useCrossDerivatives:
        textureNodetemplate1.setValueNumberOfVersions(
            textureCoordinates, -1, Node.VALUE_LABEL_D2_DS1DS2, 1)

    textureNodetemplate2 = nodes.createNodetemplate()
    textureNodetemplate2.defineField(textureCoordinates)
    textureNodetemplate2.setValueNumberOfVersions(textureCoordinates, -1,
                                                  Node.VALUE_LABEL_VALUE, 2)
    textureNodetemplate2.setValueNumberOfVersions(textureCoordinates, -1,
                                                  Node.VALUE_LABEL_D_DS1, 2)
    textureNodetemplate2.setValueNumberOfVersions(textureCoordinates, -1,
                                                  Node.VALUE_LABEL_D_DS2, 2)
    if useCrossDerivatives:
        textureNodetemplate2.setValueNumberOfVersions(
            textureCoordinates, -1, Node.VALUE_LABEL_D2_DS1DS2, 2)

    elementtemplate1 = mesh.createElementtemplate()
    elementtemplate1.setElementShapeType(Element.SHAPE_TYPE_CUBE)
    elementtemplate1.defineField(textureCoordinates, -1, eftTexture1)

    elementtemplate2 = mesh.createElementtemplate()
    elementtemplate2.setElementShapeType(Element.SHAPE_TYPE_CUBE)
    elementtemplate2.defineField(textureCoordinates, -1, eftTexture2)

    # Create nodes
    # Coordinates field
    for n in range(len(x)):
        node = nodes.createNode(nodeIdentifier, nodetemplate)
        cache.setNode(node)
        coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_VALUE, 1,
                                      x[n])
        coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS1, 1,
                                      d1[n])
        coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS2, 1,
                                      d2[n])
        coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS3, 1,
                                      d3[n])
        if useCrossDerivatives:
            coordinates.setNodeParameters(cache, -1,
                                          Node.VALUE_LABEL_D2_DS1DS2, 1, zero)
            coordinates.setNodeParameters(cache, -1,
                                          Node.VALUE_LABEL_D2_DS1DS3, 1, zero)
            coordinates.setNodeParameters(cache, -1,
                                          Node.VALUE_LABEL_D2_DS2DS3, 1, zero)
            coordinates.setNodeParameters(cache, -1,
                                          Node.VALUE_LABEL_D3_DS1DS2DS3, 1,
                                          zero)
        # print('NodeIdentifier = ', nodeIdentifier, xList[n])
        nodeIdentifier = nodeIdentifier + 1

    # Flat and texture coordinates fields
    nodeIdentifier = firstNodeIdentifier
    for n2 in range(elementsCountAlong + 1):
        for n3 in range(elementsCountThroughWall + 1):
            for n1 in range(elementsCountAround):
                i = n2 * (elementsCountAround +
                          1) * (elementsCountThroughWall +
                                1) + (elementsCountAround + 1) * n3 + n1
                node = nodes.findNodeByIdentifier(nodeIdentifier)
                node.merge(flatNodetemplate2 if n1 == 0 else flatNodetemplate1)
                node.merge(textureNodetemplate2 if n1 ==
                           0 else textureNodetemplate1)
                cache.setNode(node)
                # print('NodeIdentifier', nodeIdentifier, 'version 1, xList Index =', i+1)
                flatCoordinates.setNodeParameters(cache, -1,
                                                  Node.VALUE_LABEL_VALUE, 1,
                                                  xFlat[i])
                flatCoordinates.setNodeParameters(cache, -1,
                                                  Node.VALUE_LABEL_D_DS1, 1,
                                                  d1Flat[i])
                flatCoordinates.setNodeParameters(cache, -1,
                                                  Node.VALUE_LABEL_D_DS2, 1,
                                                  d2Flat[i])
                textureCoordinates.setNodeParameters(cache, -1,
                                                     Node.VALUE_LABEL_VALUE, 1,
                                                     xTexture[i])
                textureCoordinates.setNodeParameters(cache, -1,
                                                     Node.VALUE_LABEL_D_DS1, 1,
                                                     d1Texture[i])
                textureCoordinates.setNodeParameters(cache, -1,
                                                     Node.VALUE_LABEL_D_DS2, 1,
                                                     d2Texture[i])
                if useCrossDerivatives:
                    flatCoordinates.setNodeParameters(
                        cache, -1, Node.VALUE_LABEL_D2_DS1DS2, 1, zero)
                    textureCoordinates.setNodeParameters(
                        cache, -1, Node.VALUE_LABEL_D2_DS1DS2, 1, zero)
                if n1 == 0:
                    # print('NodeIdentifier', nodeIdentifier, 'version 2, xList Index =', i+elementsCountAround+1)
                    flatCoordinates.setNodeParameters(
                        cache, -1, Node.VALUE_LABEL_VALUE, 2,
                        xFlat[i + elementsCountAround])
                    flatCoordinates.setNodeParameters(
                        cache, -1, Node.VALUE_LABEL_D_DS1, 2,
                        d1Flat[i + elementsCountAround])
                    flatCoordinates.setNodeParameters(
                        cache, -1, Node.VALUE_LABEL_D_DS2, 2,
                        d2Flat[i + elementsCountAround])
                    textureCoordinates.setNodeParameters(
                        cache, -1, Node.VALUE_LABEL_VALUE, 2,
                        xTexture[i + elementsCountAround])
                    textureCoordinates.setNodeParameters(
                        cache, -1, Node.VALUE_LABEL_D_DS1, 2,
                        d1Texture[i + elementsCountAround])
                    textureCoordinates.setNodeParameters(
                        cache, -1, Node.VALUE_LABEL_D_DS2, 2,
                        d2Texture[i + elementsCountAround])
                    if useCrossDerivatives:
                        flatCoordinates.setNodeParameters(
                            cache, -1, Node.VALUE_LABEL_D2_DS1DS2, 2, zero)
                        textureCoordinates.setNodeParameters(
                            cache, -1, Node.VALUE_LABEL_D2_DS1DS2, 2, zero)
                nodeIdentifier = nodeIdentifier + 1

    # create elements
    now = elementsCountAround * (elementsCountThroughWall + 1)
    for e2 in range(elementsCountAlong):
        for e3 in range(elementsCountThroughWall):
            for e1 in range(elementsCountAround):
                bni11 = e2 * now + e3 * elementsCountAround + e1 + 1
                bni12 = e2 * now + e3 * elementsCountAround + (
                    e1 + 1) % elementsCountAround + 1
                bni21 = e2 * now + (e3 + 1) * elementsCountAround + e1 + 1
                bni22 = e2 * now + (e3 + 1) * elementsCountAround + (
                    e1 + 1) % elementsCountAround + 1
                nodeIdentifiers = [
                    bni11, bni12, bni11 + now, bni12 + now, bni21, bni22,
                    bni21 + now, bni22 + now
                ]
                onOpening = e1 > elementsCountAround - 2
                element = mesh.createElement(elementIdentifier,
                                             elementtemplate)
                element.setNodesByIdentifier(eft, nodeIdentifiers)
                element.merge(flatElementtemplate2
                              if onOpening else flatElementtemplate1)
                element.merge(
                    elementtemplate2 if onOpening else elementtemplate1)
                element.setNodesByIdentifier(
                    eftTexture2 if onOpening else eftTexture1, nodeIdentifiers)
                elementIdentifier = elementIdentifier + 1
                if annotationGroups:
                    for annotationGroup in annotationGroups:
                        if annotationArray[e1] == annotationGroup._name:
                            meshGroup = annotationGroup.getMeshGroup(mesh)
                            meshGroup.addElement(element)

    fm.endChange()

    return nodeIdentifier, elementIdentifier, annotationGroups