示例#1
0
 def generateNodes(self, nodes, fieldModule, coordinates):
     """
     Create cylinder nodes from coordinates.
     :param nodes: nodes from coordinates.
     :param fieldModule: Zinc fieldmodule to create nodes in. Uses DOMAIN_TYPE_NODES.
     :param coordinates: Coordinate field to define.
     """
     nodeIdentifier = max(1, getMaximumNodeIdentifier(nodes) + 1)
     self._startNodeIdentifier = nodeIdentifier
     nodeIdentifier = self._shield.generateNodes(fieldModule, coordinates,
                                                 nodeIdentifier)
     self._endNodeIdentifier = nodeIdentifier
示例#2
0
 def _loadData(self):
     """
     Load zinc data file into self._rawDataRegion.
     Rename data groups to exactly match model groups where they differ by case and whitespace only.
     Transfer data points (and converted nodes) into self._region.
     """
     result = self._rawDataRegion.readFile(self._zincDataFileName)
     assert result == RESULT_OK, "Failed to load data file " + str(
         self._zincDataFileName)
     fieldmodule = self._rawDataRegion.getFieldmodule()
     with ChangeManager(fieldmodule):
         # rename data groups to match model
         # future: match with annotation terms
         modelGroupNames = [
             group.getName() for group in getGroupList(self._fieldmodule)
         ]
         writeDiagnostics = self.getDiagnosticLevel() > 0
         for dataGroup in getGroupList(fieldmodule):
             dataGroupName = dataGroup.getName()
             compareName = dataGroupName.strip().casefold()
             for modelGroupName in modelGroupNames:
                 if modelGroupName == dataGroupName:
                     if writeDiagnostics:
                         print("Load data: Data group '" + dataGroupName +
                               "' found in model")
                     break
                 elif modelGroupName.strip().casefold() == compareName:
                     result = dataGroup.setName(modelGroupName)
                     if result == RESULT_OK:
                         if writeDiagnostics:
                             print("Load data: Data group '" +
                                   dataGroupName + "' found in model as '" +
                                   modelGroupName + "'. Renaming to match.")
                     else:
                         print("Error: Load data: Data group '" +
                               dataGroupName + "' found in model as '" +
                               modelGroupName +
                               "'. Renaming to match FAILED.")
                         if fieldmodule.findFieldByName(
                                 modelGroupName).isValid():
                             print(
                                 "    Reason: field of that name already exists."
                             )
                     break
             else:
                 if writeDiagnostics:
                     print("Load data: Data group '" + dataGroupName +
                           "' not found in model")
         # if there are both nodes and datapoints, offset datapoint identifiers to ensure different
         nodes = fieldmodule.findNodesetByFieldDomainType(
             Field.DOMAIN_TYPE_NODES)
         if nodes.getSize() > 0:
             datapoints = fieldmodule.findNodesetByFieldDomainType(
                 Field.DOMAIN_TYPE_DATAPOINTS)
             if datapoints.getSize() > 0:
                 maximumDatapointIdentifier = max(
                     0, getMaximumNodeIdentifier(datapoints))
                 maximumNodeIdentifier = max(
                     0, getMaximumNodeIdentifier(nodes))
                 # this assumes identifiers are in low ranges and can be improved if there is a problem:
                 identifierOffset = 100000
                 while (maximumDatapointIdentifier > identifierOffset) or (
                         maximumNodeIdentifier > identifierOffset):
                     assert identifierOffset < 1000000000, "Invalid node and datapoint identifier ranges"
                     identifierOffset *= 10
                 while True:
                     # logic relies on datapoints being in identifier order
                     datapoint = datapoints.createNodeiterator().next()
                     identifier = datapoint.getIdentifier()
                     if identifier >= identifierOffset:
                         break
                     result = datapoint.setIdentifier(identifier +
                                                      identifierOffset)
                     assert result == RESULT_OK, "Failed to offset datapoint identifier"
             # transfer nodes as datapoints to self._region
             sir = self._rawDataRegion.createStreaminformationRegion()
             srm = sir.createStreamresourceMemory()
             sir.setResourceDomainTypes(srm, Field.DOMAIN_TYPE_NODES)
             self._rawDataRegion.write(sir)
             result, buffer = srm.getBuffer()
             assert result == RESULT_OK, "Failed to write nodes"
             buffer = buffer.replace(bytes("!#nodeset nodes", "utf-8"),
                                     bytes("!#nodeset datapoints", "utf-8"))
             sir = self._region.createStreaminformationRegion()
             srm = sir.createStreamresourceMemoryBuffer(buffer)
             result = self._region.read(sir)
             assert result == RESULT_OK, "Failed to load nodes as datapoints"
     # transfer datapoints to self._region
     sir = self._rawDataRegion.createStreaminformationRegion()
     srm = sir.createStreamresourceMemory()
     sir.setResourceDomainTypes(srm, Field.DOMAIN_TYPE_DATAPOINTS)
     self._rawDataRegion.write(sir)
     result, buffer = srm.getBuffer()
     assert result == RESULT_OK, "Failed to write datapoints"
     sir = self._region.createStreaminformationRegion()
     srm = sir.createStreamresourceMemoryBuffer(buffer)
     result = self._region.read(sir)
     assert result == RESULT_OK, "Failed to load datapoints"
     self._discoverDataCoordinatesField()
     self._discoverMarkerGroup()
示例#3
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):
        """
        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']
        isCat = 'Cat 1' in parameterSetName
        isHuman = 'Human 1' in parameterSetName
        isMouse = 'Mouse 1' in parameterSetName
        isRat = 'Rat 1' in parameterSetName
        isPig = 'Pig 1' in parameterSetName
        isSheep = 'Sheep 1' in parameterSetName

        centralPath = options['Central path']
        brainstemPath = cls.centralPathDefaultScaffoldPackages['Brainstem 1']
        elementsCountAcrossMajor = options['Number of elements across major']
        elementsCountAcrossMinor = options['Number of elements across minor']
        elementsCountAlong = options['Number of elements along']

        # Cross section at Z axis
        halfBrainStem = False
        if halfBrainStem:
            elementsCountAcrossMajor //= 2
        elementsPerLayer = (
            (elementsCountAcrossMajor - 2) *
            elementsCountAcrossMinor) + (2 * (elementsCountAcrossMinor - 2))

        fm = region.getFieldmodule()
        cache = fm.createFieldcache()
        coordinates = findOrCreateFieldCoordinates(fm)
        mesh = fm.findMeshByDimension(3)

        # Annotation groups
        brainstemGroup = AnnotationGroup(region,
                                         get_brainstem_term('brainstem'))
        brainstemMeshGroup = brainstemGroup.getMeshGroup(mesh)
        midbrainGroup = AnnotationGroup(region, get_brainstem_term('midbrain'))
        midbrainMeshGroup = midbrainGroup.getMeshGroup(mesh)
        ponsGroup = AnnotationGroup(region, get_brainstem_term('pons'))
        ponsMeshGroup = ponsGroup.getMeshGroup(mesh)
        medullaGroup = AnnotationGroup(region,
                                       get_brainstem_term('medulla oblongata'))
        medullaMeshGroup = medullaGroup.getMeshGroup(mesh)
        annotationGroups = [
            brainstemGroup, midbrainGroup, ponsGroup, medullaGroup
        ]

        annotationGroupAlong = [[brainstemGroup, midbrainGroup],
                                [brainstemGroup, ponsGroup],
                                [brainstemGroup, medullaGroup]]

        # point markers
        # centralCanal = findOrCreateAnnotationGroupForTerm(annotationGroups, region,
        #                                                        get_brainstem_term('central canal of spinal cord'))
        # cerebralAqueduct = findOrCreateAnnotationGroupForTerm(annotationGroups, region,
        #                                                        get_brainstem_term('cerebral aqueduct'))
        # foramenCaecum = findOrCreateAnnotationGroupForTerm(annotationGroups, region,
        #                                                        get_brainstem_term('foramen caecum of medulla oblongata'))
        dorsalMidCaudalGroup = findOrCreateAnnotationGroupForTerm(
            annotationGroups, region,
            get_brainstem_term('brainstem dorsal midline caudal point'))
        ventralMidCaudalGroup = findOrCreateAnnotationGroupForTerm(
            annotationGroups, region,
            get_brainstem_term('brainstem ventral midline caudal point'))
        dorsalMidCranGroup = findOrCreateAnnotationGroupForTerm(
            annotationGroups, region,
            get_brainstem_term('brainstem dorsal midline cranial point'))
        ventralMidCranGroup = findOrCreateAnnotationGroupForTerm(
            annotationGroups, region,
            get_brainstem_term('brainstem ventral midline cranial point'))
        dorsalMidMedullaPonsJunction = findOrCreateAnnotationGroupForTerm(
            annotationGroups, region,
            get_brainstem_term(
                'brainstem dorsal midline pons-medulla junction'))
        ventralMidMedullaPonsJunction = findOrCreateAnnotationGroupForTerm(
            annotationGroups, region,
            get_brainstem_term(
                'brainstem ventral midline pons-medulla junction'))
        dorsalMidMidbrainPonsJunction = findOrCreateAnnotationGroupForTerm(
            annotationGroups, region,
            get_brainstem_term(
                'brainstem dorsal midline midbrain-pons junction'))
        ventralMidMidbrainPonsJunction = findOrCreateAnnotationGroupForTerm(
            annotationGroups, region,
            get_brainstem_term(
                'brainstem ventral midline midbrain-pons junction'))

        #######################
        # CREATE MAIN BODY MESH
        #######################
        cylinderShape = CylinderShape.CYLINDER_SHAPE_FULL if not halfBrainStem else CylinderShape.CYLINDER_SHAPE_LOWER_HALF

        # Body coordinates
        cylinderCentralPath = CylinderCentralPath(region, centralPath,
                                                  elementsCountAlong)
        base = CylinderEnds(elementsCountAcrossMajor,
                            elementsCountAcrossMinor,
                            centre=[0.0, 0.0, 0.0],
                            alongAxis=cylinderCentralPath.alongAxis[0],
                            majorAxis=cylinderCentralPath.majorAxis[0],
                            minorRadius=cylinderCentralPath.minorRadii[0])

        cylinder1 = CylinderMesh(fm,
                                 coordinates,
                                 elementsCountAlong,
                                 base,
                                 cylinderShape=cylinderShape,
                                 cylinderCentralPath=cylinderCentralPath,
                                 useCrossDerivatives=False)

        brainstem_coordinates = findOrCreateFieldCoordinates(
            fm, name="brainstem coordinates")

        # Brain coordinates
        tmp_region = region.createRegion()
        tmp_fm = tmp_region.getFieldmodule()
        tmp_brainstem_coordinates = findOrCreateFieldCoordinates(
            tmp_fm, name="brainstem coordinates")

        cylinderCentralPath1 = CylinderCentralPath(tmp_region, brainstemPath,
                                                   elementsCountAlong)

        base1 = CylinderEnds(elementsCountAcrossMajor,
                             elementsCountAcrossMinor,
                             centre=[0.0, 0.0, 0.0],
                             alongAxis=cylinderCentralPath1.alongAxis[0],
                             majorAxis=cylinderCentralPath1.majorAxis[0],
                             minorRadius=cylinderCentralPath1.minorRadii[0])

        cylinder2 = CylinderMesh(tmp_fm,
                                 tmp_brainstem_coordinates,
                                 elementsCountAlong,
                                 base1,
                                 cylinderShape=cylinderShape,
                                 cylinderCentralPath=cylinderCentralPath1,
                                 useCrossDerivatives=False)

        # Write two coordinates
        sir = tmp_region.createStreaminformationRegion()
        srm = sir.createStreamresourceMemory()
        tmp_region.write(sir)
        result, buffer = srm.getBuffer()

        sir = region.createStreaminformationRegion()
        srm = sir.createStreamresourceMemoryBuffer(buffer)
        region.read(sir)

        del srm
        del sir
        del tmp_fm
        del tmp_brainstem_coordinates
        del tmp_region

        # Annotating groups
        iRegionBoundaries = [
            int(6 * elementsCountAlong / 15),
            int(13 * elementsCountAlong / 15)
        ]
        for elementIdentifier in range(1, mesh.getSize() + 1):
            element = mesh.findElementByIdentifier(elementIdentifier)
            brainstemMeshGroup.addElement(element)
            if elementIdentifier > (iRegionBoundaries[-1] * elementsPerLayer):
                midbrainMeshGroup.addElement(element)
            elif (elementIdentifier >
                  (iRegionBoundaries[0] * elementsPerLayer)) and (
                      elementIdentifier <=
                      (iRegionBoundaries[-1] * elementsPerLayer)):
                ponsMeshGroup.addElement(element)
            else:
                medullaMeshGroup.addElement(element)

        ################
        # point markers
        ################
        pointMarkers = [
            {
                "group": dorsalMidCaudalGroup,
                "marker_brainstem_coordinates": [0.0, 1.0, 0.0]
            },
            {
                "group": ventralMidCaudalGroup,
                "marker_brainstem_coordinates": [0.0, -1.0, 0.0]
            },
            {
                "group": dorsalMidCranGroup,
                "marker_brainstem_coordinates": [0.0, 1.0, 8.0]
            },
            {
                "group": ventralMidCranGroup,
                "marker_brainstem_coordinates": [0.0, -1.0, 8.0]
            },
            {
                "group": dorsalMidMedullaPonsJunction,
                "marker_brainstem_coordinates": [0.0, 1.0, 3.0]
            },
            {
                "group": ventralMidMedullaPonsJunction,
                "marker_brainstem_coordinates": [0.0, -1.0, 3.0]
            },
            {
                "group": dorsalMidMidbrainPonsJunction,
                "marker_brainstem_coordinates": [0.0, 1.0, 6.0]
            },
            {
                "group": ventralMidMidbrainPonsJunction,
                "marker_brainstem_coordinates": [0.0, -1.0, 6.0]
            },
        ]

        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()
        markerBrainstemCoordinates = findOrCreateFieldCoordinates(
            fm, name="marker_body_coordinates")
        markerTemplateInternal = nodes.createNodetemplate()
        markerTemplateInternal.defineField(markerName)
        markerTemplateInternal.defineField(markerLocation)
        markerTemplateInternal.defineField(markerBrainstemCoordinates)

        cache = fm.createFieldcache()

        brainstemNodesetGroup = brainstemGroup.getNodesetGroup(nodes)

        nodeIdentifier = max(1, getMaximumNodeIdentifier(nodes) + 1)
        findMarkerLocation = fm.createFieldFindMeshLocation(
            markerBrainstemCoordinates, brainstem_coordinates, mesh)
        findMarkerLocation.setSearchMode(
            FieldFindMeshLocation.SEARCH_MODE_EXACT)

        for pointMarker in pointMarkers:
            group = pointMarker["group"]
            markerPoint = markerPoints.createNode(nodeIdentifier,
                                                  markerTemplateInternal)
            cache.setNode(markerPoint)

            markerBrainstemCoordinates.assignReal(
                cache, pointMarker["marker_brainstem_coordinates"])
            markerName.assignString(cache, group.getName())

            element, xi = findMarkerLocation.evaluateMeshLocation(cache, 3)
            markerLocation.assignMeshLocation(cache, element, xi)
            group.getNodesetGroup(nodes).addNode(markerPoint)
            brainstemNodesetGroup.addNode(markerPoint)
            nodeIdentifier += 1

        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
示例#6
0
 def _loadData(self):
     """
     Load zinc data file into self._rawDataRegion.
     Transfer data points (and converted nodes) into self._region.
     """
     result = self._rawDataRegion.readFile(self._zincDataFileName)
     assert result == RESULT_OK, "Failed to load data file " + str(
         self._zincDataFileName)
     # if there both nodes and datapoints, offset datapoint identifiers to ensure different
     fieldmodule = self._rawDataRegion.getFieldmodule()
     nodes = fieldmodule.findNodesetByFieldDomainType(
         Field.DOMAIN_TYPE_NODES)
     if nodes.getSize() > 0:
         datapoints = fieldmodule.findNodesetByFieldDomainType(
             Field.DOMAIN_TYPE_DATAPOINTS)
         if datapoints.getSize() > 0:
             maximumDatapointIdentifier = max(
                 0, getMaximumNodeIdentifier(datapoints))
             maximumNodeIdentifier = max(0, getMaximumNodeIdentifier(nodes))
             # this assumes identifiers are in low ranges and can be improved if there is a problem:
             identifierOffset = 100000
             while (maximumDatapointIdentifier > identifierOffset) or (
                     maximumNodeIdentifier > identifierOffset):
                 assert identifierOffset < 1000000000, "Invalid node and datapoint identifier ranges"
                 identifierOffset *= 10
             with ChangeManager(fieldmodule):
                 while True:
                     # logic relies on datapoints being in identifier order
                     datapoint = datapoints.createNodeiterator().next()
                     identifier = datapoint.getIdentifier()
                     if identifier >= identifierOffset:
                         break
                     result = datapoint.setIdentifier(identifier +
                                                      identifierOffset)
                     assert result == RESULT_OK, "Failed to offset datapoint identifier"
         # transfer nodes as datapoints to self._region
         sir = self._rawDataRegion.createStreaminformationRegion()
         srm = sir.createStreamresourceMemory()
         sir.setResourceDomainTypes(srm, Field.DOMAIN_TYPE_NODES)
         self._rawDataRegion.write(sir)
         result, buffer = srm.getBuffer()
         assert result == RESULT_OK, "Failed to write nodes"
         buffer = buffer.replace(bytes("!#nodeset nodes", "utf-8"),
                                 bytes("!#nodeset datapoints", "utf-8"))
         sir = self._region.createStreaminformationRegion()
         srm = sir.createStreamresourceMemoryBuffer(buffer)
         result = self._region.read(sir)
         assert result == RESULT_OK, "Failed to load nodes as datapoints"
     # transfer datapoints to self._region
     sir = self._rawDataRegion.createStreaminformationRegion()
     srm = sir.createStreamresourceMemory()
     sir.setResourceDomainTypes(srm, Field.DOMAIN_TYPE_DATAPOINTS)
     self._rawDataRegion.write(sir)
     result, buffer = srm.getBuffer()
     assert result == RESULT_OK, "Failed to write datapoints"
     sir = self._region.createStreaminformationRegion()
     srm = sir.createStreamresourceMemoryBuffer(buffer)
     result = self._region.read(sir)
     assert result == RESULT_OK, "Failed to load datapoints"
     self._discoverDataCoordinatesField()
     self._discoverMarkerGroup()
    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']
        isCat = 'Cat 1' in parameterSetName
        isHuman = 'Human 1' in parameterSetName
        isMouse = 'Mouse 1' in parameterSetName
        isRat = 'Rat 1' in parameterSetName
        isPig = 'Pig 1' in parameterSetName
        isSheep = 'Sheep 1' in parameterSetName

        centralPath = options['Central path']
        brainstemPath = cls.centralPathDefaultScaffoldPackages['Brainstem 1']
        elementsCountAcrossMajor = options['Number of elements across major']
        elementsCountAcrossMinor = options['Number of elements across minor']
        elementsCountAlong = options['Number of elements along']

        # Cross section at Z axis
        halfBrainStem = False
        if halfBrainStem:
            elementsCountAcrossMajor //= 2
        elementsPerLayer = ((elementsCountAcrossMajor - 2) * elementsCountAcrossMinor) + (
                2 * (elementsCountAcrossMinor - 2))

        fm = region.getFieldmodule()
        cache = fm.createFieldcache()
        coordinates = findOrCreateFieldCoordinates(fm)
        mesh = fm.findMeshByDimension(3)

        # Annotation groups
        brainstemGroup = AnnotationGroup(region, get_brainstem_term('brainstem'))
        brainstemMeshGroup = brainstemGroup.getMeshGroup(mesh)
        midbrainGroup = AnnotationGroup(region, get_brainstem_term('midbrain'))
        midbrainMeshGroup = midbrainGroup.getMeshGroup(mesh)
        ponsGroup = AnnotationGroup(region, get_brainstem_term('pons'))
        ponsMeshGroup = ponsGroup.getMeshGroup(mesh)
        medullaGroup = AnnotationGroup(region, get_brainstem_term('medulla oblongata'))
        medullaMeshGroup = medullaGroup.getMeshGroup(mesh)
        annotationGroups = [brainstemGroup, midbrainGroup, ponsGroup, medullaGroup]

        annotationGroupAlong = [[brainstemGroup, midbrainGroup],
                                [brainstemGroup, ponsGroup],
                                [brainstemGroup, medullaGroup]]
        # centralCanal = findOrCreateAnnotationGroupForTerm(annotationGroups, region,
        #                                                        get_brainstem_term('central canal of spinal cord'))
        # cerebralAqueduct = findOrCreateAnnotationGroupForTerm(annotationGroups, region,
        #                                                        get_brainstem_term('cerebral aqueduct'))
        # foramenCaecum = findOrCreateAnnotationGroupForTerm(annotationGroups, region,

        #######################
        # CREATE MAIN BODY MESH
        #######################
        cylinderShape = CylinderShape.CYLINDER_SHAPE_FULL if not halfBrainStem else CylinderShape.CYLINDER_SHAPE_LOWER_HALF

        # brainstem coordinates
        cylinderCentralPath = CylinderCentralPath(region, centralPath, elementsCountAlong)
        base = CylinderEnds(elementsCountAcrossMajor, elementsCountAcrossMinor,
                            centre=[0.0, 0.0, 0.0],
                            alongAxis=cylinderCentralPath.alongAxis[0],
                            majorAxis=cylinderCentralPath.majorAxis[0],
                            minorRadius=cylinderCentralPath.minorRadii[0])

        cylinder1 = CylinderMesh(fm, coordinates, elementsCountAlong, base,
                                 cylinderShape=cylinderShape,
                                 cylinderCentralPath=cylinderCentralPath,
                                 useCrossDerivatives=False)

        #  workaround for old Zinc field wrapper bug: must create brainstem coordinates field before reading file
        brainstem_coordinates = findOrCreateFieldCoordinates(fm, name="brainstem coordinates")

        # generate brainstem coordinates field in temporary region
        tmp_region = region.createRegion()
        tmp_fm = tmp_region.getFieldmodule()
        with ChangeManager(tmp_fm):
            tmp_brainstem_coordinates = findOrCreateFieldCoordinates(tmp_fm, name="brainstem coordinates")

            cylinderCentralPath1 = CylinderCentralPath(tmp_region, brainstemPath, elementsCountAlong)

            base1 = CylinderEnds(elementsCountAcrossMajor, elementsCountAcrossMinor,
                                 centre=[0.0, 0.0, 0.0],
                                 alongAxis=cylinderCentralPath1.alongAxis[0],
                                 majorAxis=cylinderCentralPath1.majorAxis[0],
                                 minorRadius=cylinderCentralPath1.minorRadii[0])

            cylinder2 = CylinderMesh(tmp_fm, tmp_brainstem_coordinates, elementsCountAlong, base1,
                                     cylinderShape=cylinderShape,
                                     cylinderCentralPath=cylinderCentralPath1,
                                     useCrossDerivatives=False)

            # write to memory buffer
            sir = tmp_region.createStreaminformationRegion()
            srm = sir.createStreamresourceMemory()
            tmp_region.write(sir)
            result, buffer = srm.getBuffer()

            # read into main region
            sir = region.createStreaminformationRegion()
            srm = sir.createStreamresourceMemoryBuffer(buffer)
            region.read(sir)

            del srm
            del sir
            del tmp_brainstem_coordinates
        del tmp_fm
        del tmp_region

        # Annotating groups
        iRegionBoundaries = [int(6 * elementsCountAlong / 15), int(13 * elementsCountAlong / 15)]
        for elementIdentifier in range(1, mesh.getSize() + 1):
            element = mesh.findElementByIdentifier(elementIdentifier)
            brainstemMeshGroup.addElement(element)
            if elementIdentifier > (iRegionBoundaries[-1] * elementsPerLayer):
                midbrainMeshGroup.addElement(element)
            elif (elementIdentifier > (iRegionBoundaries[0] * elementsPerLayer)) and (
                    elementIdentifier <= (iRegionBoundaries[-1] * elementsPerLayer)):
                ponsMeshGroup.addElement(element)
            else:
                medullaMeshGroup.addElement(element)

        ################
        # point markers
        ################

        markerTermNameBrainstemCoordinatesMap = {
            'brainstem dorsal midline caudal point': [0.0, 1.0, 0.0],
            'brainstem ventral midline caudal point': [0.0, -1.0, 0.0],
            'brainstem dorsal midline cranial point': [0.0, 1.0, 8.0],
            'brainstem ventral midline cranial point': [0.0, -1.0, 8.0],
            'brainstem dorsal midline pons-medulla junction': [0.0, 1.0, 3.0],
            'brainstem ventral midline pons-medulla junction': [0.0, -1.0, 3.0],
            'brainstem dorsal midline midbrain-pons junction': [0.0, 1.0, 6.0],
            'brainstem ventral midline midbrain-pons junction': [0.0, -1.0, 6.0]
        }
        nodes = fm.findNodesetByFieldDomainType(Field.DOMAIN_TYPE_NODES)
        nodeIdentifier = max(1, getMaximumNodeIdentifier(nodes) + 1)
        for termName, brainstemCoordinatesValues in markerTermNameBrainstemCoordinatesMap.items():
            annotationGroup = findOrCreateAnnotationGroupForTerm(annotationGroups, region, get_brainstem_term(termName))
            annotationGroup.createMarkerNode(nodeIdentifier, brainstem_coordinates, brainstemCoordinatesValues)
            nodeIdentifier += 1

        return annotationGroups