예제 #1
0
 def generateElements(self, mesh, fieldModule, coordinates):
     """
     Create cylinder elements from nodes.
     :param mesh:
     :param fieldModule: Zinc fieldmodule to create nodes in. Uses DOMAIN_TYPE_NODES.
     :param coordinates: Coordinate field to define.
     """
     elementIdentifier = max(1, getMaximumElementIdentifier(mesh) + 1)
     self._startElementIdentifier = elementIdentifier
     elementIdentifier = self._shield.generateElements(
         fieldModule, coordinates, elementIdentifier, [])
     self._endElementIdentifier = elementIdentifier
예제 #2
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.
        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 = findOrCreateFieldCoordinates(fm)
        cache = fm.createFieldcache()

        mesh = fm.findMeshByDimension(3)

        if aorticNotPulmonary:
            arterialRootGroup = AnnotationGroup(region, get_heart_term("root of aorta"))
            cuspGroups = [
                AnnotationGroup(region, get_heart_term("posterior cusp of aortic valve")),
                AnnotationGroup(region, get_heart_term("right cusp of aortic valve")),
                AnnotationGroup(region, get_heart_term("left cusp of aortic valve")) ]
        else:
            arterialRootGroup = AnnotationGroup(region, get_heart_term("root of pulmonary trunk"))
            cuspGroups = [
                AnnotationGroup(region, get_heart_term("right cusp of pulmonary valve")),
                AnnotationGroup(region, get_heart_term("anterior cusp of pulmonary valve")),
                AnnotationGroup(region, get_heart_term("left cusp of pulmonary valve")) ]

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

        nodes = fm.findNodesetByFieldDomainType(Field.DOMAIN_TYPE_NODES)

        #################
        # 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)
        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, 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
        #################

        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, 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)
                result3 = element.setScaleFactors(eft1, scalefactors) if scalefactors else None
                #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)
                result3 = element.setScaleFactors(eft1, scalefactors) if scalefactors else None
                #print('create semilunar cusp', cusp, e, 'element',elementIdentifier, result, result2, result3, nids)
                elementIdentifier += 1

                for meshGroup in meshGroups:
                    meshGroup.addElement(element)

        fm.endChange()
        return annotationGroups