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
Beispiel #2
0
    def __init__(self, sourceRegion, targetRegion, sourceAnnotationGroups=[]):
        '''
        Assumes targetRegion is empty.
        :param sourceAnnotationGroups: List of AnnotationGroup for source mesh in sourceRegion.
        A copy containing the refined elements is created by the MeshRefinement.
        '''
        self._sourceRegion = sourceRegion
        self._sourceFm = sourceRegion.getFieldmodule()
        self._sourceCache = self._sourceFm.createFieldcache()
        self._sourceCoordinates = findOrCreateFieldCoordinates(self._sourceFm)
        # get range of source coordinates for octree range
        self._sourceFm.beginChange()
        sourceNodes = self._sourceFm.findNodesetByFieldDomainType(
            Field.DOMAIN_TYPE_NODES)
        minimumsField = self._sourceFm.createFieldNodesetMinimum(
            self._sourceCoordinates, sourceNodes)
        result, minimums = minimumsField.evaluateReal(self._sourceCache, 3)
        assert result == ZINC_OK, 'MeshRefinement failed to get minimum coordinates'
        maximumsField = self._sourceFm.createFieldNodesetMaximum(
            self._sourceCoordinates, sourceNodes)
        result, maximums = maximumsField.evaluateReal(self._sourceCache, 3)
        assert result == ZINC_OK, 'MeshRefinement failed to get maximum coordinates'
        xrange = [(maximums[i] - minimums[i]) for i in range(3)]
        edgeTolerance = 0.5 * (max(xrange))
        if edgeTolerance == 0.0:
            edgeTolerance = 1.0
        minimums = [(minimums[i] - edgeTolerance) for i in range(3)]
        maximums = [(maximums[i] + edgeTolerance) for i in range(3)]
        minimumsField = None
        maximumsField = None
        self._sourceMesh = self._sourceFm.findMeshByDimension(3)
        self._sourceNodes = self._sourceFm.findNodesetByFieldDomainType(
            Field.DOMAIN_TYPE_NODES)
        self._sourceElementiterator = self._sourceMesh.createElementiterator()
        self._octree = Octree(minimums, maximums)

        self._targetRegion = targetRegion
        self._targetFm = targetRegion.getFieldmodule()
        self._targetFm.beginChange()
        self._targetCache = self._targetFm.createFieldcache()
        self._targetCoordinates = findOrCreateFieldCoordinates(self._targetFm)

        self._targetNodes = self._targetFm.findNodesetByFieldDomainType(
            Field.DOMAIN_TYPE_NODES)
        self._nodetemplate = self._targetNodes.createNodetemplate()
        self._nodetemplate.defineField(self._targetCoordinates)

        self._targetMesh = self._targetFm.findMeshByDimension(3)
        self._targetBasis = self._targetFm.createElementbasis(
            3, Elementbasis.FUNCTION_TYPE_LINEAR_LAGRANGE)
        self._targetEft = self._targetMesh.createElementfieldtemplate(
            self._targetBasis)
        self._targetElementtemplate = self._targetMesh.createElementtemplate()
        self._targetElementtemplate.setElementShapeType(
            Element.SHAPE_TYPE_CUBE)
        result = self._targetElementtemplate.defineField(
            self._targetCoordinates, -1, self._targetEft)

        self._nodeIdentifier = 1
        self._elementIdentifier = 1
        # prepare annotation group map
        self._sourceAnnotationGroups = sourceAnnotationGroups
        self._annotationGroups = []
        self._sourceAndTargetMeshGroups = []
        self._sourceAndTargetNodesetGroups = []
        for sourceAnnotationGroup in sourceAnnotationGroups:
            targetAnnotationGroup = AnnotationGroup(
                self._targetRegion, sourceAnnotationGroup.getTerm())
            self._annotationGroups.append(targetAnnotationGroup)
            # assume have only highest dimension element or node/point annotation groups:
            if sourceAnnotationGroup.hasMeshGroup(self._sourceMesh):
                self._sourceAndTargetMeshGroups.append(
                    (sourceAnnotationGroup.getMeshGroup(self._sourceMesh),
                     targetAnnotationGroup.getMeshGroup(self._targetMesh)))
            else:
                self._sourceAndTargetNodesetGroups.append(
                    (sourceAnnotationGroup.getNodesetGroup(self._sourceNodes),
                     targetAnnotationGroup.getNodesetGroup(self._targetNodes)))

        # prepare element -> marker point list map
        self.elementMarkerMap = {}
        sourceMarkerGroup = findOrCreateFieldGroup(self._sourceFm, "marker")
        sourceMarkerName = findOrCreateFieldStoredString(self._sourceFm,
                                                         name="marker_name")
        sourceMarkerLocation = findOrCreateFieldStoredMeshLocation(
            self._sourceFm, self._sourceMesh, name="marker_location")
        sourceMarkerNodes = findOrCreateFieldNodeGroup(
            sourceMarkerGroup, sourceNodes).getNodesetGroup()
        nodeIter = sourceMarkerNodes.createNodeiterator()
        node = nodeIter.next()
        while node.isValid():
            self._sourceCache.setNode(node)
            element, xi = sourceMarkerLocation.evaluateMeshLocation(
                self._sourceCache, self._sourceMesh.getDimension())
            if element.isValid():
                elementIdentifier = element.getIdentifier()
                markerName = sourceMarkerName.evaluateString(self._sourceCache)
                markerList = self.elementMarkerMap.get(elementIdentifier)
                if not markerList:
                    markerList = []
                    self.elementMarkerMap[elementIdentifier] = markerList
                markerList.append((markerName, xi, node.getIdentifier()))
            node = nodeIter.next()
        if self.elementMarkerMap:
            self._targetMarkerGroup = findOrCreateFieldGroup(
                self._targetFm, "marker")
            self._targetMarkerName = findOrCreateFieldStoredString(
                self._targetFm, name="marker_name")
            self._targetMarkerLocation = findOrCreateFieldStoredMeshLocation(
                self._targetFm, self._targetMesh, name="marker_location")
            self._targetMarkerNodes = findOrCreateFieldNodeGroup(
                self._targetMarkerGroup, self._targetNodes).getNodesetGroup()
            self._targetMarkerTemplate = self._targetMarkerNodes.createNodetemplate(
            )
            self._targetMarkerTemplate.defineField(self._targetMarkerName)
            self._targetMarkerTemplate.defineField(self._targetMarkerLocation)
    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: 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()
        fm.beginChange()
        coordinates = zinc_utils.getOrCreateCoordinateField(fm)
        cache = fm.createFieldcache()

        # generate heartventriclesbase1 model and put atria1 on it
        annotationGroups = MeshType_3d_heartventriclesbase1.generateBaseMesh(region, options)
        annotationGroups += MeshType_3d_heartatria1.generateBaseMesh(region, options)
        lFibrousRingGroup = AnnotationGroup(region, 'left fibrous ring', FMANumber = 77124, lyphID = 'Lyph ID unknown')
        rFibrousRingGroup = AnnotationGroup(region, 'right fibrous ring', FMANumber = 77125, lyphID = 'Lyph ID unknown')
        annotationGroups += [ lFibrousRingGroup, rFibrousRingGroup ]

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

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

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

        nodes = fm.findNodesetByFieldDomainType(Field.DOMAIN_TYPE_NODES)

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

        mesh = fm.findMeshByDimension(3)

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

        elementIdentifier = startElementIdentifier = zinc_utils.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)
            if scalefactors:
                result3 = element.setScaleFactors(eft1, scalefactors)
            else:
                result3 = 7
            #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)
            if scalefactors:
                result3 = element.setScaleFactors(eft1, scalefactors)
            else:
                result3 = 7
            #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)
            if scalefactors:
                result3 = element.setScaleFactors(eft1, scalefactors)
            else:
                result3 = 7
            #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.0, 0.5, 1.0 ]
        cache.setMeshLocation(cruxElement, cruxXi)
        result, cruxCoordinates = coordinates.evaluateReal(cache, 3)
        datapoint = fiducialPoints.createNode(-1, datapointTemplateInternal)
        cache.setNode(datapoint)
        fiducialCoordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_VALUE, 1, cruxCoordinates)
        fiducialLabel.assignString(cache, 'crux')
        fiducialElementXi.assignMeshLocation(cache, cruxElement, cruxXi)

        fm.endChange()
        return annotationGroups