def generateMesh(region, options):
        """
        :param region: Zinc region to define model in. Must be empty.
        :param options: Dict containing options. See getDefaultOptions().
        :return: None
        """
        elementsCount1 = options['Number of elements 1']
        elementsCount2 = options['Number of elements 2']
        elementsCount3 = options['Number of elements 3']
        elementsCountThroughWall = options['Number of elements through wall']
        elementsCountAround = 2 * (elementsCount1 + elementsCount2)
        holeRadius = options['Hole diameter'] * 0.5
        useCrossDerivatives = options['Use cross derivatives']

        fm = region.getFieldmodule()
        fm.beginChange()
        coordinates = fm.createFieldFiniteElement(3)
        coordinates.setName('coordinates')
        coordinates.setManaged(True)
        coordinates.setTypeCoordinate(True)
        coordinates.setCoordinateSystemType(
            Field.COORDINATE_SYSTEM_TYPE_RECTANGULAR_CARTESIAN)
        coordinates.setComponentName(1, 'x')
        coordinates.setComponentName(2, 'y')
        coordinates.setComponentName(3, 'z')

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

        mesh = fm.findMeshByDimension(3)

        tricubichermite = eftfactory_tricubichermite(mesh, useCrossDerivatives)
        eft = tricubichermite.createEftBasic()

        tricubicHermiteBasis = fm.createElementbasis(
            3, Elementbasis.FUNCTION_TYPE_CUBIC_HERMITE)

        # note I'm cheating here by using element-based scale factors
        # i.e. not shared by neighbouring elements, and I'm also using the same scale
        # factors for the bottom and top of each element
        eftOuter1 = mesh.createElementfieldtemplate(tricubicHermiteBasis)
        eftOuter2 = mesh.createElementfieldtemplate(tricubicHermiteBasis)
        i = 0
        for eftOuter in [eftOuter1, eftOuter2]:
            i += 1
            eftOuter.setNumberOfLocalScaleFactors(10)
            eft.setScaleFactorType(
                1, Elementfieldtemplate.SCALE_FACTOR_TYPE_GLOBAL_GENERAL)
            # GRC: allow scale factor identifier for global -1 to be prescribed
            eft.setScaleFactorIdentifier(1, 1)
            # Global scale factor 4 to subtract lateral derivative term from cross derivative to fix edges
            eft.setScaleFactorType(
                2, Elementfieldtemplate.SCALE_FACTOR_TYPE_GLOBAL_GENERAL)
            eft.setScaleFactorIdentifier(2, 2)
            nonCrossDerivativesNodes = [0, 1, 4, 5
                                        ] if useCrossDerivatives else range(8)
            for n in nonCrossDerivativesNodes:
                eftOuter.setFunctionNumberOfTerms(n * 8 + 4, 0)
                eftOuter.setFunctionNumberOfTerms(n * 8 + 6, 0)
                eftOuter.setFunctionNumberOfTerms(n * 8 + 7, 0)
                eftOuter.setFunctionNumberOfTerms(n * 8 + 8, 0)
            # nodes 1,2,5,6: general map dxi1, dsxi2 from ds1, ds2
            for n in [0, 1, 4, 5]:
                ln = n + 1
                sfo = (n % 2) * 4 + 2
                eftOuter.setFunctionNumberOfTerms(n * 8 + 2, 2)
                eftOuter.setTermNodeParameter(n * 8 + 2, 1, ln,
                                              Node.VALUE_LABEL_D_DS1, 1)
                eftOuter.setTermScaling(n * 8 + 2, 1, [1 + sfo])
                eftOuter.setTermNodeParameter(n * 8 + 2, 2, ln,
                                              Node.VALUE_LABEL_D_DS2, 1)
                eftOuter.setTermScaling(n * 8 + 2, 2, [2 + sfo])
                eftOuter.setFunctionNumberOfTerms(n * 8 + 3, 2)
                eftOuter.setTermNodeParameter(n * 8 + 3, 1, ln,
                                              Node.VALUE_LABEL_D_DS1, 1)
                eftOuter.setTermScaling(n * 8 + 3, 1, [3 + sfo])
                eftOuter.setTermNodeParameter(n * 8 + 3, 2, ln,
                                              Node.VALUE_LABEL_D_DS2, 1)
                eftOuter.setTermScaling(n * 8 + 3, 2, [4 + sfo])
                # map d2_dxi1dxi2 to subtract corner angle terms to fix edge continuity
                eftOuter.setFunctionNumberOfTerms(n * 8 + 4, 1)
                if i == 1:
                    eftOuter.setTermNodeParameter(n * 8 + 4, 1, ln,
                                                  Node.VALUE_LABEL_D_DS1, 1)
                    if (n % 2) == 0:
                        eftOuter.setTermScaling(n * 8 + 4, 1, [1, 2, 3 + sfo])
                    else:
                        eftOuter.setTermScaling(n * 8 + 4, 1, [2, 3 + sfo])
                else:
                    eftOuter.setTermNodeParameter(n * 8 + 4, 1, ln,
                                                  Node.VALUE_LABEL_D_DS2, 1)
                    if (n % 2) == 0:
                        eftOuter.setTermScaling(n * 8 + 4, 1, [1, 2, 4 + sfo])
                    else:
                        eftOuter.setTermScaling(n * 8 + 4, 1, [2, 4 + sfo])

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

        elementtemplateOuter1 = mesh.createElementtemplate()
        elementtemplateOuter1.setElementShapeType(Element.SHAPE_TYPE_CUBE)
        result = elementtemplateOuter1.defineField(coordinates, -1, eftOuter1)
        elementtemplateOuter2 = mesh.createElementtemplate()
        elementtemplateOuter2.setElementShapeType(Element.SHAPE_TYPE_CUBE)
        result = elementtemplateOuter2.defineField(coordinates, -1, eftOuter2)

        cache = fm.createFieldcache()

        # create nodes
        radiansPerElementAround = 2.0 * math.pi / elementsCountAround
        wallThicknessMin = (0.5 - holeRadius)
        wallThicknessMax = math.sqrt(0.5) - holeRadius
        wallThicknessPerElement = wallThicknessMin / elementsCountThroughWall
        radius = holeRadius
        inner_x = []
        inner_d1 = []
        inner_d2 = []
        startRadians = math.pi * (-0.5 - elementsCount1 / elementsCountAround)
        for n1 in range(elementsCountAround):
            radiansAround = startRadians + n1 * radiansPerElementAround
            cosRadiansAround = math.cos(radiansAround)
            sinRadiansAround = math.sin(radiansAround)
            inner_x.append(
                (radius * cosRadiansAround, radius * sinRadiansAround))
            inner_d1.append(
                (radiansPerElementAround * radius * -sinRadiansAround,
                 radiansPerElementAround * radius * cosRadiansAround))
            #inner_d2.append((wallThicknessPerElement*cosRadiansAround, wallThicknessPerElement*sinRadiansAround))
            inner_d2.append((wallThicknessMin * cosRadiansAround,
                             wallThicknessMin * sinRadiansAround))
        outer_x = []
        outer_d1 = []
        outer_d2 = []
        mag = math.sqrt(elementsCount1 * elementsCount1 +
                        elementsCount2 * elementsCount2)
        outer_dx1_ds1 = 1.0 / elementsCount1
        outer_dx2_ds2 = 1.0 / elementsCount2
        #cornerScale1 = 1.0 / max(elementsCount1 + 1, elementsCount2 + 1)
        #cornerScale1 = 1.0 / min(2, max(elementsCount1, elementsCount2))
        cornerScale1 = 1.0 / max(elementsCount1 + elementsCountThroughWall,
                                 elementsCount2 + elementsCountThroughWall)
        sqrt05 = math.sqrt(0.5)
        wallThicknessMax = sqrt05 - holeRadius
        for n in range(elementsCount1):
            x = -0.5 + n / elementsCount1
            outer_x.append((x, -0.5))
            if n == 0:
                rx = x / sqrt05
                ry = -0.5 / sqrt05
                scale2 = wallThicknessMax
                outer_d1.append((-ry * cornerScale1, rx * cornerScale1))
                outer_d2.append((rx * scale2, ry * scale2))
            else:
                scale2 = wallThicknessMin
                outer_d1.append((outer_dx1_ds1, 0.0))
                outer_d2.append((0.0, -scale2))
        for n in range(elementsCount2):
            y = -0.5 + n / elementsCount2
            outer_x.append((0.5, y))
            if n == 0:
                rx = 0.5 / sqrt05
                ry = y / sqrt05
                scale2 = wallThicknessMax
                outer_d1.append((-ry * cornerScale1, rx * cornerScale1))
                outer_d2.append((rx * scale2, ry * scale2))
            else:
                scale2 = wallThicknessMin
                outer_d1.append((0.0, outer_dx2_ds2))
                outer_d2.append((scale2, 0.0))
        for n in range(elementsCount1):
            x = 0.5 - n / elementsCount1
            outer_x.append((x, 0.5))
            if n == 0:
                rx = x / sqrt05
                ry = 0.5 / sqrt05
                scale2 = wallThicknessMax
                outer_d1.append((-ry * cornerScale1, rx * cornerScale1))
                outer_d2.append((rx * scale2, ry * scale2))
            else:
                scale2 = wallThicknessMin
                outer_d1.append((-outer_dx1_ds1, 0.0))
                outer_d2.append((0.0, scale2))
        for n in range(elementsCount2):
            y = 0.5 - n / elementsCount2
            outer_x.append((-0.5, y))
            if n == 0:
                rx = -0.5 / sqrt05
                ry = y / sqrt05
                scale2 = wallThicknessMax
                outer_d1.append((-ry * cornerScale1, rx * cornerScale1))
                outer_d2.append((rx * scale2, ry * scale2))
            else:
                scale2 = wallThicknessMin
                outer_d1.append((0.0, -outer_dx2_ds2))
                outer_d2.append((-scale2, 0.0))

        nodeIdentifier = 1
        x = [0.0, 0.0, 0.0]
        dx_ds1 = [0.0, 0.0, 0.0]
        dx_ds2 = [0.0, 0.0, 0.0]
        dx_ds3 = [0.0, 0.0, 1.0 / elementsCount3]
        outer_dx_ds1 = [1.0 / elementsCount1, 0.0, 0.0]
        outer_dx_ds2 = [0.0, 1.0 / elementsCount2, 0.0]
        outer_dx_ds3 = [0.0, 0.0, 1.0 / elementsCount3]
        zero = [0.0, 0.0, 0.0]
        for n3 in range(elementsCount3 + 1):
            x[2] = n3 / elementsCount3
            # outer nodes
            for n1 in range(elementsCountAround):
                x[0] = outer_x[n1][0]
                x[1] = outer_x[n1][1]
                node = nodes.createNode(nodeIdentifier, nodetemplate)
                cache.setNode(node)
                coordinates.setNodeParameters(cache, -1,
                                              Node.VALUE_LABEL_VALUE, 1, x)
                coordinates.setNodeParameters(cache, -1,
                                              Node.VALUE_LABEL_D_DS1, 1,
                                              outer_dx_ds1)
                coordinates.setNodeParameters(cache, -1,
                                              Node.VALUE_LABEL_D_DS2, 1,
                                              outer_dx_ds2)
                #coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS1, 1, [ outer_d1[n1][0], outer_d1[n1][1], 0.0 ])
                #coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS2, 1, [ outer_d2[n1][0], outer_d2[n1][1], 0.0 ])
                coordinates.setNodeParameters(cache, -1,
                                              Node.VALUE_LABEL_D_DS3, 1,
                                              outer_dx_ds3)
                if useCrossDerivatives:
                    coordinates.setNodeParameters(cache, -1,
                                                  Node.VALUE_LABEL_D2_DS1DS2,
                                                  1, zero)
                    coordinates.setNodeParameters(cache, -1,
                                                  Node.VALUE_LABEL_D2_DS1DS3,
                                                  1, zero)
                    coordinates.setNodeParameters(cache, -1,
                                                  Node.VALUE_LABEL_D2_DS2DS3,
                                                  1, zero)
                    coordinates.setNodeParameters(
                        cache, -1, Node.VALUE_LABEL_D3_DS1DS2DS3, 1, zero)
                nodeIdentifier = nodeIdentifier + 1
            # inner nodes
            for n2 in range(elementsCountThroughWall):
                xir = (n2 + 1) / elementsCountThroughWall
                xi = 1.0 - xir
                for n1 in range(elementsCountAround):
                    node = nodes.createNode(nodeIdentifier, nodetemplate)
                    cache.setNode(node)
                    v = interpolateCubicHermite(inner_x[n1], inner_d2[n1],
                                                outer_x[n1], outer_d2[n1], xi)
                    x[0] = v[0]
                    x[1] = v[1]
                    dx_ds1[0] = xir * inner_d1[n1][0] + xi * outer_d1[n1][0]
                    dx_ds1[1] = xir * inner_d1[n1][1] + xi * outer_d1[n1][1]
                    d2 = interpolateCubicHermiteDerivative(
                        inner_x[n1], inner_d2[n1], outer_x[n1], outer_d2[n1],
                        xi)
                    dx_ds2[0] = -d2[
                        0] / elementsCountThroughWall  # *wallThicknessPerElement
                    dx_ds2[1] = -d2[
                        1] / elementsCountThroughWall  # *wallThicknessPerElement
                    coordinates.setNodeParameters(cache, -1,
                                                  Node.VALUE_LABEL_VALUE, 1, x)
                    coordinates.setNodeParameters(cache, -1,
                                                  Node.VALUE_LABEL_D_DS1, 1,
                                                  dx_ds1)
                    coordinates.setNodeParameters(cache, -1,
                                                  Node.VALUE_LABEL_D_DS2, 1,
                                                  dx_ds2)
                    coordinates.setNodeParameters(cache, -1,
                                                  Node.VALUE_LABEL_D_DS3, 1,
                                                  dx_ds3)
                    if useCrossDerivatives:
                        coordinates.setNodeParameters(
                            cache, -1, Node.VALUE_LABEL_D2_DS1DS2, 1, zero)
                        coordinates.setNodeParameters(
                            cache, -1, Node.VALUE_LABEL_D2_DS1DS3, 1, zero)
                        coordinates.setNodeParameters(
                            cache, -1, Node.VALUE_LABEL_D2_DS2DS3, 1, zero)
                        coordinates.setNodeParameters(
                            cache, -1, Node.VALUE_LABEL_D3_DS1DS2DS3, 1, zero)
                    nodeIdentifier = nodeIdentifier + 1

        # create elements
        elementIdentifier = 1
        no3 = (elementsCountThroughWall + 1) * elementsCountAround
        for e3 in range(elementsCount3):
            # first row general maps ds1, ds2 to dxi1, dxi2
            for e1 in range(elementsCountAround):
                en = (e1 + 1) % elementsCountAround
                onX = (e1 % (elementsCount1 + elementsCount2)) < elementsCount1
                elementtemplateOuter = elementtemplateOuter1 if onX else elementtemplateOuter2
                eftOuter = eftOuter1 if onX else eftOuter2
                element = mesh.createElement(elementIdentifier,
                                             elementtemplateOuter)
                bni11 = e3 * no3 + e1 + 1
                bni12 = e3 * no3 + en + 1
                bni21 = e3 * no3 + elementsCountAround + e1 + 1
                bni22 = e3 * no3 + elementsCountAround + en + 1
                nodeIdentifiers = [
                    bni11, bni12, bni21, bni22, bni11 + no3, bni12 + no3,
                    bni21 + no3, bni22 + no3
                ]
                result = element.setNodesByIdentifier(eftOuter,
                                                      nodeIdentifiers)
                rev = e1 >= (elementsCount1 + elementsCount2)
                one = -1.0 if rev else 1.0
                vx = one if onX else 0.0
                vy = 0.0 if onX else one
                scaleFactors = [
                    -1.0, 4.0, vx, vy, -outer_d2[e1][0] / outer_dx_ds1[0] /
                    elementsCountThroughWall, -outer_d2[e1][1] /
                    outer_dx_ds2[1] / elementsCountThroughWall, vx, vy,
                    -outer_d2[en][0] / outer_dx_ds1[0] /
                    elementsCountThroughWall, -outer_d2[en][1] /
                    outer_dx_ds2[1] / elementsCountThroughWall
                ]
                element.setScaleFactors(eftOuter, scaleFactors)
                elementIdentifier = elementIdentifier + 1

            # remaining rows
            for e2 in range(1, elementsCountThroughWall):
                for e1 in range(elementsCountAround):
                    element = mesh.createElement(elementIdentifier,
                                                 elementtemplate)
                    bni11 = e3 * no3 + e2 * elementsCountAround + e1 + 1
                    bni12 = e3 * no3 + e2 * elementsCountAround + (
                        e1 + 1) % elementsCountAround + 1
                    bni21 = e3 * no3 + (e2 + 1) * elementsCountAround + e1 + 1
                    bni22 = e3 * no3 + (e2 + 1) * elementsCountAround + (
                        e1 + 1) % elementsCountAround + 1
                    nodeIdentifiers = [
                        bni11, bni12, bni21, bni22, bni11 + no3, bni12 + no3,
                        bni21 + no3, bni22 + no3
                    ]
                    result = element.setNodesByIdentifier(eft, nodeIdentifiers)
                    elementIdentifier = elementIdentifier + 1

        fm.endChange()
Exemple #2
0
    def generateMesh(region, options):
        """
        :param region: Zinc region to define model in. Must be empty.
        :param options: Dict containing options. See getDefaultOptions().
        :return: None
        """
        elementsCountAlong = options['Number of elements along']
        elementsCountAcross = options['Number of elements across']
        wallThickness = [
            options['Wall thickness left'], options['Wall thickness right']
        ]
        flangeLength = options['Flange length']
        bulgeRadius = options['Bulge radius']
        useCrossDerivatives = options['Use cross derivatives']

        fm = region.getFieldmodule()
        fm.beginChange()
        coordinates = fm.createFieldFiniteElement(3)
        coordinates.setName('coordinates')
        coordinates.setManaged(True)
        coordinates.setTypeCoordinate(True)
        coordinates.setCoordinateSystemType(
            Field.COORDINATE_SYSTEM_TYPE_RECTANGULAR_CARTESIAN)
        coordinates.setComponentName(1, 'x')
        coordinates.setComponentName(2, 'y')
        coordinates.setComponentName(3, 'z')

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

        mesh = fm.findMeshByDimension(3)

        tricubichermite = eftfactory_tricubichermite(mesh, useCrossDerivatives)
        eft = tricubichermite.createEftBasic()
        eftOuter = tricubichermite.createEftTubeSeptumOuter()
        eftInner1 = tricubichermite.createEftTubeSeptumInner1()
        eftInner2 = tricubichermite.createEftTubeSeptumInner2()

        elementtemplate = mesh.createElementtemplate()
        elementtemplate.setElementShapeType(Element.SHAPE_TYPE_CUBE)
        result = elementtemplate.defineField(coordinates, -1, eft)
        elementtemplateOuter = mesh.createElementtemplate()
        elementtemplateOuter.setElementShapeType(Element.SHAPE_TYPE_CUBE)
        result = elementtemplateOuter.defineField(coordinates, -1, eftOuter)
        elementtemplateInner1 = mesh.createElementtemplate()
        elementtemplateInner1.setElementShapeType(Element.SHAPE_TYPE_CUBE)
        result = elementtemplateInner1.defineField(coordinates, -1, eftInner1)
        elementtemplateInner2 = mesh.createElementtemplate()
        elementtemplateInner2.setElementShapeType(Element.SHAPE_TYPE_CUBE)
        result = elementtemplateInner2.defineField(coordinates, -1, eftInner2)

        cache = fm.createFieldcache()

        # create nodes
        nodeIdentifier = 1
        #radiansPerElementAcross = math.pi/elementsCountAcross
        bevelAngle = math.pi / 2
        sinBevelAngle = math.sin(bevelAngle)
        cosBevelAngle = math.cos(bevelAngle)

        x = [0.0, 0.0, 0.0]
        dx_ds1 = [0.0, 0.0, 0.0]
        dx_ds2 = [0.0, 0.0, 1.0 / elementsCountAlong]
        dx_ds3 = [0.0, 0.0, 0.0]
        zero = [0.0, 0.0, 0.0]
        wallThicknessSeptum = wallThickness[0]
        #wallThicknessMin = min(wallThickness)
        wallThicknessMax = max(wallThickness)
        for n3 in range(2):
            sign = -1.0 if (n3 == 0) else 1.0
            radiusY = 0.5 * wallThicknessSeptum
            radiusX = 0.5 - wallThicknessMax
            for n2 in range(elementsCountAlong + 1):
                x[2] = n2 / elementsCountAlong
                for n1 in range(elementsCountAcross + 3):
                    if (n1 == 0) or (n1 == (elementsCountAcross + 2)):
                        flip = -1.0 if (n1 == 0) else 1.0
                        x[0] = radiusX + wallThickness[n3]
                        if n1 == 0:
                            x[0] = -x[0]
                        x[1] = -sign * (radiusY + flangeLength)
                        #if wallThickness[0] > wallThickness[1]:
                        #    x[1] -= flangeLength
                        #elif wallThickness[0] < wallThickness[1]:
                        #    x[1] += flangeLength
                        dx_ds1[0] = 0.0
                        dx_ds1[1] = 2.0 * flip * (radiusY + flangeLength)
                        dx_ds3[0] = flip * wallThickness[n3]
                        dx_ds3[1] = 0.0
                    elif (n1 == 1) or (n1 == (elementsCountAcross + 1)):
                        flip = -1.0 if (n1 == 1) else 1.0
                        radius = radiusX
                        x[0] = flip * radius
                        x[1] = -sign * (radiusY + flangeLength)
                        #dx_ds1[0] = -sign*2.0*radiusX/elementsCountAcross*cosBevelAngle
                        #dx_ds1[1] = 2.0*radiusX/elementsCountAcross*sinBevelAngle
                        mag1x = radiusX / elementsCountAcross
                        mag1y = flangeLength
                        #mag1min = radiusX/elementsCountAcross + flangeLength + math.sqrt(mag1x*mag1x + mag1y*mag1y)
                        mag1min = 2.0 * math.sqrt(mag1x * mag1x +
                                                  mag1y * mag1y)
                        mag1max = 2.0 * (radiusY + flangeLength)
                        mag1 = mag1min if (mag1min < mag1max) else mag1max
                        dx_ds1[0] = -sign * mag1 * cosBevelAngle
                        dx_ds1[1] = mag1 * sinBevelAngle
                        #if ((n3 == 1) and (wallThickness[0] > wallThickness[1])) or \
                        #    ((n3 == 0) and (wallThickness[0] < wallThickness[1])):
                        dx_ds3[0] = wallThickness[n3]
                        dx_ds3[1] = 0.0
                        #else:
                        #    dx_ds3[0] = wallThickness[n3]*sinBevelAngle
                        #    dx_ds3[1] = sign*wallThickness[n3]*cosBevelAngle
                        if n1 == 1:
                            dx_ds1[1] = -dx_ds1[1]
                            dx_ds3[0] = -dx_ds3[0]
                    else:
                        f1 = (n1 - 1) / elementsCountAcross
                        f2 = 1.0 - f1
                        x[0] = (f1 - f2) * radiusX
                        x[1] = -sign * radiusY
                        dx_ds1[0] = 2.0 * radiusX / elementsCountAcross
                        dx_ds1[1] = 0.0
                        dx_ds3[0] = 0.0
                        dx_ds3[1] = -wallThicknessSeptum
                    node = nodes.createNode(nodeIdentifier, nodetemplate)
                    cache.setNode(node)
                    coordinates.setNodeParameters(cache, -1,
                                                  Node.VALUE_LABEL_VALUE, 1, x)
                    coordinates.setNodeParameters(cache, -1,
                                                  Node.VALUE_LABEL_D_DS1, 1,
                                                  dx_ds1)
                    coordinates.setNodeParameters(cache, -1,
                                                  Node.VALUE_LABEL_D_DS2, 1,
                                                  dx_ds2)
                    coordinates.setNodeParameters(cache, -1,
                                                  Node.VALUE_LABEL_D_DS3, 1,
                                                  dx_ds3)
                    if useCrossDerivatives:
                        coordinates.setNodeParameters(
                            cache, -1, Node.VALUE_LABEL_D2_DS1DS2, 1, zero)
                        coordinates.setNodeParameters(
                            cache, -1, Node.VALUE_LABEL_D2_DS1DS3, 1, zero)
                        coordinates.setNodeParameters(
                            cache, -1, Node.VALUE_LABEL_D2_DS2DS3, 1, zero)
                        coordinates.setNodeParameters(
                            cache, -1, Node.VALUE_LABEL_D3_DS1DS2DS3, 1, zero)
                    nodeIdentifier = nodeIdentifier + 1

        rimCrossAngle = math.pi / 4
        sinRimCrossAngle = math.sin(rimCrossAngle)
        cosRimCrossAngle = math.cos(rimCrossAngle)
        scaleFactorsOuter = [
            cosRimCrossAngle, sinRimCrossAngle, cosRimCrossAngle,
            -sinRimCrossAngle, cosRimCrossAngle, sinRimCrossAngle,
            cosRimCrossAngle, -sinRimCrossAngle
        ]
        scaleFactorsInner1 = [
            -1.0, 4.0, cosRimCrossAngle, sinRimCrossAngle, cosRimCrossAngle,
            sinRimCrossAngle, cosRimCrossAngle, -sinRimCrossAngle,
            cosRimCrossAngle, -sinRimCrossAngle
        ]
        scaleFactorsInner2 = [
            -1.0, 4.0, cosRimCrossAngle, -sinRimCrossAngle, cosRimCrossAngle,
            -sinRimCrossAngle, cosRimCrossAngle, sinRimCrossAngle,
            cosRimCrossAngle, sinRimCrossAngle
        ]

        # create elements
        elementIdentifier = 1
        rno = elementsCountAcross + 3
        wno = (elementsCountAlong + 1) * rno
        for e2 in range(elementsCountAlong):
            bn = e2 * rno
            element = mesh.createElement(elementIdentifier,
                                         elementtemplateOuter)
            bni11 = bn + 2
            bni12 = bn + wno + 2
            bni21 = bn + rno + 2
            bni22 = bn + wno + rno + 2
            nodeIdentifiers = [
                bni11, bni12, bni21, bni22, bni11 - 1, bni12 - 1, bni21 - 1,
                bni22 - 1
            ]
            result = element.setNodesByIdentifier(eftOuter, nodeIdentifiers)
            #print(result, 'element', elementIdentifier, nodeIdentifiers)
            element.setScaleFactors(eftOuter, scaleFactorsOuter)
            elementIdentifier = elementIdentifier + 1

            element = mesh.createElement(elementIdentifier,
                                         elementtemplateInner1)
            bni11 = bn + 2
            bni12 = bn + 3
            bni21 = bn + rno + 2
            bni22 = bn + rno + 3
            nodeIdentifiers = [
                bni11, bni12, bni21, bni22, bni11 + wno, bni12 + wno,
                bni21 + wno, bni22 + wno
            ]
            result = element.setNodesByIdentifier(eftInner1, nodeIdentifiers)
            #print(result, 'element', elementIdentifier, 'nodes', nodeIdentifiers)
            element.setScaleFactors(eftInner1, scaleFactorsInner1)
            #print(result, 'element', elementIdentifier, 'scale factors', scaleFactorsInner1)
            elementIdentifier = elementIdentifier + 1

            for e1 in range(elementsCountAcross - 2):
                element = mesh.createElement(elementIdentifier,
                                             elementtemplate)
                bni11 = bn + e1 + 3
                bni12 = bn + e1 + 4
                bni21 = bn + rno + e1 + 3
                bni22 = bn + rno + e1 + 4
                nodeIdentifiers = [
                    bni11, bni12, bni21, bni22, bni11 + wno, bni12 + wno,
                    bni21 + wno, bni22 + wno
                ]
                result = element.setNodesByIdentifier(eft, nodeIdentifiers)
                #print(result, 'element', elementIdentifier, 'nodes', nodeIdentifiers)
                elementIdentifier = elementIdentifier + 1

            element = mesh.createElement(elementIdentifier,
                                         elementtemplateInner2)
            bni11 = bn + rno - 2
            bni12 = bn + rno - 1
            bni21 = bn + 2 * rno - 2
            bni22 = bn + 2 * rno - 1
            nodeIdentifiers = [
                bni11, bni12, bni21, bni22, bni11 + wno, bni12 + wno,
                bni21 + wno, bni22 + wno
            ]
            result = element.setNodesByIdentifier(eftInner2, nodeIdentifiers)
            #print(result, 'element', elementIdentifier, 'nodes', nodeIdentifiers)
            element.setScaleFactors(eftInner2, scaleFactorsInner2)
            #print(result, 'element', elementIdentifier, 'scale factors', scaleFactorsInner1)
            elementIdentifier = elementIdentifier + 1

            element = mesh.createElement(elementIdentifier,
                                         elementtemplateOuter)
            bni11 = bn + wno + rno - 1
            bni12 = bn + rno - 1
            bni21 = bn + wno + 2 * rno - 1
            bni22 = bn + 2 * rno - 1
            nodeIdentifiers = [
                bni11, bni12, bni21, bni22, bni11 + 1, bni12 + 1, bni21 + 1,
                bni22 + 1
            ]
            result = element.setNodesByIdentifier(eftOuter, nodeIdentifiers)
            #print(result, 'element', elementIdentifier, nodeIdentifiers)
            element.setScaleFactors(eftOuter, scaleFactorsOuter)
            elementIdentifier = elementIdentifier + 1

        if bulgeRadius != 0.0:
            # cylindrical polar coordinates:
            # r = y - bulgeRadius
            # theta = -x / bulgeRadius
            # z = z
            yxzCoordinates = fm.createFieldComponent(coordinates, [2, 1, 3])
            scale = fm.createFieldConstant([1.0, -1.0 / bulgeRadius, 1.0])
            scaleCoordinates = fm.createFieldMultiply(yxzCoordinates, scale)
            offset = fm.createFieldConstant([-bulgeRadius, 0.0, 0.0])
            polarCoordinates = fm.createFieldAdd(scaleCoordinates, offset)
            polarCoordinates.setCoordinateSystemType(
                Field.COORDINATE_SYSTEM_TYPE_CYLINDRICAL_POLAR)
            rcCoordinates = fm.createFieldCoordinateTransformation(
                polarCoordinates)
            rcCoordinates.setCoordinateSystemType(
                Field.COORDINATE_SYSTEM_TYPE_RECTANGULAR_CARTESIAN)
            newyxzCoordinates = fm.createFieldSubtract(rcCoordinates, offset)
            newCoordinates = fm.createFieldComponent(newyxzCoordinates,
                                                     [2, 1, 3])
            fieldassignment = coordinates.createFieldassignment(newCoordinates)
            result = fieldassignment.assign()

        fm.endChange()
Exemple #3
0
    def generateMesh(region, options):
        """
        :param region: Zinc region to define model in. Must be empty.
        :param options: Dict containing options. See getDefaultOptions().
        :return: None
        """
        lvWallThickness = options['LV wall thickness']
        baseHeight = options['Base height']
        baseThickness = options['Base thickness']
        lvOutletRadius = options['LV outlet inner diameter'] * 0.5
        lvOutletWallThickness = options['LV outlet wall thickness']
        rvOutletRadius = options['RV outlet inner diameter'] * 0.5
        rvOutletWallThickness = options['RV outlet wall thickness']
        outletElementLength = options['Outlet element length']
        useCrossDerivatives = False

        # generate default heart ventricles model to add base plane to
        heartVentriclesOptions = MeshType_3d_heartventricles2.getDefaultOptions(
        )
        for key in [
                'LV wall thickness', 'LV wall thickness ratio apex',
                'LV wall thickness ratio base', 'RV free wall thickness',
                'RV width', 'Length ratio', 'Element length ratio equator/apex'
        ]:
            heartVentriclesOptions[key] = options[key]
        MeshType_3d_heartventricles2.generateMesh(region,
                                                  heartVentriclesOptions)

        fm = region.getFieldmodule()
        fm.beginChange()
        # find the coordinates field created for the sphere shell
        coordinates = fm.findFieldByName('coordinates').castFiniteElement()

        nodes = fm.findNodesetByFieldDomainType(Field.DOMAIN_TYPE_NODES)
        nodetemplateFull = nodes.createNodetemplate()
        nodetemplateFull.defineField(coordinates)
        nodetemplateFull.setValueNumberOfVersions(coordinates, -1,
                                                  Node.VALUE_LABEL_VALUE, 1)
        nodetemplateFull.setValueNumberOfVersions(coordinates, -1,
                                                  Node.VALUE_LABEL_D_DS1, 1)
        nodetemplateFull.setValueNumberOfVersions(coordinates, -1,
                                                  Node.VALUE_LABEL_D_DS2, 1)
        nodetemplateFull.setValueNumberOfVersions(coordinates, -1,
                                                  Node.VALUE_LABEL_D_DS3, 1)
        # nodes used only in bicubic-linear elements do not have D_DS3 parameters
        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)

        mesh = fm.findMeshByDimension(3)

        tricubichermite = eftfactory_tricubichermite(mesh, useCrossDerivatives)
        eft = tricubichermite.createEftBasic()

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

        cache = fm.createFieldcache()

        # create nodes
        nodeIdentifier = startNodeIdentifier = getMaximumNodeIdentifier(
            nodes) + 1

        outletJoinRadians = math.pi  # *0.8
        cosOutletJoinRadians = math.cos(outletJoinRadians)
        sinOutletJoinRadians = math.sin(outletJoinRadians)
        lvOutletOffset = 0.5 - lvWallThickness - lvOutletRadius
        #lvOutletCentreX = cosOutletJoinRadians*lvOutletOffset
        #lvOutletCentreY = sinOutletJoinRadians*lvOutletOffset
        lvOutletCentreX = 0.0
        lvOutletCentreY = lvOutletOffset

        # LV outlet - for bicubic-linear tube connection
        elementsCountAround = 6
        radiansPerElementAround = 2.0 * math.pi / elementsCountAround
        x = [0.0, 0.0, baseHeight + baseThickness]
        dx_ds1 = [0.0, 0.0, 0.0]
        dx_ds2 = [0.0, 0.0, outletElementLength]
        dx_ds3 = [0.0, 0.0, 0.0]
        zero = [0.0, 0.0, 0.0]
        for n3 in range(2):
            radius = lvOutletRadius + lvOutletWallThickness * n3
            for n1 in range(elementsCountAround):
                radiansAround = outletJoinRadians - math.pi + n1 * radiansPerElementAround
                cosRadiansAround = math.cos(radiansAround)
                sinRadiansAround = math.sin(radiansAround)
                x[0] = lvOutletCentreX + radius * cosRadiansAround
                x[1] = lvOutletCentreY + radius * sinRadiansAround
                dx_ds1[
                    0] = radiansPerElementAround * radius * -sinRadiansAround
                dx_ds1[1] = radiansPerElementAround * radius * cosRadiansAround
                nodetemplate = nodetemplateLinearS3 if (
                    n3 == 0) else nodetemplateFull
                node = nodes.createNode(nodeIdentifier, nodetemplate)
                cache.setNode(node)
                coordinates.setNodeParameters(cache, -1,
                                              Node.VALUE_LABEL_VALUE, 1, x)
                coordinates.setNodeParameters(cache, -1,
                                              Node.VALUE_LABEL_D_DS1, 1,
                                              dx_ds1)
                coordinates.setNodeParameters(cache, -1,
                                              Node.VALUE_LABEL_D_DS2, 1,
                                              dx_ds2)
                if nodetemplate is nodetemplateFull:
                    dx_ds3[0] = lvOutletWallThickness * cosRadiansAround
                    dx_ds3[1] = lvOutletWallThickness * sinRadiansAround
                    coordinates.setNodeParameters(cache, -1,
                                                  Node.VALUE_LABEL_D_DS3, 1,
                                                  dx_ds3)
                nodeIdentifier += 1

        # RV outlet - for bicubic-linear tube connection
        elementsCountAround = 6
        radiansPerElementAround = 2.0 * math.pi / elementsCountAround
        rvOutletOffset = lvOutletRadius + lvOutletWallThickness + rvOutletWallThickness + rvOutletRadius
        rvOutletCentreX = lvOutletCentreX + cosOutletJoinRadians * rvOutletOffset
        rvOutletCentreY = lvOutletCentreY + sinOutletJoinRadians * rvOutletOffset

        x = [0.0, 0.0, baseHeight + baseThickness]
        dx_ds1 = [0.0, 0.0, 0.0]
        dx_ds2 = [0.0, 0.0, outletElementLength]
        dx_ds3 = [0.0, 0.0, 0.0]
        for n3 in range(2):
            radius = rvOutletRadius + rvOutletWallThickness * n3
            for n1 in range(elementsCountAround):
                if (n3 == 1) and (n1 == 0):
                    continue  # node is common with LV outlet
                radiansAround = outletJoinRadians - math.pi + n1 * radiansPerElementAround
                cosRadiansAround = math.cos(radiansAround)
                sinRadiansAround = math.sin(radiansAround)
                x[0] = rvOutletCentreX + radius * cosRadiansAround
                x[1] = rvOutletCentreY + radius * sinRadiansAround
                dx_ds1[
                    0] = radiansPerElementAround * radius * -sinRadiansAround
                dx_ds1[1] = radiansPerElementAround * radius * cosRadiansAround
                node = nodes.createNode(nodeIdentifier, nodetemplateLinearS3)
                cache.setNode(node)
                coordinates.setNodeParameters(cache, -1,
                                              Node.VALUE_LABEL_VALUE, 1, x)
                coordinates.setNodeParameters(cache, -1,
                                              Node.VALUE_LABEL_D_DS1, 1,
                                              dx_ds1)
                coordinates.setNodeParameters(cache, -1,
                                              Node.VALUE_LABEL_D_DS2, 1,
                                              dx_ds2)
                nodeIdentifier += 1

        # create elements
        elementIdentifier = startElementIdentifier = getMaximumElementIdentifier(
            mesh) + 1

        rvNids = [[104, 105, 110, 111], [105, 106, 111, 112],
                  [106, 107, 112, 113], [107, 108, 113, 114],
                  [116, 117, 113, 122], [117, 118, 122, 123],
                  [118, 119, 123, 124], [119, 120, 124, 125]]

        scalefactors5hanging = [-1.0, 0.5, 0.25, 0.125, 0.75]

        i = -1
        for eid in range(55, 59):
            i += 1
            if i < 2:
                continue
            origElement = mesh.findElementByIdentifier(eid)
            origEft = origElement.getElementfieldtemplate(coordinates, -1)
            origNodeIdentifiers = getElementNodeIdentifiers(
                origElement, origEft)

            # first and last elements have scaling to use and fix (GRC to do)
            if False:  #eid == 55:
                eft1 = origEft
            else:
                eft1 = tricubichermite.createEftBasic()
            if False:  #eid == 58:
                eft2 = origEft
            else:
                eft2 = tricubichermite.createEftBasic()

            # general scale factors 1 -> 1, 102 -> 1/2, 104 -> 1/4, 108 -> 1/8, 304 -> 3/4
            setEftScaleFactorIds(eft1, [1, 102, 104, 108, 304], [])
            setEftScaleFactorIds(eft2, [1, 102, 104, 108, 304], [])

            tricubichermite.setEftMidsideXi1HangingNode(
                eft1, 2, 1, 1, 2, [1, 2, 3, 4, 5])
            tricubichermite.setEftMidsideXi1HangingNode(
                eft1, 6, 5, 5, 6, [1, 2, 3, 4, 5])
            tricubichermite.setEftLinearDerivativeXi3(eft1, 3, 7, 3, 7, 1)
            tricubichermite.setEftLinearDerivativeXi3(eft1, 4, 8, 4, 8, 1)

            tricubichermite.setEftMidsideXi1HangingNode(
                eft2, 1, 2, 1, 2, [1, 2, 3, 4, 5])
            tricubichermite.setEftMidsideXi1HangingNode(
                eft2, 5, 6, 5, 6, [1, 2, 3, 4, 5])
            tricubichermite.setEftLinearDerivativeXi3(eft2, 3, 7, 3, 7, 1)
            tricubichermite.setEftLinearDerivativeXi3(eft2, 4, 8, 4, 8, 1)

            elementtemplate1 = mesh.createElementtemplate()
            elementtemplate1.setElementShapeType(Element.SHAPE_TYPE_CUBE)
            result = elementtemplate1.defineField(coordinates, -1, eft1)
            element = mesh.createElement(elementIdentifier, elementtemplate1)
            nids = rvNids[i * 2]
            nodeIdentifiers1 = [
                origNodeIdentifiers[2], origNodeIdentifiers[3], nids[0],
                nids[1], origNodeIdentifiers[6], origNodeIdentifiers[7],
                nids[2], nids[3]
            ]
            result2 = element.setNodesByIdentifier(eft1, nodeIdentifiers1)
            result3 = element.setScaleFactors(eft1, scalefactors5hanging)
            print('create hanging element 1', elementIdentifier, result2,
                  result3, nodeIdentifiers1, scalefactors5hanging)
            elementIdentifier += 1

            elementtemplate2 = mesh.createElementtemplate()
            elementtemplate2.setElementShapeType(Element.SHAPE_TYPE_CUBE)
            result = elementtemplate2.defineField(coordinates, -1, eft2)
            element = mesh.createElement(elementIdentifier, elementtemplate2)
            nids = rvNids[i * 2 + 1]
            nodeIdentifiers2 = [
                origNodeIdentifiers[2], origNodeIdentifiers[3], nids[0],
                nids[1], origNodeIdentifiers[6], origNodeIdentifiers[7],
                nids[2], nids[3]
            ]
            result2 = element.setNodesByIdentifier(eft2, nodeIdentifiers2)
            result3 = element.setScaleFactors(eft2, scalefactors5hanging)
            print('create hanging element 2', elementIdentifier, result2,
                  result3, nodeIdentifiers2, scalefactors5hanging)
            elementIdentifier += 1

        fm.endChange()
    def generateMesh(region, options):
        """
        :param region: Zinc region to define model in. Must be empty.
        :param options: Dict containing options. See getDefaultOptions().
        :return: None
        """
        elementsCountUp = options['Number of elements up']
        elementsCountAround = options['Number of elements around']
        elementsCountThroughWall = options['Number of elements through wall']
        useCrossDerivatives = options['Use cross derivatives']
        excludeBottomRows = options['Exclude bottom rows']
        excludeTopRows = options['Exclude top rows']
        wallThickness = options['Wall thickness']
        wallThicknessRatioApex = options['Wall thickness ratio apex']
        lengthRatio = options['Length ratio']
        elementLengthRatioEquatorApex = options[
            'Element length ratio equator/apex']

        fm = region.getFieldmodule()
        fm.beginChange()
        coordinates = fm.createFieldFiniteElement(3)
        coordinates.setName('coordinates')
        coordinates.setManaged(True)
        coordinates.setTypeCoordinate(True)
        coordinates.setCoordinateSystemType(
            Field.COORDINATE_SYSTEM_TYPE_RECTANGULAR_CARTESIAN)
        coordinates.setComponentName(1, 'x')
        coordinates.setComponentName(2, 'y')
        coordinates.setComponentName(3, 'z')

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

        mesh = fm.findMeshByDimension(3)

        tricubichermite = eftfactory_tricubichermite(mesh, useCrossDerivatives)
        eft = tricubichermite.createEftBasic()

        tricubicHermiteBasis = fm.createElementbasis(
            3, Elementbasis.FUNCTION_TYPE_CUBIC_HERMITE)

        # Apex1: collapsed on xi2 = 0
        eftApex1 = mesh.createElementfieldtemplate(tricubicHermiteBasis)
        eftApex1.setNumberOfLocalNodes(6)
        eftApex1.setNumberOfLocalScaleFactors(13)
        # GRC: allow scale factor identifier for global -1.0 to be prescribed
        eftApex1.setScaleFactorType(
            1, Elementfieldtemplate.SCALE_FACTOR_TYPE_GLOBAL_GENERAL)
        eftApex1.setScaleFactorIdentifier(1, 1)
        for layer in range(2):
            so = layer * 6 + 1
            no = layer * 3
            fo = layer * 32
            for s in range(6):
                si = so + s + 1
                # 3 scale factors per node: cos(theta), sin(theta), arc angle radians
                sid = (s // 3) * 100 + (
                    s % 3) + 1  # add 100 for different 'version'
                eftApex1.setScaleFactorType(
                    si, Elementfieldtemplate.SCALE_FACTOR_TYPE_NODE_GENERAL)
                eftApex1.setScaleFactorIdentifier(si, sid)

            # basis node 1 -> local node 1
            ln = no + 1
            eftApex1.setTermNodeParameter(fo + 1, 1, ln,
                                          Node.VALUE_LABEL_VALUE, 1)
            # 0 terms = zero parameter for d/dxi1 basis
            eftApex1.setFunctionNumberOfTerms(fo + 2, 0)
            # 2 terms for d/dxi2 via general linear map:
            eftApex1.setFunctionNumberOfTerms(fo + 3, 2)
            eftApex1.setTermNodeParameter(fo + 3, 1, ln,
                                          Node.VALUE_LABEL_D_DS1, 1)
            eftApex1.setTermScaling(fo + 3, 1, [so + 1])
            eftApex1.setTermNodeParameter(fo + 3, 2, ln,
                                          Node.VALUE_LABEL_D_DS2, 1)
            eftApex1.setTermScaling(fo + 3, 2, [so + 2])
            # 2 terms for cross derivative 1 2 to correct circular apex: -sin(theta).phi, cos(theta).phi
            eftApex1.setFunctionNumberOfTerms(fo + 4, 2)
            eftApex1.setTermNodeParameter(fo + 4, 1, ln,
                                          Node.VALUE_LABEL_D_DS1, 1)
            eftApex1.setTermScaling(fo + 4, 1, [so + 2, so + 3])
            eftApex1.setTermNodeParameter(fo + 4, 2, ln,
                                          Node.VALUE_LABEL_D_DS2, 1)
            eftApex1.setTermScaling(fo + 4, 2, [1, so + 1, so + 3])
            eftApex1.setTermNodeParameter(fo + 5, 1, ln,
                                          Node.VALUE_LABEL_D_DS3, 1)
            # 0 terms = zero parameter for cross derivative 1 3, 2 3 and 1 2 3
            eftApex1.setFunctionNumberOfTerms(fo + 6, 0)
            eftApex1.setFunctionNumberOfTerms(fo + 7, 0)
            eftApex1.setFunctionNumberOfTerms(fo + 8, 0)

            # basis node 2 -> local node 1
            eftApex1.setTermNodeParameter(fo + 9, 1, ln,
                                          Node.VALUE_LABEL_VALUE, 1)
            # 0 terms = zero parameter for d/dxi1 basis
            eftApex1.setFunctionNumberOfTerms(fo + 10, 0)
            # 2 terms for d/dxi2 via general linear map:
            eftApex1.setFunctionNumberOfTerms(fo + 11, 2)
            eftApex1.setTermNodeParameter(fo + 11, 1, ln,
                                          Node.VALUE_LABEL_D_DS1, 1)
            eftApex1.setTermScaling(fo + 11, 1, [so + 4])
            eftApex1.setTermNodeParameter(fo + 11, 2, ln,
                                          Node.VALUE_LABEL_D_DS2, 1)
            eftApex1.setTermScaling(fo + 11, 2, [so + 5])
            # 2 terms for cross derivative 1 2 to correct circular apex: -sin(theta).phi, cos(theta).phi
            eftApex1.setFunctionNumberOfTerms(fo + 12, 2)
            eftApex1.setTermNodeParameter(fo + 12, 1, ln,
                                          Node.VALUE_LABEL_D_DS1, 1)
            eftApex1.setTermScaling(fo + 12, 1, [so + 5, so + 6])
            eftApex1.setTermNodeParameter(fo + 12, 2, ln,
                                          Node.VALUE_LABEL_D_DS2, 1)
            eftApex1.setTermScaling(fo + 12, 2, [1, so + 4, so + 6])
            eftApex1.setTermNodeParameter(fo + 13, 1, ln,
                                          Node.VALUE_LABEL_D_DS3, 1)
            # 0 terms = zero parameter for cross derivative 1 3, 2 3 and 1 2 3
            eftApex1.setFunctionNumberOfTerms(fo + 14, 0)
            eftApex1.setFunctionNumberOfTerms(fo + 15, 0)
            eftApex1.setFunctionNumberOfTerms(fo + 16, 0)

            # basis nodes 3, 4 -> regular local nodes 2, 3
            for bn in range(2, 4):
                fo2 = fo + bn * 8
                ni = no + bn
                eftApex1.setTermNodeParameter(fo2 + 1, 1, ni,
                                              Node.VALUE_LABEL_VALUE, 1)
                eftApex1.setTermNodeParameter(fo2 + 2, 1, ni,
                                              Node.VALUE_LABEL_D_DS1, 1)
                eftApex1.setTermNodeParameter(fo2 + 3, 1, ni,
                                              Node.VALUE_LABEL_D_DS2, 1)
                eftApex1.setTermNodeParameter(fo2 + 5, 1, ni,
                                              Node.VALUE_LABEL_D_DS3, 1)
                if useCrossDerivatives:
                    eftApex1.setTermNodeParameter(fo2 + 4, 1, ni,
                                                  Node.VALUE_LABEL_D2_DS1DS2,
                                                  1)
                    eftApex1.setTermNodeParameter(fo2 + 6, 1, ni,
                                                  Node.VALUE_LABEL_D2_DS1DS3,
                                                  1)
                    eftApex1.setTermNodeParameter(fo2 + 7, 1, ni,
                                                  Node.VALUE_LABEL_D2_DS2DS3,
                                                  1)
                    eftApex1.setTermNodeParameter(
                        fo2 + 8, 1, ni, Node.VALUE_LABEL_D3_DS1DS2DS3, 1)
                else:
                    eftApex1.setFunctionNumberOfTerms(fo2 + 4, 0)
                    eftApex1.setFunctionNumberOfTerms(fo2 + 6, 0)
                    eftApex1.setFunctionNumberOfTerms(fo2 + 7, 0)
                    eftApex1.setFunctionNumberOfTerms(fo2 + 8, 0)

        # Apex2: collapsed on xi2 = 1
        eftApex2 = mesh.createElementfieldtemplate(tricubicHermiteBasis)
        eftApex2.setNumberOfLocalNodes(6)
        eftApex2.setNumberOfLocalScaleFactors(8)
        eftApex2.setNumberOfLocalScaleFactors(13)
        # GRC: allow scale factor identifier for global -1.0 to be prescribed
        eftApex2.setScaleFactorType(
            1, Elementfieldtemplate.SCALE_FACTOR_TYPE_GLOBAL_GENERAL)
        eftApex2.setScaleFactorIdentifier(1, 1)
        for layer in range(2):
            so = layer * 6 + 1
            no = layer * 3
            fo = layer * 32
            for s in range(6):
                si = so + s + 1
                # 3 scale factors per node: cos(theta), sin(theta), arc angle radians
                sid = (s // 3) * 100 + (
                    s % 3) + 1  # add 100 for different 'version'
                eftApex2.setScaleFactorType(
                    si, Elementfieldtemplate.SCALE_FACTOR_TYPE_NODE_GENERAL)
                eftApex2.setScaleFactorIdentifier(si, sid)

            # basis nodes 1, 2 -> regular local nodes 1, 2 (for each layer)
            for bn in range(2):
                fo2 = fo + bn * 8
                ni = no + bn + 1
                eftApex2.setTermNodeParameter(fo2 + 1, 1, ni,
                                              Node.VALUE_LABEL_VALUE, 1)
                eftApex2.setTermNodeParameter(fo2 + 2, 1, ni,
                                              Node.VALUE_LABEL_D_DS1, 1)
                eftApex2.setTermNodeParameter(fo2 + 3, 1, ni,
                                              Node.VALUE_LABEL_D_DS2, 1)
                eftApex2.setTermNodeParameter(fo2 + 5, 1, ni,
                                              Node.VALUE_LABEL_D_DS3, 1)
                if useCrossDerivatives:
                    eftApex2.setTermNodeParameter(fo2 + 4, 1, ni,
                                                  Node.VALUE_LABEL_D2_DS1DS2,
                                                  1)
                    eftApex2.setTermNodeParameter(fo2 + 6, 1, ni,
                                                  Node.VALUE_LABEL_D2_DS1DS3,
                                                  1)
                    eftApex2.setTermNodeParameter(fo2 + 7, 1, ni,
                                                  Node.VALUE_LABEL_D2_DS2DS3,
                                                  1)
                    eftApex2.setTermNodeParameter(
                        fo2 + 8, 1, ni, Node.VALUE_LABEL_D3_DS1DS2DS3, 1)
                else:
                    eftApex2.setFunctionNumberOfTerms(fo2 + 4, 0)
                    eftApex2.setFunctionNumberOfTerms(fo2 + 6, 0)
                    eftApex2.setFunctionNumberOfTerms(fo2 + 7, 0)
                    eftApex2.setFunctionNumberOfTerms(fo2 + 8, 0)

            # basis node 3 -> local node 3
            ln = no + 3
            fo3 = fo + 16
            eftApex2.setTermNodeParameter(fo3 + 1, 1, ln,
                                          Node.VALUE_LABEL_VALUE, 1)
            # 0 terms = zero parameter for d/dxi1 basis
            eftApex2.setFunctionNumberOfTerms(fo3 + 2, 0)
            # 2 terms for d/dxi2 via general linear map:
            eftApex2.setFunctionNumberOfTerms(fo3 + 3, 2)
            eftApex2.setTermNodeParameter(fo3 + 3, 1, ln,
                                          Node.VALUE_LABEL_D_DS1, 1)
            eftApex2.setTermScaling(fo3 + 3, 1, [so + 1])
            eftApex2.setTermNodeParameter(fo3 + 3, 2, ln,
                                          Node.VALUE_LABEL_D_DS2, 1)
            eftApex2.setTermScaling(fo3 + 3, 2, [so + 2])
            # 2 terms for cross derivative 1 2 to correct circular apex: -sin(theta).phi, cos(theta).phi
            eftApex2.setFunctionNumberOfTerms(fo3 + 4, 2)
            eftApex2.setTermNodeParameter(fo3 + 4, 1, ln,
                                          Node.VALUE_LABEL_D_DS1, 1)
            eftApex2.setTermScaling(fo3 + 4, 1, [1, so + 2, so + 3])
            eftApex2.setTermNodeParameter(fo3 + 4, 2, ln,
                                          Node.VALUE_LABEL_D_DS2, 1)
            eftApex2.setTermScaling(fo3 + 4, 2, [so + 1, so + 3])
            eftApex2.setTermNodeParameter(fo3 + 5, 1, ln,
                                          Node.VALUE_LABEL_D_DS3, 1)
            # 0 terms = zero parameter for cross derivative 1 3, 2 3 and 1 2 3
            eftApex2.setFunctionNumberOfTerms(fo3 + 6, 0)
            eftApex2.setFunctionNumberOfTerms(fo3 + 7, 0)
            eftApex2.setFunctionNumberOfTerms(fo3 + 8, 0)

            # basis node 4 -> local node 3
            eftApex2.setTermNodeParameter(fo3 + 9, 1, ln,
                                          Node.VALUE_LABEL_VALUE, 1)
            # 0 terms = zero parameter for d/dxi1 basis
            eftApex2.setFunctionNumberOfTerms(fo3 + 10, 0)
            # 2 terms for d/dxi2 via general linear map:
            eftApex2.setFunctionNumberOfTerms(fo3 + 11, 2)
            eftApex2.setTermNodeParameter(fo3 + 11, 1, ln,
                                          Node.VALUE_LABEL_D_DS1, 1)
            eftApex2.setTermScaling(fo3 + 11, 1, [so + 4])
            eftApex2.setTermNodeParameter(fo3 + 11, 2, ln,
                                          Node.VALUE_LABEL_D_DS2, 1)
            eftApex2.setTermScaling(fo3 + 11, 2, [so + 5])
            # 2 terms for cross derivative 1 2 to correct circular apex: -sin(theta).phi, cos(theta).phi
            eftApex2.setFunctionNumberOfTerms(fo3 + 12, 2)
            eftApex2.setTermNodeParameter(fo3 + 12, 1, ln,
                                          Node.VALUE_LABEL_D_DS1, 1)
            eftApex2.setTermScaling(fo3 + 12, 1, [1, so + 5, so + 6])
            eftApex2.setTermNodeParameter(fo3 + 12, 2, ln,
                                          Node.VALUE_LABEL_D_DS2, 1)
            eftApex2.setTermScaling(fo3 + 12, 2, [so + 4, so + 6])
            eftApex2.setTermNodeParameter(fo3 + 13, 1, ln,
                                          Node.VALUE_LABEL_D_DS3, 1)
            # 0 terms = zero parameter for cross derivative 1 3, 2 3 and 1 2 3
            eftApex2.setFunctionNumberOfTerms(fo3 + 14, 0)
            eftApex2.setFunctionNumberOfTerms(fo3 + 15, 0)
            eftApex2.setFunctionNumberOfTerms(fo3 + 16, 0)

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

        cache = fm.createFieldcache()

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

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

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

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

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

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

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

            n3_fraction = n3 / elementsCountThroughWall

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

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

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

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

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

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

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

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

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

            if excludeBottomRows == 0:
                # create Apex1 elements, editing eft scale factor identifiers around apex
                # scale factor identifiers follow convention of offsetting by 100 for each 'version'
                for e1 in range(elementsCountAround):
                    va = e1
                    vb = (e1 + 1) % elementsCountAround
                    eftApex1.setScaleFactorIdentifier(2, va * 100 + 1)
                    eftApex1.setScaleFactorIdentifier(3, va * 100 + 2)
                    eftApex1.setScaleFactorIdentifier(4, va * 100 + 3)
                    eftApex1.setScaleFactorIdentifier(5, vb * 100 + 1)
                    eftApex1.setScaleFactorIdentifier(6, vb * 100 + 2)
                    eftApex1.setScaleFactorIdentifier(7, vb * 100 + 3)
                    eftApex1.setScaleFactorIdentifier(8, va * 100 + 1)
                    eftApex1.setScaleFactorIdentifier(9, va * 100 + 2)
                    eftApex1.setScaleFactorIdentifier(10, va * 100 + 3)
                    eftApex1.setScaleFactorIdentifier(11, vb * 100 + 1)
                    eftApex1.setScaleFactorIdentifier(12, vb * 100 + 2)
                    eftApex1.setScaleFactorIdentifier(13, vb * 100 + 3)
                    # redefine field in template for changes to eftApex1:
                    elementtemplateApex1.defineField(coordinates, -1, eftApex1)
                    element = mesh.createElement(elementIdentifier,
                                                 elementtemplateApex1)
                    bni1 = no + 1
                    bni2 = no + e1 + 2
                    bni3 = no + (e1 + 1) % elementsCountAround + 2
                    nodeIdentifiers = [
                        bni1, bni2, bni3, bni1 + now, bni2 + now, bni3 + now
                    ]
                    element.setNodesByIdentifier(eftApex1, nodeIdentifiers)
                    # set general linear map coefficients
                    radiansAround = e1 * radiansPerElementAround
                    radiansAroundNext = (
                        (e1 + 1) %
                        elementsCountAround) * radiansPerElementAround
                    scalefactors = [
                        -1.0,
                        math.sin(radiansAround),
                        math.cos(radiansAround), radiansPerElementAround,
                        math.sin(radiansAroundNext),
                        math.cos(radiansAroundNext), radiansPerElementAround,
                        math.sin(radiansAround),
                        math.cos(radiansAround), radiansPerElementAround,
                        math.sin(radiansAroundNext),
                        math.cos(radiansAroundNext), radiansPerElementAround
                    ]
                    result = element.setScaleFactors(eftApex1, scalefactors)
                    elementIdentifier = elementIdentifier + 1

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

            if excludeTopRows == 0:
                # create Apex2 elements, editing eft scale factor identifiers around apex
                # scale factor identifiers follow convention of offsetting by 100 for each 'version'
                for e1 in range(elementsCountAround):
                    va = e1
                    vb = (e1 + 1) % elementsCountAround
                    eftApex2.setScaleFactorIdentifier(2, va * 100 + 1)
                    eftApex2.setScaleFactorIdentifier(3, va * 100 + 2)
                    eftApex2.setScaleFactorIdentifier(4, va * 100 + 3)
                    eftApex2.setScaleFactorIdentifier(5, vb * 100 + 1)
                    eftApex2.setScaleFactorIdentifier(6, vb * 100 + 2)
                    eftApex2.setScaleFactorIdentifier(7, vb * 100 + 3)
                    eftApex2.setScaleFactorIdentifier(8, va * 100 + 1)
                    eftApex2.setScaleFactorIdentifier(9, va * 100 + 2)
                    eftApex2.setScaleFactorIdentifier(10, va * 100 + 3)
                    eftApex2.setScaleFactorIdentifier(11, vb * 100 + 1)
                    eftApex2.setScaleFactorIdentifier(12, vb * 100 + 2)
                    eftApex2.setScaleFactorIdentifier(13, vb * 100 + 3)
                    # redefine field in template for changes to eftApex2:
                    elementtemplateApex1.defineField(coordinates, -1, eftApex2)
                    element = mesh.createElement(elementIdentifier,
                                                 elementtemplateApex1)
                    bni3 = no + now
                    bni1 = bni3 - elementsCountAround + e1
                    bni2 = bni3 - elementsCountAround + (
                        e1 + 1) % elementsCountAround
                    nodeIdentifiers = [
                        bni1, bni2, bni3, bni1 + now, bni2 + now, bni3 + now
                    ]
                    element.setNodesByIdentifier(eftApex2, nodeIdentifiers)
                    # set general linear map coefficients
                    radiansAround = math.pi + e1 * radiansPerElementAround
                    radiansAroundNext = math.pi + (
                        (e1 + 1) %
                        elementsCountAround) * radiansPerElementAround
                    scalefactors = [
                        -1.0, -math.sin(radiansAround),
                        math.cos(radiansAround), radiansPerElementAround,
                        -math.sin(radiansAroundNext),
                        math.cos(radiansAroundNext), radiansPerElementAround,
                        -math.sin(radiansAround),
                        math.cos(radiansAround), radiansPerElementAround,
                        -math.sin(radiansAroundNext),
                        math.cos(radiansAroundNext), radiansPerElementAround
                    ]
                    result = element.setScaleFactors(eftApex2, scalefactors)
                    elementIdentifier = elementIdentifier + 1

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

        fm.endChange()
Exemple #5
0
    def generateMesh(region, options):
        """
        :param region: Zinc region to define model in. Must be empty.
        :param options: Dict containing options. See getDefaultOptions().
        :return: None
        """
        elementsCountAlong = options['Number of elements along']
        elementsCountAround = options['Number of elements around']
        elementsCountThroughWall = options['Number of elements through wall']
        wallThickness = options['Wall thickness']
        useCrossDerivatives = options['Use cross derivatives']

        fm = region.getFieldmodule()
        fm.beginChange()
        coordinates = fm.createFieldFiniteElement(3)
        coordinates.setName('coordinates')
        coordinates.setManaged(True)
        coordinates.setTypeCoordinate(True)
        coordinates.setCoordinateSystemType(
            Field.COORDINATE_SYSTEM_TYPE_RECTANGULAR_CARTESIAN)
        coordinates.setComponentName(1, 'x')
        coordinates.setComponentName(2, 'y')
        coordinates.setComponentName(3, 'z')

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

        mesh = fm.findMeshByDimension(3)

        tricubichermite = eftfactory_tricubichermite(mesh, useCrossDerivatives)
        eft = tricubichermite.createEftBasic()

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

        cache = fm.createFieldcache()

        # create nodes
        nodeIdentifier = 1
        radiansPerElementAround = 2.0 * math.pi / elementsCountAround
        wallThicknessPerElement = wallThickness / elementsCountThroughWall
        x = [0.0, 0.0, 0.0]
        dx_ds1 = [0.0, 0.0, 0.0]
        dx_ds2 = [0.0, 0.0, 1.0 / elementsCountAlong]
        dx_ds3 = [0.0, 0.0, 0.0]
        zero = [0.0, 0.0, 0.0]
        for n3 in range(elementsCountThroughWall + 1):
            radius = 0.5 + wallThickness * (n3 / elementsCountThroughWall -
                                            1.0)
            for n2 in range(elementsCountAlong + 1):
                x[2] = n2 / elementsCountAlong
                for n1 in range(elementsCountAround):
                    radiansAround = n1 * radiansPerElementAround
                    cosRadiansAround = math.cos(radiansAround)
                    sinRadiansAround = math.sin(radiansAround)
                    x[0] = radius * cosRadiansAround
                    x[1] = radius * sinRadiansAround
                    dx_ds1[
                        0] = radiansPerElementAround * radius * -sinRadiansAround
                    dx_ds1[
                        1] = radiansPerElementAround * radius * cosRadiansAround
                    dx_ds3[0] = wallThicknessPerElement * cosRadiansAround
                    dx_ds3[1] = wallThicknessPerElement * sinRadiansAround
                    node = nodes.createNode(nodeIdentifier, nodetemplate)
                    cache.setNode(node)
                    coordinates.setNodeParameters(cache, -1,
                                                  Node.VALUE_LABEL_VALUE, 1, x)
                    coordinates.setNodeParameters(cache, -1,
                                                  Node.VALUE_LABEL_D_DS1, 1,
                                                  dx_ds1)
                    coordinates.setNodeParameters(cache, -1,
                                                  Node.VALUE_LABEL_D_DS2, 1,
                                                  dx_ds2)
                    coordinates.setNodeParameters(cache, -1,
                                                  Node.VALUE_LABEL_D_DS3, 1,
                                                  dx_ds3)
                    if useCrossDerivatives:
                        coordinates.setNodeParameters(
                            cache, -1, Node.VALUE_LABEL_D2_DS1DS2, 1, zero)
                        coordinates.setNodeParameters(
                            cache, -1, Node.VALUE_LABEL_D2_DS1DS3, 1, zero)
                        coordinates.setNodeParameters(
                            cache, -1, Node.VALUE_LABEL_D2_DS2DS3, 1, zero)
                        coordinates.setNodeParameters(
                            cache, -1, Node.VALUE_LABEL_D3_DS1DS2DS3, 1, zero)
                    nodeIdentifier = nodeIdentifier + 1

        # create elements
        elementIdentifier = 1
        now = (elementsCountAlong + 1) * elementsCountAround
        for e3 in range(elementsCountThroughWall):
            for e2 in range(elementsCountAlong):
                for e1 in range(elementsCountAround):
                    element = mesh.createElement(elementIdentifier,
                                                 elementtemplate)
                    bni11 = e3 * now + e2 * elementsCountAround + e1 + 1
                    bni12 = e3 * now + e2 * elementsCountAround + (
                        e1 + 1) % elementsCountAround + 1
                    bni21 = e3 * now + (e2 + 1) * elementsCountAround + e1 + 1
                    bni22 = e3 * now + (e2 + 1) * elementsCountAround + (
                        e1 + 1) % elementsCountAround + 1
                    nodeIdentifiers = [
                        bni11, bni12, bni21, bni22, bni11 + now, bni12 + now,
                        bni21 + now, bni22 + now
                    ]
                    result = element.setNodesByIdentifier(eft, nodeIdentifiers)
                    elementIdentifier = elementIdentifier + 1

        fm.endChange()
Exemple #6
0
    def generateMesh(region, options):
        """
        :param region: Zinc region to define model in. Must be empty.
        :param options: Dict containing options. See getDefaultOptions().
        :return: None
        """
        elementsCount1 = options['Number of elements 1']
        elementsCount2 = options['Number of elements 2']
        elementsCount3 = options['Number of elements 3']
        useCrossDerivatives = options['Use cross derivatives']

        fm = region.getFieldmodule()
        fm.beginChange()
        coordinates = fm.createFieldFiniteElement(3)
        coordinates.setName('coordinates')
        coordinates.setManaged(True)
        coordinates.setTypeCoordinate(True)
        coordinates.setCoordinateSystemType(Field.COORDINATE_SYSTEM_TYPE_RECTANGULAR_CARTESIAN)
        coordinates.setComponentName(1, 'x')
        coordinates.setComponentName(2, 'y')
        coordinates.setComponentName(3, 'z')

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

        mesh = fm.findMeshByDimension(3)

        tricubichermite = eftfactory_tricubichermite(mesh, useCrossDerivatives)
        eft = tricubichermite.createEftBasic()

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

        cache = fm.createFieldcache()

        # create nodes
        nodeIdentifier = 1
        x = [ 0.0, 0.0, 0.0 ]
        dx_ds1 = [ 1.0 / elementsCount1, 0.0, 0.0 ]
        dx_ds2 = [ 0.0, 1.0 / elementsCount2, 0.0 ]
        dx_ds3 = [ 0.0, 0.0, 1.0 / elementsCount3 ]
        zero = [ 0.0, 0.0, 0.0 ]
        for n3 in range(elementsCount3 + 1):
            x[2] = n3 / elementsCount3
            for n2 in range(elementsCount2 + 1):
                x[1] = n2 / elementsCount2
                for n1 in range(elementsCount1 + 1):
                    x[0] = n1 / elementsCount1
                    node = nodes.createNode(nodeIdentifier, nodetemplate)
                    cache.setNode(node)
                    coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_VALUE, 1, x)
                    coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS1, 1, dx_ds1)
                    coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS2, 1, dx_ds2)
                    coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS3, 1, dx_ds3)
                    if useCrossDerivatives:
                        coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D2_DS1DS2, 1, zero)
                        coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D2_DS1DS3, 1, zero)
                        coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D2_DS2DS3, 1, zero)
                        coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D3_DS1DS2DS3, 1, zero)
                    nodeIdentifier = nodeIdentifier + 1

        # create elements
        elementIdentifier = 1
        no2 = (elementsCount1 + 1)
        no3 = (elementsCount2 + 1)*no2
        for e3 in range(elementsCount3):
            for e2 in range(elementsCount2):
                for e1 in range(elementsCount1):
                    element = mesh.createElement(elementIdentifier, elementtemplate)
                    bni = e3*no3 + e2*no2 + e1 + 1
                    nodeIdentifiers = [ bni, bni + 1, bni + no2, bni + no2 + 1, bni + no3, bni + no3 + 1, bni + no2 + no3, bni + no2 + no3 + 1 ]
                    result = element.setNodesByIdentifier(eft, nodeIdentifiers)
                    elementIdentifier = elementIdentifier + 1

        fm.endChange()
Exemple #7
0
    def generateMesh(region, options):
        """
        :param region: Zinc region to define model in. Must be empty.
        :param options: Dict containing options. See getDefaultOptions().
        :return: None
        """
        lvWallThickness = options['LV wall thickness']
        lvWallThicknessRatioBase = options['LV wall thickness ratio base']
        baseHeight = options['Base height']
        baseThickness = options['Base thickness']
        lvOutletRadius = options['LV outlet inner diameter']*0.5
        lvOutletWallThickness = options['LV outlet wall thickness']
        rvOutletRadius = options['RV outlet inner diameter']*0.5
        rvOutletWallThickness = options['RV outlet wall thickness']
        outletElementLength = options['Outlet element length']
        useCrossDerivatives = False

        # generate default heart ventricles model to add base plane to
        MeshType_3d_heartventricles1.generateMesh(region, options)

        fm = region.getFieldmodule()
        fm.beginChange()
        # find the coordinates field created for the sphere shell
        coordinates = fm.findFieldByName('coordinates').castFiniteElement()

        nodes = fm.findNodesetByFieldDomainType(Field.DOMAIN_TYPE_NODES)
        nodetemplateFull = nodes.createNodetemplate()
        nodetemplateFull.defineField(coordinates)
        nodetemplateFull.setValueNumberOfVersions(coordinates, -1, Node.VALUE_LABEL_VALUE, 1)
        nodetemplateFull.setValueNumberOfVersions(coordinates, -1, Node.VALUE_LABEL_D_DS1, 1)
        nodetemplateFull.setValueNumberOfVersions(coordinates, -1, Node.VALUE_LABEL_D_DS2, 1)
        nodetemplateFull.setValueNumberOfVersions(coordinates, -1, Node.VALUE_LABEL_D_DS3, 1)
        # nodes used only in bicubic-linear elements do not have D_DS3 parameters
        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)

        mesh = fm.findMeshByDimension(3)

        tricubichermite = eftfactory_tricubichermite(mesh, useCrossDerivatives)
        eft = tricubichermite.createEftBasic()

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

        cache = fm.createFieldcache()

        # create nodes
        nodeIdentifier = startNodeIdentifier = getMaximumNodeIdentifier(nodes) + 1

        lvDisplacementRadians = math.pi*2.0/3.0
        coslvDisplacementRadians = math.cos(lvDisplacementRadians)
        sinlvDisplacementRadians = math.sin(lvDisplacementRadians)

        outletJoinRadians = math.pi  # *0.8
        cosOutletJoinRadians = math.cos(outletJoinRadians)
        sinOutletJoinRadians = math.sin(outletJoinRadians)
        lvOutletOffset = 0.5 - lvWallThickness*(1.0 - lvWallThicknessRatioBase) - lvOutletRadius - lvOutletWallThickness
        #lvOutletCentreX = cosOutletJoinRadians*lvOutletOffset
        #lvOutletCentreY = sinOutletJoinRadians*lvOutletOffset
        lvOutletCentreX = lvOutletOffset*math.cos(lvDisplacementRadians)
        lvOutletCentreY = lvOutletOffset*math.sin(lvDisplacementRadians)

        # LV outlet - for bicubic-linear tube connection
        elementsCountAround = 6
        radiansPerElementAround = 2.0*math.pi/elementsCountAround
        x = [ 0.0, 0.0, baseHeight + baseThickness ]
        dx_ds1 = [ 0.0, 0.0, 0.0 ]
        dx_ds2 = [ 0.0, 0.0, outletElementLength ]
        dx_ds3 = [ 0.0, 0.0, 0.0 ]
        zero = [ 0.0, 0.0, 0.0 ]
        outletScale3 = 0.15
        for n3 in range(2):
            radius = lvOutletRadius + lvOutletWallThickness*n3
            for n1 in range(elementsCountAround):
                radiansAround = outletJoinRadians - math.pi + n1*radiansPerElementAround
                cosRadiansAround = math.cos(radiansAround)
                sinRadiansAround = math.sin(radiansAround)
                x[0] = lvOutletCentreX + radius*cosRadiansAround
                x[1] = lvOutletCentreY + radius*sinRadiansAround
                dx_ds1[0] = radiansPerElementAround*radius*-sinRadiansAround
                dx_ds1[1] = radiansPerElementAround*radius*cosRadiansAround
                nodetemplate = nodetemplateLinearS3 if ((n3 == 0) or (n1 == 3)) else nodetemplateFull
                node = nodes.createNode(nodeIdentifier, nodetemplate)
                cache.setNode(node)
                coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_VALUE, 1, x)
                coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS1, 1, dx_ds1)
                coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS2, 1, dx_ds2)
                if nodetemplate is nodetemplateFull:
                    dx_ds3[0] = outletScale3*cosRadiansAround
                    dx_ds3[1] = outletScale3*sinRadiansAround
                    coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS3, 1, dx_ds3)
                nodeIdentifier += 1

        # RV outlet - for bicubic-linear tube connection
        elementsCountAround = 6
        radiansPerElementAround = 2.0*math.pi/elementsCountAround
        rvOutletOffset = lvOutletRadius + lvOutletWallThickness + rvOutletWallThickness + rvOutletRadius
        rvOutletCentreX = lvOutletCentreX + cosOutletJoinRadians*rvOutletOffset
        rvOutletCentreY = lvOutletCentreY + sinOutletJoinRadians*rvOutletOffset

        x = [ 0.0, 0.0, baseHeight + baseThickness ]
        dx_ds1 = [ 0.0, 0.0, 0.0 ]
        dx_ds2 = [ 0.0, 0.0, outletElementLength ]
        dx_ds3 = [ 0.0, 0.0, 0.0 ]
        for n3 in range(2):
            radius = rvOutletRadius + rvOutletWallThickness*n3
            for n1 in range(elementsCountAround):
                if (n3 == 1) and (n1 == 0):
                    continue  # node is common with LV outlet
                radiansAround = outletJoinRadians - math.pi + n1*radiansPerElementAround
                cosRadiansAround = math.cos(radiansAround)
                sinRadiansAround = math.sin(radiansAround)
                x[0] = rvOutletCentreX + radius*cosRadiansAround
                x[1] = rvOutletCentreY + radius*sinRadiansAround
                dx_ds1[0] = radiansPerElementAround*radius*-sinRadiansAround
                dx_ds1[1] = radiansPerElementAround*radius*cosRadiansAround
                node = nodes.createNode(nodeIdentifier, nodetemplateLinearS3)
                cache.setNode(node)
                coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_VALUE, 1, x)
                coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS1, 1, dx_ds1)
                coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS2, 1, dx_ds2)
                nodeIdentifier += 1


        # create nodes on top and bottom of supraventricular crest centre where it meets
        # between nodes 141 and 130
        for i in range(2):
            radiansAround = outletJoinRadians - math.pi + (1.5 + 0.5*i)*radiansPerElementAround
            cosRadiansAround = math.cos(radiansAround)
            sinRadiansAround = math.sin(radiansAround)
            scale1 = 0.15
            distance = 0.15
            if i == 0:
                scale2 = 0.25
            else:
                scale2 = 0.1
            x = [
                lvOutletCentreX + cosRadiansAround*(lvOutletRadius + lvOutletWallThickness + distance),
                lvOutletCentreY + sinRadiansAround*(lvOutletRadius + lvOutletWallThickness + distance),
                baseHeight
            ]
            dx_ds1 = [ -sinRadiansAround*scale1, cosRadiansAround*scale1, 0.0 ]
            dx_ds2 = [ -cosRadiansAround*scale2, -sinRadiansAround*scale2, 0.0 ]
            dx_ds3 = [ 0.0, 0.0, baseThickness ]
            node = nodes.createNode(nodeIdentifier, nodetemplateFull)
            cache.setNode(node)
            coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_VALUE, 1, x)
            coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS1, 1, dx_ds1)
            coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS2, 1, dx_ds2)
            coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS3, 1, dx_ds3)
            if i == 1:
                crest_nid1 = nodeIdentifier
            else:
                ra_nid1 = nodeIdentifier
            nodeIdentifier += 1

            x[2] = baseHeight + baseThickness
            node = nodes.createNode(nodeIdentifier, nodetemplateFull)
            cache.setNode(node)
            result = coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_VALUE, 1, x)
            coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS1, 1, dx_ds1)
            coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS2, 1, dx_ds2)
            coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS3, 1, dx_ds3)
            if i == 1:
                crest_nid2 = nodeIdentifier
            else:
                ra_nid2 = nodeIdentifier
            nodeIdentifier += 1

        # place nodes below RV outlet nodes 143, 144
        for nid in [143, 144]:
            node1 = nodes.findNodeByIdentifier(nid)
            cache.setNode(node1)
            result, x = coordinates.getNodeParameters(cache, -1, Node.VALUE_LABEL_VALUE, 1, 3)
            result, dx_ds1 = coordinates.getNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS1, 1, 3)
            dx_ds2 = [ 0.0, 0.0, baseThickness ]
            result, dx_ds3 = coordinates.getNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS3, 1, 3)
            for c in range(3):
                dx_ds3[c] *= lvOutletWallThickness/outletScale3
            x[2] -= baseThickness
            node = nodes.createNode(nodeIdentifier, nodetemplateFull)
            nodeIdentifier += 1
            cache.setNode(node)
            result = coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_VALUE, 1, x)
            coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS1, 1, dx_ds1)
            coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS2, 1, dx_ds2)
            coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS3, 1, dx_ds3)

        if False:
            # create extra nodes on LV 'crest' and edge of RA
            for i in range(2):
                scale1 = 0.08
                scale2 = 0.2
                x = [ -0.06 - scale1*i, -0.16, baseHeight ]
                dx_ds1 = [ scale1, 0.0, 0.0 ]
                dx_ds2 = [ 0.0, scale2, 0.0 ]
                dx_ds3 = [ 0.0, 0.0, baseThickness ]
                node = nodes.createNode(nodeIdentifier, nodetemplateFull)
                cache.setNode(node)
                coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_VALUE, 1, x)
                coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS1, 1, dx_ds1)
                coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS2, 1, dx_ds2)
                coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS3, 1, dx_ds3)
                nodeIdentifier += 1

            x[2] = baseHeight + baseThickness
            node = nodes.createNode(nodeIdentifier, nodetemplateFull)
            cache.setNode(node)
            result = coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_VALUE, 1, x)
            coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS1, 1, dx_ds1)
            coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS2, 1, dx_ds2)
            coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS3, 1, dx_ds3)
            nodeIdentifier += 1


        # create elements
        elementIdentifier = startElementIdentifier = getMaximumElementIdentifier(mesh) + 1

        # crux of the heart - collapsed on 1 side, and partially on the other
        eft1 = tricubichermite.createEftBasic()
        ln_map = [ 1, 2, 1, 3, 4, 5, 1, 6 ]
        remapEftLocalNodes(eft1, 6, ln_map)
        setEftScaleFactorIds(eft1, [1], [])
        for n in [0, 2, 4]:
            ln = ln_map[n]
            eft1.setFunctionNumberOfTerms(n*8 + 3, 0)
        for n in [2]:
            ln = ln_map[n]
            eft1.setFunctionNumberOfTerms(n*8 + 5, 0)
        for n in [4, 6]:
            ln = ln_map[n]
            mapEftFunction1Node1Term(eft1, n*8 + 3, ln, Node.VALUE_LABEL_D_DS3, 1, [1])
        for n in [2, 6]:
            ln = ln_map[n]
            eft1.setFunctionNumberOfTerms(n*8 + 5, 0)
        tricubichermite.setEftLinearDerivativeXi3(eft1, 4, 8, 3, 6, 1)
        elementtemplate1 = mesh.createElementtemplate()
        elementtemplate1.setElementShapeType(Element.SHAPE_TYPE_CUBE)
        result = elementtemplate1.defineField(coordinates, -1, eft1)
        element = mesh.createElement(elementIdentifier, elementtemplate1)
        nodeIdentifiers1 = [ 39, 40, 133, 88, 89, 139 ]
        result2 = element.setNodesByIdentifier(eft1, nodeIdentifiers1)
        result3 = element.setScaleFactors(eft1, [-1.0])
        print('create element crux', elementIdentifier, result2, result3, nodeIdentifiers1 )
        elementIdentifier += 1

        # Mitral - Aorta separator, collapsed on one end and one side
        eft1 = tricubichermite.createEftBasic()
        ln_map = [ 1, 2, 3, 4, 1, 2, 5, 6 ]
        remapEftLocalNodes(eft1, 6, ln_map)
        setEftScaleFactorIds(eft1, [1], [])
        for n in [3, 7]:
            ln = ln_map[n]
            #eft1.setFunctionNumberOfTerms(n*8 + 3, 0)
            mapEftFunction1Node1Term(eft1, n*8 + 3, ln, Node.VALUE_LABEL_D_DS1, 1, [])
        for n in [0, 1, 4, 5]:
            ln = ln_map[n]
            eft1.setFunctionNumberOfTerms(n*8 + 5, 0)
        for n in [1, 5]:
            ln = ln_map[n]
            mapEftFunction1Node1Term(eft1, n*8 + 2, ln, Node.VALUE_LABEL_D_DS1, 1, [1])
            mapEftFunction1Node1Term(eft1, n*8 + 3, ln, Node.VALUE_LABEL_D_DS1, 1, [])
        for n in [0]:
            ln = ln_map[n]
            mapEftFunction1Node1Term(eft1, n*8 + 3, ln, Node.VALUE_LABEL_D_DS3, 1, [1])
        tricubichermite.setEftLinearDerivativeXi3(eft1, 3, 7, 3, 5, 1)
        tricubichermite.setEftLinearDerivativeXi3(eft1, 4, 8, 4, 6, 1)
        elementtemplate1 = mesh.createElementtemplate()
        elementtemplate1.setElementShapeType(Element.SHAPE_TYPE_CUBE)
        result = elementtemplate1.defineField(coordinates, -1, eft1)
        element = mesh.createElement(elementIdentifier, elementtemplate1)
        nodeIdentifiers1 = [ 161, 39, 138, 133, 144, 139 ]
        result2 = element.setNodesByIdentifier(eft1, nodeIdentifiers1)
        result3 = element.setScaleFactors(eft1, [-1.0])
        print('create element Mitral - Aorta separator', elementIdentifier, result2, result3, nodeIdentifiers1 )
        elementIdentifier += 1



        eftLinearOutlet = tricubichermite.createEftBasic()
        setEftScaleFactorIds(eftLinearOutlet, [1], [])
        # transition to no derivatives at outlet
        tricubichermite.setEftLinearDerivativeXi3(eftLinearOutlet, 3, 7, 3, 7, 1)
        tricubichermite.setEftLinearDerivativeXi3(eftLinearOutlet, 4, 8, 4, 8, 1)
        elementtemplateLinearOutlet = mesh.createElementtemplate()
        elementtemplateLinearOutlet.setElementShapeType(Element.SHAPE_TYPE_CUBE)
        result = elementtemplateLinearOutlet.defineField(coordinates, -1, eftLinearOutlet)

        if False:
            # triangle wedges on top of ventricular septum
            # collapsed at xi2 = 1, angling in xi3 = 0 face
            eftSeptumWedge = tricubichermite.createEftBasic()
            eftSeptumWedge.setNumberOfLocalNodes(6)
            setEftScaleFactorIds(eftSeptumWedge, [1], [])
            for ln in [5, 6]:
                n = ln - 1
                # general map d/ds2 to get angle:
                mapEftFunction1Node2Terms(eftSeptumWedge, n*8 + 3, ln, Node.VALUE_LABEL_D_DS2, 1, [], Node.VALUE_LABEL_D_DS3, 1, [1])
            for ln in [3, 4]:
                n = ln + 3
                # shape value and d/ds1
                eftSeptumWedge.setTermNodeParameter(n*8 + 1, 1, ln, Node.VALUE_LABEL_VALUE, 1)
                eftSeptumWedge.setTermNodeParameter(n*8 + 2, 1, ln, Node.VALUE_LABEL_D_DS1, 1)
                # general map d/ds2 to get angle:
                mapEftFunction1Node2Terms(eftSeptumWedge, n*8 + 3, ln, Node.VALUE_LABEL_D_DS2, 1, [], Node.VALUE_LABEL_D_DS3, 1, [1])
                # zero d/dxi3
                eftSeptumWedge.setFunctionNumberOfTerms(n*8 + 5, 0)
            print('eftSeptumWedge.validate()', eftSeptumWedge.validate())
            elementtemplateSeptumWedge = mesh.createElementtemplate()
            elementtemplateSeptumWedge.setElementShapeType(Element.SHAPE_TYPE_CUBE)
            result = elementtemplateSeptumWedge.defineField(coordinates, -1, eftSeptumWedge)

        septumNids = [
            [ 40, 41, 133, 134, 89, 90, 139, 140 ],
            [ 41, 42, 134, 135, 90, 91, 140, 141 ],
            [ 42, 43, 135, 136, 91, 92, 141, 142 ],
            [ 43, 44, 136, 137, 92, 93, 142, 143 ]
        ]
        for i in range(4):
            element = mesh.createElement(elementIdentifier, elementtemplateLinearOutlet)
            result2 = element.setNodesByIdentifier(eftLinearOutlet, septumNids[i])
            result3 = element.setScaleFactors(eftLinearOutlet, [-1])
            print('create septum element', elementIdentifier, result2, result3, septumNids[i])
            elementIdentifier += 1

        # supraventricular crest at RA by RV free wall
        eft1 = tricubichermite.createEftBasic()
        eft1.setTermNodeParameter(0*8 + 3, 1, 1, Node.VALUE_LABEL_D_DS1, 1)
        eft1.setTermNodeParameter(4*8 + 3, 1, 5, Node.VALUE_LABEL_D_DS1, 1)
        elementtemplate1 = mesh.createElementtemplate()
        elementtemplate1.setElementShapeType(Element.SHAPE_TYPE_CUBE)
        result = elementtemplate1.defineField(coordinates, -1, eft1)
        element = mesh.createElement(elementIdentifier, elementtemplate1)
        nodeIdentifiers1 = [ 111, 112, 156, 158, 129, 130, 157, 159 ]
        result2 = element.setNodesByIdentifier(eft1, nodeIdentifiers1)
        print('create element supra RA 1', elementIdentifier, result2, nodeIdentifiers1 )
        elementIdentifier += 1

        # supraventricular crest at PO by RV free wall
        eft1 = tricubichermite.createEftBasic()
        setEftScaleFactorIds(eft1, [1], [])
        tricubichermite.setEftLinearDerivativeXi3(eft1, 4, 8, 4, 8, 1)
        for ln in [4,8]:
            n = ln - 1
            mapEftFunction1Node1Term(eft1, n*8 + 2, ln, Node.VALUE_LABEL_D_DS2, 1, [])
            #mapEftFunction1Node1Term(eft1, n*8 + 3, ln, Node.VALUE_LABEL_D_DS1, 1, [1])
        elementtemplate1 = mesh.createElementtemplate()
        elementtemplate1.setElementShapeType(Element.SHAPE_TYPE_CUBE)
        result = elementtemplate1.defineField(coordinates, -1, eft1)
        element = mesh.createElement(elementIdentifier, elementtemplate1)
        nodeIdentifiers1 = [ 112, 113, 158, 147, 130, 131, 159, 152 ]
        result2 = element.setNodesByIdentifier(eft1, nodeIdentifiers1)
        result3 = element.setScaleFactors(eft1, [-1.0])
        print('create element supra PO 1', elementIdentifier, result2, result3, nodeIdentifiers1 )
        elementIdentifier += 1

        # supraventricular crest at RA by RV septum
        eft1 = tricubichermite.createEftBasic()
        setEftScaleFactorIds(eft1, [1], [])
        for ln in [3, 4]:
            n = ln - 1
            mapEftFunction1Node1Term(eft1, n*8 + 3, ln, Node.VALUE_LABEL_D_DS2, 1, [1])
            mapEftFunction1Node1Term(eft1, n*8 + 5, ln, Node.VALUE_LABEL_D_DS2, 1, [])
        for ln in [7]:
            n = ln - 1
            mapEftFunction1Node1Term(eft1, n*8 + 3, ln, Node.VALUE_LABEL_D_DS1, 1, [1])
            mapEftFunction1Node1Term(eft1, n*8 + 5, ln, Node.VALUE_LABEL_D_DS2, 1, [])
        for ln in [8]:
            n = ln - 1
            mapEftFunction1Node1Term(eft1, n*8 + 3, ln, Node.VALUE_LABEL_D_DS3, 1, [1])
            mapEftFunction1Node1Term(eft1, n*8 + 5, ln, Node.VALUE_LABEL_D_DS2, 1, [])
        elementtemplate1 = mesh.createElementtemplate()
        elementtemplate1.setElementShapeType(Element.SHAPE_TYPE_CUBE)
        result = elementtemplate1.defineField(coordinates, -1, eft1)
        element = mesh.createElement(elementIdentifier, elementtemplate1)
        nodeIdentifiers1 = [ 156, 158, 90, 91, 157, 159, 140, 141 ]
        result2 = element.setNodesByIdentifier(eft1, nodeIdentifiers1)
        result3 = element.setScaleFactors(eft1, [-1.0])
        print('create element supra RA 2', elementIdentifier, result2, result3, nodeIdentifiers1 )
        elementIdentifier += 1

        # supraventricular crest at PO by RV septum
        eft1 = tricubichermite.createEftBasic()
        setEftScaleFactorIds(eft1, [1], [])
        tricubichermite.setEftLinearDerivativeXi3(eft1, 2, 6, 2, 6, 1)
        tricubichermite.setEftLinearDerivativeXi3(eft1, 4, 8, 4, 8, 1)
        for ln in [3]:
            n = ln - 1
            mapEftFunction1Node1Term(eft1, n*8 + 2, ln, Node.VALUE_LABEL_D_DS2, 1, [])
            mapEftFunction1Node1Term(eft1, n*8 + 3, ln, Node.VALUE_LABEL_D_DS2, 1, [1])
            mapEftFunction1Node1Term(eft1, n*8 + 5, ln, Node.VALUE_LABEL_D_DS2, 1, [])
        for ln in [7]:
            n = ln - 1
            mapEftFunction1Node1Term(eft1, n*8 + 2, ln, Node.VALUE_LABEL_D_DS3, 1, [])
            mapEftFunction1Node1Term(eft1, n*8 + 3, ln, Node.VALUE_LABEL_D_DS3, 1, [1])
            mapEftFunction1Node1Term(eft1, n*8 + 5, ln, Node.VALUE_LABEL_D_DS2, 1, [])
        for ln in [2,6]:
            n = ln - 1
            mapEftFunction1Node1Term(eft1, n*8 + 2, ln, Node.VALUE_LABEL_D_DS2, 1, [])
            mapEftFunction1Node1Term(eft1, n*8 + 3, ln, Node.VALUE_LABEL_D_DS1, 1, [1])
        for ln in [4,8]:
            n = ln - 1
            mapEftFunction1Node1Term(eft1, n*8 + 2, ln, Node.VALUE_LABEL_D_DS2, 1, [])
            mapEftFunction1Node1Term(eft1, n*8 + 3, ln, Node.VALUE_LABEL_D_DS1, 1, [1])
        elementtemplate1 = mesh.createElementtemplate()
        elementtemplate1.setElementShapeType(Element.SHAPE_TYPE_CUBE)
        result = elementtemplate1.defineField(coordinates, -1, eft1)
        element = mesh.createElement(elementIdentifier, elementtemplate1)
        nodeIdentifiers1 = [ 158, 147, 91, 146, 159, 152, 141, 151 ]
        result2 = element.setNodesByIdentifier(eft1, nodeIdentifiers1)
        result3 = element.setScaleFactors(eft1, [-1.0])
        print('create element supra PO 2', elementIdentifier, result2, result3, nodeIdentifiers1 )
        elementIdentifier += 1

        # supraventricular crest at PO-AO joiner
        # This element has some penetraton when very thin. Possible adding a rake angle to septal elements will help
        eft1 = tricubichermite.createEftBasic()
        eft1.setNumberOfLocalNodes(7)
        setEftScaleFactorIds(eft1, [1], [])
        tricubichermite.setEftLinearDerivativeXi3(eft1, 2, 6, 2, 6, 1)
        #tricubichermite.setEftLinearDerivativeXi3(eft1, 4, 7, 4, 7, 1)
        tricubichermite.setEftLinearDerivativeXi3(eft1, 4, 8, 4, 7, 1)
        for ln in [1]:
            n = ln - 1
            mapEftFunction1Node1Term(eft1, n*8 + 2, ln, Node.VALUE_LABEL_D_DS2, 1, [])
            mapEftFunction1Node1Term(eft1, n*8 + 3, ln, Node.VALUE_LABEL_D_DS1, 1, [])
            mapEftFunction1Node1Term(eft1, n*8 + 5, ln, Node.VALUE_LABEL_D_DS2, 1, [])
        #print('eft1.validate() 1', eft1.validate())
        for ln in [3]:
            n = ln - 1
            mapEftFunction1Node1Term(eft1, n*8 + 2, ln, Node.VALUE_LABEL_D_DS2, 1, [])
            mapEftFunction1Node1Term(eft1, n*8 + 3, ln, Node.VALUE_LABEL_D_DS1, 1, [])
            mapEftFunction1Node1Term(eft1, n*8 + 5, ln, Node.VALUE_LABEL_D_DS2, 1, [])
            #eft1.setFunctionNumberOfTerms(n*8 + 5, 0)
        #print('eft1.validate() 2', eft1.validate())
        for ln in [2, 4, 6]:
            n = ln - 1
            mapEftFunction1Node1Term(eft1, n*8 + 2, ln, Node.VALUE_LABEL_D_DS2, 1, [])
            mapEftFunction1Node1Term(eft1, n*8 + 3, ln, Node.VALUE_LABEL_D_DS1, 1, [1])
        for ln in [5]:
            n = ln - 1
            mapEftFunction1Node1Term(eft1, n*8 + 2, ln, Node.VALUE_LABEL_D_DS3, 1, [])
            mapEftFunction1Node1Term(eft1, n*8 + 3, ln, Node.VALUE_LABEL_D_DS1, 1, [])
            mapEftFunction1Node1Term(eft1, n*8 + 5, ln, Node.VALUE_LABEL_D_DS2, 1, [])
        #print('eft1.validate() 3', eft1.validate())
        for n in [6, 7]:
            ln = 7  # collapse on one edge
            mapEftFunction1Node1Term(eft1, n*8 + 1, ln, Node.VALUE_LABEL_VALUE, 1, [])
            eft1.setFunctionNumberOfTerms(n*8 + 2, 0)
            #eft1.setFunctionNumberOfTerms(n*8 + 3, 0)
            mapEftFunction1Node1Term(eft1, n*8 + 3, ln, Node.VALUE_LABEL_D_DS1, 1, [])
            #eft1.setFunctionNumberOfTerms(n*8 + 5, 0)
        for n in [6]:
            ln = 7  # collapse on one edge
            mapEftFunction1Node1Term(eft1, n*8 + 5, ln, Node.VALUE_LABEL_D_DS2, 1, [])
            #eft1.setFunctionNumberOfTerms(n*8 + 5, 0)
        print('eft1.validate() 4', eft1.validate())
        elementtemplate1 = mesh.createElementtemplate()
        elementtemplate1.setElementShapeType(Element.SHAPE_TYPE_CUBE)
        result = elementtemplate1.defineField(coordinates, -1, eft1)
        element = mesh.createElement(elementIdentifier, elementtemplate1)
        nodeIdentifiers1 = [ 91, 146, 92, 145, 141, 151, 142 ]
        result2 = element.setNodesByIdentifier(eft1, nodeIdentifiers1)
        result3 = element.setScaleFactors(eft1, [-1.0])
        print('create element supra PO AO 3', elementIdentifier, result2, result3, nodeIdentifiers1 )
        elementIdentifier += 1


        if False:
            eft1 = tricubichermite.createEftBasic()
            setEftScaleFactorIds(eft1, [1], [])
            mapEftFunction1Node1Term(eft1, 0*8 + 2, 1, Node.VALUE_LABEL_D_DS2, 1, [1])
            mapEftFunction1Node1Term(eft1, 0*8 + 3, 1, Node.VALUE_LABEL_D_DS1, 1, [])
            mapEftFunction1Node1Term(eft1, 1*8 + 2, 2, Node.VALUE_LABEL_D_DS2, 1, [1])
            mapEftFunction1Node1Term(eft1, 4*8 + 2, 5, Node.VALUE_LABEL_D_DS2, 1, [1])
            mapEftFunction1Node1Term(eft1, 4*8 + 3, 5, Node.VALUE_LABEL_D_DS1, 1, [])
            mapEftFunction1Node1Term(eft1, 5*8 + 2, 6, Node.VALUE_LABEL_D_DS2, 1, [1])
            # transition to no derivatives at outlet
            tricubichermite.setEftLinearDerivativeXi3(eft1, 3, 7, 3, 7, 1)
            tricubichermite.setEftLinearDerivativeXi3(eft1, 4, 8, 4, 8, 1)
            elementtemplate1 = mesh.createElementtemplate()
            elementtemplate1.setElementShapeType(Element.SHAPE_TYPE_CUBE)
            result = elementtemplate1.defineField(coordinates, -1, eft1)
            element = mesh.createElement(elementIdentifier, elementtemplate1)
            nodeIdentifiers1 = [ 156, 112, 146, 147, 157, 130, 151, 152 ]
            result2 = element.setNodesByIdentifier(eft1, nodeIdentifiers1)
            result3 = element.setScaleFactors(eft1, [-1.0])
            print('create element', elementIdentifier, result2, result3, nodeIdentifiers1 )
            elementIdentifier += 1

            rvNids = [
                [147, 148, 152, 153 ],
                [148, 149, 153, 154 ]
            ]

            i = 0
            for eid in [ 67, 68 ]:
                origElement = mesh.findElementByIdentifier(eid)
                origEft = origElement.getElementfieldtemplate(coordinates, -1)
                origNodeIdentifiers = getElementNodeIdentifiers(origElement, origEft)
                eft1 = origEft
                setEftScaleFactorIds(eft1, [1], [])
                # transition to no derivatives at outlet
                tricubichermite.setEftLinearDerivativeXi3(eft1, 3, 7, 3, 7, 1)
                tricubichermite.setEftLinearDerivativeXi3(eft1, 4, 8, 4, 8, 1)
                elementtemplate1 = mesh.createElementtemplate()
                elementtemplate1.setElementShapeType(Element.SHAPE_TYPE_CUBE)
                result = elementtemplate1.defineField(coordinates, -1, eft1)
                element = mesh.createElement(elementIdentifier, elementtemplate1)
                nids = rvNids[i]
                nodeIdentifiers1 = [ origNodeIdentifiers[2], origNodeIdentifiers[3], nids[0], nids[1], origNodeIdentifiers[6], origNodeIdentifiers[7], nids[2], nids[3] ]
                result2 = element.setNodesByIdentifier(eft1, nodeIdentifiers1)
                result3 = element.setScaleFactors(eft1, [-1.0])
                print('create element', elementIdentifier, result2, result3, nodeIdentifiers1 )
                elementIdentifier += 1
                i += 1


        rvNids = [
            [147, 148, 152, 153 ],
            [148, 149, 153, 154 ]
        ]

        scalefactors5hanging = [ -1.0, 0.5, 0.25, 0.125, 0.75 ]

        i = -1
        for eid in [ 68 ]:
            i += 1
            origElement = mesh.findElementByIdentifier(eid)
            origEft = origElement.getElementfieldtemplate(coordinates, -1)
            origNodeIdentifiers = getElementNodeIdentifiers(origElement, origEft)

            eft1 = tricubichermite.createEftBasic()
            eft2 = tricubichermite.createEftBasic()

            # general scale factors 1 -> 1, 102 -> 1/2, 104 -> 1/4, 108 -> 1/8, 304 -> 3/4
            setEftScaleFactorIds(eft1, [1, 102, 104, 108, 304], [])
            setEftScaleFactorIds(eft2, [1, 102, 104, 108, 304], [])

            tricubichermite.setEftMidsideXi1HangingNode(eft1, 2, 1, 1, 2, [1, 2, 3, 4, 5])
            tricubichermite.setEftMidsideXi1HangingNode(eft1, 6, 5, 5, 6, [1, 2, 3, 4, 5])
            tricubichermite.setEftLinearDerivativeXi3(eft1, 3, 7, 3, 7, 1)
            tricubichermite.setEftLinearDerivativeXi3(eft1, 4, 8, 4, 8, 1)

            tricubichermite.setEftMidsideXi1HangingNode(eft2, 1, 2, 1, 2, [1, 2, 3, 4, 5])
            tricubichermite.setEftMidsideXi1HangingNode(eft2, 5, 6, 5, 6, [1, 2, 3, 4, 5])
            tricubichermite.setEftLinearDerivativeXi3(eft2, 3, 7, 3, 7, 1)
            tricubichermite.setEftLinearDerivativeXi3(eft2, 4, 8, 4, 8, 1)

            elementtemplate1 = mesh.createElementtemplate()
            elementtemplate1.setElementShapeType(Element.SHAPE_TYPE_CUBE)
            result = elementtemplate1.defineField(coordinates, -1, eft1)
            element = mesh.createElement(elementIdentifier, elementtemplate1)
            nids = rvNids[i*2]
            nodeIdentifiers1 = [ origNodeIdentifiers[2], origNodeIdentifiers[3], nids[0], nids[1], origNodeIdentifiers[6], origNodeIdentifiers[7], nids[2], nids[3] ]
            result2 = element.setNodesByIdentifier(eft1, nodeIdentifiers1)
            result3 = element.setScaleFactors(eft1, scalefactors5hanging)
            print('create hanging element 1', elementIdentifier, result2, result3, nodeIdentifiers1, scalefactors5hanging)
            elementIdentifier += 1

            elementtemplate2 = mesh.createElementtemplate()
            elementtemplate2.setElementShapeType(Element.SHAPE_TYPE_CUBE)
            result = elementtemplate2.defineField(coordinates, -1, eft2)
            element = mesh.createElement(elementIdentifier, elementtemplate2)
            nids = rvNids[i*2 + 1]
            nodeIdentifiers2 = [ origNodeIdentifiers[2], origNodeIdentifiers[3], nids[0], nids[1], origNodeIdentifiers[6], origNodeIdentifiers[7], nids[2], nids[3] ]
            result2 = element.setNodesByIdentifier(eft2, nodeIdentifiers2)
            result3 = element.setScaleFactors(eft2, scalefactors5hanging)
            print('create hanging element 2', elementIdentifier, result2, result3, nodeIdentifiers2, scalefactors5hanging)
            elementIdentifier += 1

        # PO segment on RV free wall h element
        eft1 = tricubichermite.createEftBasic()
        setEftScaleFactorIds(eft1, [1], [])
        for ln in [2]:
            n = ln - 1
            mapEftFunction1Node1Term(eft1, n*8 + 2, ln, Node.VALUE_LABEL_D_DS1, 1, [1])
            mapEftFunction1Node1Term(eft1, n*8 + 5, ln, Node.VALUE_LABEL_D_DS1, 1, [])
        for ln in [ 6]:
            n = ln - 1
            #mapEftFunction1Node1Term(eft1, n*8 + 2, ln, Node.VALUE_LABEL_D_DS1, 1, [1])
            mapEftFunction1Node1Term(eft1, n*8 + 5, ln, Node.VALUE_LABEL_D_DS1, 1, [])
        tricubichermite.setEftLinearDerivativeXi3(eft1, 3, 7, 3, 7, 1)
        tricubichermite.setEftLinearDerivativeXi3(eft1, 4, 8, 4, 8, 1)
        elementtemplate1 = mesh.createElementtemplate()
        elementtemplate1.setElementShapeType(Element.SHAPE_TYPE_CUBE)
        result = elementtemplate1.defineField(coordinates, -1, eft1)
        element = mesh.createElement(elementIdentifier, elementtemplate1)
        nodeIdentifiers1 = [ 114, 93, 149, 150, 132, 94, 154, 155 ]
        result2 = element.setNodesByIdentifier(eft1, nodeIdentifiers1)
        result3 = element.setScaleFactors(eft1, [-1.0])
        print('create element PO 5', elementIdentifier, result2, result3, nodeIdentifiers1 )
        elementIdentifier += 1

        # Last PO segment on LV/AO side
        eft1 = tricubichermite.createEftBasic()
        ln_map = [ 1, 2, 3, 4, 5, 6, 7, 6 ]
        remapEftLocalNodes(eft1, 7, ln_map)
        setEftScaleFactorIds(eft1, [1], [])
        for n in [0, 1, 4, 5]:
            ln = ln_map[n]
            mapEftFunction1Node1Term(eft1, n*8 + 5, ln, Node.VALUE_LABEL_D_DS2, 1, [])
        for n in [0, 1, 4, 5, 7]:
            ln = ln_map[n]
            mapEftFunction1Node1Term(eft1, n*8 + 2, ln, Node.VALUE_LABEL_D_DS1, 1, [1])
        for n in [4]:
            ln = ln_map[n]
            mapEftFunction1Node1Term(eft1, n*8 + 3, ln, Node.VALUE_LABEL_D_DS1, 1, [1])
        for n in [5, 7]:
            ln = ln_map[n]
            eft1.setFunctionNumberOfTerms(n*8 + 3, 0)
        for n in [5]:
            ln = ln_map[n]
            eft1.setFunctionNumberOfTerms(n*8 + 5, 0)
        tricubichermite.setEftLinearDerivativeXi3(eft1, 3, 7, 3, 7, 1)
        tricubichermite.setEftLinearDerivativeXi3(eft1, 4, 8, 4, 6, 1)
        elementtemplate1 = mesh.createElementtemplate()
        elementtemplate1.setElementShapeType(Element.SHAPE_TYPE_CUBE)
        result = elementtemplate1.defineField(coordinates, -1, eft1)
        element = mesh.createElement(elementIdentifier, elementtemplate1)
        nodeIdentifiers1 = [ 93, 92, 150, 145, 143, 142, 155 ]
        result2 = element.setNodesByIdentifier(eft1, nodeIdentifiers1)
        result3 = element.setScaleFactors(eft1, [-1.0])
        print('create element PO 6', elementIdentifier, result2, result3, nodeIdentifiers1 )
        elementIdentifier += 1

        # LV/RV junction 1: element collapsed on 3 edges
        eft1 = tricubichermite.createEftBasic()
        ln_map = [ 1, 1, 2, 2, 3, 4, 5, 5 ]
        remapEftLocalNodes(eft1, 5, ln_map)
        setEftScaleFactorIds(eft1, [1, 2], [])
        for n in [0, 1, 2, 3, 6, 7]:
            ln = ln_map[n]
            eft1.setFunctionNumberOfTerms(n*8 + 2, 0)
        for n in [0]:
            ln = ln_map[n]
            mapEftFunction1Node1Term(eft1, n*8 + 5, ln, Node.VALUE_LABEL_D_DS1, 1, [])
        for n in [1]:
            ln = ln_map[n]
            mapEftFunction1Node1Term(eft1, n*8 + 5, ln, Node.VALUE_LABEL_D_DS2, 1, [])
        for n in [4]:
            ln = ln_map[n]
            mapEftFunction1Node1Term(eft1, n*8 + 2, ln, Node.VALUE_LABEL_D_DS2, 1, [])
            #mapEftFunction1Node2Terms(eft1, n*8 + 2, ln, Node.VALUE_LABEL_D_DS1, 1, [], Node.VALUE_LABEL_D_DS2, 1, [])
            mapEftFunction1Node1Term(eft1, n*8 + 5, ln, Node.VALUE_LABEL_D_DS1, 1, [])
            # cross derivative to reduce bulge:
            mapEftFunction1Node2Terms(eft1, n*8 + 4, ln, Node.VALUE_LABEL_D_DS1, 1, [2], Node.VALUE_LABEL_D_DS2, 1, [1, 2])
        for n in [5]:
            ln = ln_map[n]
            mapEftFunction1Node1Term(eft1, n*8 + 2, ln, Node.VALUE_LABEL_D_DS3, 1, [1])
            mapEftFunction1Node1Term(eft1, n*8 + 3, ln, Node.VALUE_LABEL_D_DS1, 1, [1])
            mapEftFunction1Node1Term(eft1, n*8 + 5, ln, Node.VALUE_LABEL_D_DS2, 1, [])
        tricubichermite.setEftLinearDerivativeXi3(eft1, 3, 7, 2, 5, 1)
        tricubichermite.setEftLinearDerivativeXi3(eft1, 4, 8, 2, 5, 1)
        elementtemplate1 = mesh.createElementtemplate()
        elementtemplate1.setElementShapeType(Element.SHAPE_TYPE_CUBE)
        result = elementtemplate1.defineField(coordinates, -1, eft1)
        element = mesh.createElement(elementIdentifier, elementtemplate1)
        nodeIdentifiers1 = [ 93, 150, 94, 143, 155 ]
        result2 = element.setNodesByIdentifier(eft1, nodeIdentifiers1)
        result3 = element.setScaleFactors(eft1, [-1.0, 4.0])
        print('create element LV/RV junction 1', elementIdentifier, result2, result3, nodeIdentifiers1 )
        elementIdentifier += 1

        # LV/RV junction 2: element collapsed on 1 edge
        # This needs work - non positive jacobian
        eft1 = tricubichermite.createEftBasic()
        ln_map = [ 1, 2, 3, 4, 5, 6, 7, 7 ]
        remapEftLocalNodes(eft1, 7, ln_map)
        setEftScaleFactorIds(eft1, [1], [])
        for n in [2]:
            ln = ln_map[n]
            mapEftFunction1Node1Term(eft1, n*8 + 2, ln, Node.VALUE_LABEL_D_DS2, 1, [1])
        for n in [3]:
            ln = ln_map[n]
            mapEftFunction1Node1Term(eft1, n*8 + 2, ln, Node.VALUE_LABEL_D_DS3, 1, [])
            mapEftFunction1Node1Term(eft1, n*8 + 3, ln, Node.VALUE_LABEL_D_DS3, 1, [1])
            mapEftFunction1Node1Term(eft1, n*8 + 5, ln, Node.VALUE_LABEL_D_DS2, 1, [])
        for n in [6, 7]:
            ln = ln_map[n]
            eft1.setFunctionNumberOfTerms(n*8 + 2, 0)
        for n in [6]:
            ln = ln_map[n]
            mapEftFunction1Node1Term(eft1, n*8 + 5, ln, Node.VALUE_LABEL_D_DS2, 1, [])
        for n in [7]:
            ln = ln_map[n]
            mapEftFunction1Node1Term(eft1, n*8 + 3, ln, Node.VALUE_LABEL_D_DS3, 1, [1])
            mapEftFunction1Node1Term(eft1, n*8 + 5, ln, Node.VALUE_LABEL_D_DS2, 1, [])
        tricubichermite.setEftLinearDerivativeXi3(eft1, 3, 7, 3, 7, 1)
        elementtemplate1 = mesh.createElementtemplate()
        elementtemplate1.setElementShapeType(Element.SHAPE_TYPE_CUBE)
        result = elementtemplate1.defineField(coordinates, -1, eft1)
        element = mesh.createElement(elementIdentifier, elementtemplate1)
        nodeIdentifiers1 = [ 44, 45, 137, 160, 93, 94, 143 ]
        result2 = element.setNodesByIdentifier(eft1, nodeIdentifiers1)
        result3 = element.setScaleFactors(eft1, [-1.0])
        print('create element LV/RV junction 2', elementIdentifier, result2, result3, nodeIdentifiers1 )
        elementIdentifier += 1

        # AO 5
        eft1 = tricubichermite.createEftBasic()
        ln_map = [ 1, 2, 3, 4, 1, 2, 5, 6 ]
        remapEftLocalNodes(eft1, 6, ln_map)
        setEftScaleFactorIds(eft1, [1], [])
        for n in [0, 1]:
            ln = ln_map[n]
            eft1.setFunctionNumberOfTerms(n*8 + 5, 0)
            mapEftFunction1Node1Term(eft1, n*8 + 3, ln, Node.VALUE_LABEL_D_DS3, 1, [1])
        tricubichermite.setEftLinearDerivativeXi3(eft1, 3, 7, 3, 5, 1)
        tricubichermite.setEftLinearDerivativeXi3(eft1, 4, 8, 4, 6, 1)
        elementtemplate1 = mesh.createElementtemplate()
        elementtemplate1.setElementShapeType(Element.SHAPE_TYPE_CUBE)
        result = elementtemplate1.defineField(coordinates, -1, eft1)
        element = mesh.createElement(elementIdentifier, elementtemplate1)
        nodeIdentifiers1 = [ 160, 161, 137, 138, 143, 144 ]
        result2 = element.setNodesByIdentifier(eft1, nodeIdentifiers1)
        result3 = element.setScaleFactors(eft1, [-1.0])
        print('create element LV/RV junction 2', elementIdentifier, result2, result3, nodeIdentifiers1 )
        elementIdentifier += 1

        if False:
            # LV supraventricular crest at LA by LV free wall
            eft1 = tricubichermite.createEftBasic()
            setEftScaleFactorIds(eft1, [1], [])
            for n in [1, 5]:
                ln = n + 1
                mapEftFunction1Node1Term(eft1, n*8 + 3, ln, Node.VALUE_LABEL_D_DS1, 1, [1])
                mapEftFunction1Node1Term(eft1, n*8 + 3, ln, Node.VALUE_LABEL_D_DS1, 1, [1])
            elementtemplate1 = mesh.createElementtemplate()
            elementtemplate1.setElementShapeType(Element.SHAPE_TYPE_CUBE)
            result = elementtemplate1.defineField(coordinates, -1, eft1)
            element = mesh.createElement(elementIdentifier, elementtemplate1)
            nodeIdentifiers1 = [ 46, 47, 164, 162, 95, 96, 165, 163 ]
            result2 = element.setNodesByIdentifier(eft1, nodeIdentifiers1)
            result3 = element.setScaleFactors(eft1, [-1.0])
            print('create element supra LA 1', elementIdentifier, result2, result3, nodeIdentifiers1 )
            elementIdentifier += 1

            # LV supraventricular crest at LA by LV septum 2
            eft1 = tricubichermite.createEftBasic()
            setEftScaleFactorIds(eft1, [1], [])
            for n in [2, 3, 6, 7]:
                ln = n + 1
                mapEftFunction1Node1Term(eft1, n*8 + 3, ln, Node.VALUE_LABEL_D_DS3, 1, [1])
                mapEftFunction1Node1Term(eft1, n*8 + 5, ln, Node.VALUE_LABEL_D_DS2, 1, [])
            for n in [2, 6]:
                ln = n + 1
                mapEftFunction1Node1Term(eft1, n*8 + 3, ln, Node.VALUE_LABEL_D_DS1, 1, [1])
            for n in [3, 7]:
                ln = n + 1
                mapEftFunction1Node1Term(eft1, n*8 + 3, ln, Node.VALUE_LABEL_D_DS1, 1, [])
            elementtemplate1 = mesh.createElementtemplate()
            elementtemplate1.setElementShapeType(Element.SHAPE_TYPE_CUBE)
            result = elementtemplate1.defineField(coordinates, -1, eft1)
            element = mesh.createElement(elementIdentifier, elementtemplate1)
            nodeIdentifiers1 = [ 164, 162, 160, 161, 165, 163, 143, 144 ]
            result2 = element.setNodesByIdentifier(eft1, nodeIdentifiers1)
            result3 = element.setScaleFactors(eft1, [-1.0])
            print('create element supra LA 2', elementIdentifier, result2, result3, nodeIdentifiers1 )
            elementIdentifier += 1

            # LV supraventricular crest at LA by LV septum 3
            eft1 = tricubichermite.createEftBasic()
            setEftScaleFactorIds(eft1, [1], [])
            for n in [2, 6]:
                ln = n + 1
                #mapEftFunction1Node1Term(eft1, n*8 + 2, ln, Node.VALUE_LABEL_D_DS3, 1, [1])
                mapEftFunction1Node1Term(eft1, n*8 + 3, ln, Node.VALUE_LABEL_D_DS3, 1, [1])
                mapEftFunction1Node1Term(eft1, n*8 + 5, ln, Node.VALUE_LABEL_D_DS2, 1, [])
            for n in [3, 7]:
                ln = n + 1
                mapEftFunction1Node1Term(eft1, n*8 + 2, ln, Node.VALUE_LABEL_D_DS2, 1, [1])
            elementtemplate1 = mesh.createElementtemplate()
            elementtemplate1.setElementShapeType(Element.SHAPE_TYPE_CUBE)
            result = elementtemplate1.defineField(coordinates, -1, eft1)
            element = mesh.createElement(elementIdentifier, elementtemplate1)
            nodeIdentifiers1 = [ 45, 46, 160, 164, 94, 95, 143, 165 ]
            result2 = element.setNodesByIdentifier(eft1, nodeIdentifiers1)
            result3 = element.setScaleFactors(eft1, [-1.0])
            print('create element supra LA 3', elementIdentifier, result2, result3, nodeIdentifiers1 )
            elementIdentifier += 1

        # LV free wall to AO
        eft1 = tricubichermite.createEftBasic()
        setEftScaleFactorIds(eft1, [1], [])
        for n in [1, 5]:
            ln = n + 1
            mapEftFunction1Node1Term(eft1, n*8 + 3, ln, Node.VALUE_LABEL_D_DS1, 1, [1])
        for n in [2, 3, 6, 7]:
            ln = n + 1
            mapEftFunction1Node1Term(eft1, n*8 + 3, ln, Node.VALUE_LABEL_D_DS3, 1, [1])
            mapEftFunction1Node1Term(eft1, n*8 + 5, ln, Node.VALUE_LABEL_D_DS2, 1, [])
        for n in [3, 7]:
            ln = n + 1
            mapEftFunction1Node1Term(eft1, n*8 + 3, ln, Node.VALUE_LABEL_D_DS1, 1, [])
        elementtemplate1 = mesh.createElementtemplate()
        elementtemplate1.setElementShapeType(Element.SHAPE_TYPE_CUBE)
        result = elementtemplate1.defineField(coordinates, -1, eft1)
        element = mesh.createElement(elementIdentifier, elementtemplate1)
        nodeIdentifiers1 = [ 45, 46, 160, 161, 94, 95, 143, 144 ]
        result2 = element.setNodesByIdentifier(eft1, nodeIdentifiers1)
        result3 = element.setScaleFactors(eft1, [-1.0])
        print('create element LV free wall to AO', elementIdentifier, result2, result3, nodeIdentifiers1 )
        elementIdentifier += 1



        fm.endChange()
Exemple #8
0
    def generateMesh(region, options):
        """
        :param region: Zinc region to define model in. Must be empty.
        :param options: Dict containing options. See getDefaultOptions().
        :return: None
        """
        elementsCountUp = options['Number of elements up']
        elementsCountAround = options['Number of elements around']
        elementsCountAcrossSeptum = options['Number of elements across septum']
        elementsCountBelowSeptum = options['Number of elements below septum']
        elementsCountThroughLVWall = options[
            'Number of elements through LV wall']
        LVWallThickness = options['LV wall thickness']
        LVWallThicknessRatioApex = options['LV wall thickness ratio apex']
        LVWallThicknessRatioBase = options['LV wall thickness ratio base']
        LVBaseFlattenRatio = options['LV base flatten ratio']
        LVBaseFlattenAngleRadians = options[
            'LV base flatten angle degrees'] * math.pi / 180.0

        lengthRatio = options['Length ratio']
        RVWidthTop = options['RV width']
        RVFreeWallThickness = options['RV free wall thickness']
        septumArcAngleRadians = options[
            'Septum arc angle degrees'] * math.pi / 180.0
        useCrossDerivatives = options['Use cross derivatives']

        # generate a half sphere shell which will be edited
        sphereShellOptions = MeshType_3d_sphereshell1.getDefaultOptions()
        sphereShellOptions['Number of elements up'] = elementsCountUp * 2
        sphereShellOptions['Number of elements around'] = elementsCountAround
        sphereShellOptions[
            'Number of elements through wall'] = elementsCountThroughLVWall
        sphereShellOptions['Exclude top rows'] = elementsCountUp
        sphereShellOptions['Wall thickness'] = LVWallThickness
        sphereShellOptions[
            'Wall thickness ratio apex'] = LVWallThicknessRatioApex
        sphereShellOptions['Length ratio'] = lengthRatio
        sphereShellOptions['Element length ratio equator/apex'] = options[
            'Element length ratio equator/apex']
        MeshType_3d_sphereshell1.generateMesh(region, sphereShellOptions)

        fm = region.getFieldmodule()
        fm.beginChange()
        # find the coordinates field created for the sphere shell
        coordinates = fm.findFieldByName('coordinates').castFiniteElement()

        nodes = fm.findNodesetByFieldDomainType(Field.DOMAIN_TYPE_NODES)
        nodetemplate = nodes.createNodetemplate()
        nodetemplate.defineField(coordinates)
        nodetemplate.setValueNumberOfVersions(coordinates, -1,
                                              Node.VALUE_LABEL_VALUE, 1)
        nodetemplate.setValueNumberOfVersions(coordinates, -1,
                                              Node.VALUE_LABEL_D_DS1, 1)
        nodetemplate.setValueNumberOfVersions(coordinates, -1,
                                              Node.VALUE_LABEL_D_DS2, 1)
        nodetemplate.setValueNumberOfVersions(coordinates, -1,
                                              Node.VALUE_LABEL_D_DS3, 1)

        mesh = fm.findMeshByDimension(3)

        cache = fm.createFieldcache()

        nor = elementsCountAround
        now = 1 + elementsCountUp * nor

        # Resize elements around LV to get desired septum arc angle
        radiansPerElementOrig = 2.0 * math.pi / elementsCountAround
        radiansPerElementSeptum = septumArcAngleRadians / elementsCountAcrossSeptum
        # want LV-RV 'transition' elements to be mean size of Septum and LV FreeWall elements
        radiansRemaining = 2.0 * math.pi - septumArcAngleRadians
        elementsCountAroundLVFreeWall = elementsCountAround - elementsCountAcrossSeptum - 2
        radiansPerElementLVFreeWall = (radiansRemaining -
                                       radiansPerElementSeptum) / (
                                           elementsCountAroundLVFreeWall + 1)
        radiansPerElementTransition = 0.5 * (radiansPerElementSeptum +
                                             radiansPerElementLVFreeWall)
        #print('Element size ratio LVFreeWall / Septum', radiansPerElementLVFreeWall/radiansPerElementSeptum)
        xyz_scale = fm.createFieldConstant([
            1.0, 1.0,
            lengthRatio / 2.0 - LVWallThickness * LVWallThicknessRatioApex
        ])
        coordinates_scale = fm.createFieldMultiply(coordinates, xyz_scale)
        sp = fm.createFieldCoordinateTransformation(coordinates_scale)
        sp.setCoordinateSystemType(
            Field.COORDINATE_SYSTEM_TYPE_SPHERICAL_POLAR)
        r = fm.createFieldComponent(sp, 1)
        theta = fm.createFieldComponent(sp, 2)
        phi = fm.createFieldComponent(sp, 3)
        zero = fm.createFieldConstant([0.0])
        one = fm.createFieldConstant([1.0])  # also used as 'true'
        two_pi = fm.createFieldConstant([2.0 * math.pi])
        radians_per_element_orig = fm.createFieldConstant(
            [radiansPerElementOrig])
        half_radians_per_element_orig = fm.createFieldConstant(
            [0.5 * radiansPerElementOrig])
        radians_per_element_transition = fm.createFieldConstant(
            [radiansPerElementTransition])

        theta_continuous = fm.createFieldIf(
            fm.createFieldLessThan(theta, half_radians_per_element_orig),
            fm.createFieldAdd(theta, two_pi), theta)
        theta_offset = fm.createFieldSubtract(theta_continuous,
                                              radians_per_element_orig)
        theta_offset_septum_start = fm.createFieldConstant(
            [-0.5 * radiansPerElementOrig])
        theta_offset_septum_end = fm.createFieldConstant([
            (elementsCountAcrossSeptum + 0.5) * radiansPerElementOrig
        ])
        in_septum = fm.createFieldAnd(
            fm.createFieldGreaterThan(theta_offset, theta_offset_septum_start),
            fm.createFieldLessThan(theta_offset, theta_offset_septum_end))
        septum_scale = fm.createFieldConstant(
            [radiansPerElementSeptum / radiansPerElementOrig])
        thetaNewSeptumStart = radiansPerElementOrig + (
            radiansPerElementOrig -
            radiansPerElementSeptum) * elementsCountAcrossSeptum / 2.0
        theta_new_septum_start = fm.createFieldConstant([thetaNewSeptumStart])
        theta_new_septum = fm.createFieldAdd(
            fm.createFieldMultiply(theta_offset, septum_scale),
            theta_new_septum_start)
        lvfreewall_scale = fm.createFieldConstant(
            [radiansPerElementLVFreeWall / radiansPerElementOrig])
        theta_offset_lvfreewall_start = fm.createFieldConstant(
            [radiansPerElementOrig * (elementsCountAcrossSeptum + 1.0)])
        theta_new_lvfreewall_start = fm.createFieldConstant([
            thetaNewSeptumStart + septumArcAngleRadians +
            radiansPerElementTransition
        ])
        theta_new_lvfreewall = fm.createFieldAdd(
            fm.createFieldMultiply(
                fm.createFieldSubtract(theta_offset,
                                       theta_offset_lvfreewall_start),
                lvfreewall_scale), theta_new_lvfreewall_start)
        theta_new = fm.createFieldIf(in_septum, theta_new_septum,
                                     theta_new_lvfreewall)

        sp_new = fm.createFieldConcatenate([r, theta_new, phi])
        sp_new.setCoordinateSystemType(
            Field.COORDINATE_SYSTEM_TYPE_SPHERICAL_POLAR)
        coordinates_new_scale = fm.createFieldCoordinateTransformation(sp_new)
        coordinates_new_scale.setCoordinateSystemType(
            Field.COORDINATE_SYSTEM_TYPE_RECTANGULAR_CARTESIAN)
        coordinates_new = fm.createFieldDivide(coordinates_new_scale,
                                               xyz_scale)
        nonApexNodesetGroupField = fm.createFieldNodeGroup(nodes)
        nonApexNodesetGroup = nonApexNodesetGroupField.getNodesetGroup()
        nonApexNodesetGroup.addNodesConditional(one)  # add all
        # remove apex nodes:
        for i in range(elementsCountThroughLVWall + 1):
            node = nodes.findNodeByIdentifier(i * now + 1)
            nonApexNodesetGroup.removeNode(node)
        fieldassignment = coordinates.createFieldassignment(coordinates_new)
        fieldassignment.setNodeset(nonApexNodesetGroup)
        fieldassignment.assign()

        baseNodesetGroup = None
        baseNodeGroupField = fm.createFieldNodeGroup(nodes)
        z = fm.createFieldComponent(coordinates, 3)
        is_base = fm.createFieldGreaterThan(z,
                                            fm.createFieldConstant([-0.0001]))
        baseNodesetGroup = baseNodeGroupField.getNodesetGroup()
        baseNodesetGroup.addNodesConditional(is_base)
        #print('baseNodesetGroup.getSize()', baseNodesetGroup.getSize())

        if LVWallThicknessRatioBase != 1.0:
            # make LV walls thinner at base
            # get inside node at middle of RV
            now = 1 + elementsCountUp * elementsCountAround
            midRVnid = now - elementsCountAround + 2 + (
                elementsCountAcrossSeptum // 2)
            #print('midRVnid', midRVnid)
            midRVnode = nodes.findNodeByIdentifier(midRVnid)
            cp_coordinates = fm.createFieldCoordinateTransformation(
                coordinates)
            cp_coordinates.setCoordinateSystemType(
                Field.COORDINATE_SYSTEM_TYPE_CYLINDRICAL_POLAR)
            radius = fm.createFieldComponent(cp_coordinates, 1)
            theta = fm.createFieldComponent(cp_coordinates, 2)
            z = fm.createFieldComponent(cp_coordinates, 3)
            cache.setNode(midRVnode)
            #result, cp = cp_coordinates.evaluateReal(cache, 3)
            #coordinates.getNodeParameters(cache, -1, Node.VALUE_LABEL_VALUE, 1, 3)
            result, innerRadius = radius.evaluateReal(cache, 1)
            #print('innerRadius', innerRadius)
            ir = fm.createFieldConstant([innerRadius * 0.9999])
            radius_gt_ir = fm.createFieldGreaterThan(radius, ir)
            radius_minus_ir = fm.createFieldSubtract(radius, ir)
            thickness_scale = fm.createFieldConstant(
                [LVWallThicknessRatioBase])
            delta_radius = fm.createFieldMultiply(radius_minus_ir,
                                                  thickness_scale)
            new_radius = fm.createFieldAdd(ir, delta_radius)
            new_cp_coordinates = fm.createFieldConcatenate(
                [new_radius, theta, z])
            new_cp_coordinates.setCoordinateSystemType(
                Field.COORDINATE_SYSTEM_TYPE_CYLINDRICAL_POLAR)
            new_coordinates = fm.createFieldCoordinateTransformation(
                new_cp_coordinates)
            new_coordinates.setCoordinateSystemType(
                Field.COORDINATE_SYSTEM_TYPE_RECTANGULAR_CARTESIAN)

            fieldassignment = coordinates.createFieldassignment(
                new_coordinates)
            result = fieldassignment.setNodeset(baseNodesetGroup)
            fieldassignment.assign()

        if LVBaseFlattenRatio != 1.0:
            # flatten LV normal to mid-RV-aorta-mitral axis
            septumCentreRadians = (
                1.0 + elementsCountAcrossSeptum / 2.0) * radiansPerElementOrig
            flattenAxisRadians = septumCentreRadians + LVBaseFlattenAngleRadians - 0.5 * math.pi

            half = fm.createFieldConstant([0.5])
            minus_one = fm.createFieldConstant([-1.0])
            minus_two = fm.createFieldConstant([-2.0])

            beta_base = fm.createFieldConstant([1.0 - LVBaseFlattenRatio])
            z = fm.createFieldComponent(coordinates, 3)
            z2 = fm.createFieldMultiply(z, z)
            zero_dist_node = nodes.findNodeByIdentifier(now - nor)
            cache.setNode(zero_dist_node)
            result, z_zero_dist_value = z.evaluateReal(cache, 1)
            #print('zero dist', result, z_zero_dist_value)
            z_zerodist = fm.createFieldConstant([z_zero_dist_value])
            z_zerodist2 = fm.createFieldMultiply(z_zerodist, z_zerodist)
            z_a = fm.createFieldDivide(one, z_zerodist2)
            z_b = fm.createFieldDivide(minus_two, z_zerodist)
            zfact = fm.createFieldAdd(
                fm.createFieldAdd(fm.createFieldMultiply(z_a, z2),
                                  fm.createFieldMultiply(z_b, z)), one)
            beta = fm.createFieldMultiply(zfact,
                                          beta_base)  # 1 - squash factor
            alpha = fm.createFieldSubtract(one,
                                           beta)  # z-dependent squash factor

            psi = fm.createFieldConstant([flattenAxisRadians])
            ri = fm.createFieldConstant([0.5 - LVWallThickness])
            theta_minus_psi = fm.createFieldSubtract(theta, psi)
            cos_theta_minus_psi = fm.createFieldCos(theta_minus_psi)
            sin_theta_minus_psi = fm.createFieldSin(theta_minus_psi)
            r_minus_ri = fm.createFieldSubtract(r, ri)

            rf = fm.createFieldAdd(fm.createFieldMultiply(alpha, r),
                                   fm.createFieldMultiply(beta, r_minus_ri))
            x_new = fm.createFieldMultiply(rf, cos_theta_minus_psi)
            y_new = fm.createFieldMultiply(r, sin_theta_minus_psi)
            r2_new = fm.createFieldAdd(fm.createFieldMultiply(x_new, x_new),
                                       fm.createFieldMultiply(y_new, y_new))
            r_new = fm.createFieldSqrt(r2_new)
            theta_minus_psi_raw = fm.createFieldAtan2(y_new, x_new)
            theta_wrap = fm.createFieldAnd(
                fm.createFieldLessThan(theta_minus_psi, minus_one),
                fm.createFieldGreaterThan(theta_minus_psi_raw, one))
            theta_minus_psi_fix = fm.createFieldIf(
                theta_wrap, fm.createFieldAdd(theta_minus_psi, two_pi),
                theta_minus_psi)
            # above theta is too great; average with theta_minus_psi_raw
            theta_minus_psi_new = fm.createFieldMultiply(
                half,
                fm.createFieldAdd(theta_minus_psi_fix, theta_minus_psi_raw))
            theta_new = fm.createFieldAdd(theta_minus_psi_new, psi)
            sp_new = fm.createFieldConcatenate([r_new, theta_new, phi])
            sp_new.setCoordinateSystemType(
                Field.COORDINATE_SYSTEM_TYPE_SPHERICAL_POLAR)
            coordinates_new_scale = fm.createFieldCoordinateTransformation(
                sp_new)
            coordinates_new_scale.setCoordinateSystemType(
                Field.COORDINATE_SYSTEM_TYPE_RECTANGULAR_CARTESIAN)
            coordinates_new = fm.createFieldDivide(coordinates_new_scale,
                                                   xyz_scale)

            fieldassignment = coordinates.createFieldassignment(
                coordinates_new)
            result = fieldassignment.setNodeset(baseNodesetGroup)
            fieldassignment.assign()

        baseNodesetGroup = None

        tricubichermite = eftfactory_tricubichermite(mesh, useCrossDerivatives)
        tricubicHermiteBasis = fm.createElementbasis(
            3, Elementbasis.FUNCTION_TYPE_CUBIC_HERMITE)

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

        crossAngle = math.pi / 8
        sinCrossAngle = math.sin(crossAngle)
        cosCrossAngle = math.cos(crossAngle)
        edgeRotateCrossAngle = math.pi / 24
        sinEdgeRotateCrossAngle = math.sin(edgeRotateCrossAngle)
        cosEdgeRotateCrossAngle = math.cos(edgeRotateCrossAngle)

        # create RV nodes and modify adjoining LV nodes
        elementsCountUpRV = elementsCountUp - elementsCountBelowSeptum
        lv_bni_base = 3 + elementsCountThroughLVWall * now + (
            elementsCountBelowSeptum - 1) * elementsCountAround
        nodeIdentifier = startNodeIdentifier = getMaximumNodeIdentifier(
            nodes) + 1
        baseExtraRVWidth = LVWallThickness * (1.0 - LVWallThicknessRatioBase)
        rv_nids = []
        for n3 in range(2):  # only 1 element through RV free wall

            onInside = n3 == 0

            for n2 in range(elementsCountUpRV + 1):

                #if n2 > (elementsCountUpRV - elementsCountUpBase):
                #    theta = math.pi*0.5
                #else:
                #    theta = (n2 / (elementsCountUpRV - elementsCountUpBase))*math.pi*0.5
                #theta = (n2 / elementsCountUpRV)*math.pi*0.5
                RVWidth = RVWidthTop  #*math.sin(theta)
                #RVWidth = RVWidthTop

                #if n2 == 0:
                #    edgeOffsetInner = RVWidth*0.25
                #else:
                edgeOffsetInner = RVWidth * 0.65
                if n2 == elementsCountUpRV:
                    RVWidth += baseExtraRVWidth
                    edgeOffsetInner = edgeOffsetInner + baseExtraRVWidth * 0.5

                onBottomEdge = (n2 == 0)

                for n1 in range(elementsCountAcrossSeptum + 1):

                    onSideEdge1 = (n1 == 0)
                    onSideEdge2 = (n1 == elementsCountAcrossSeptum)
                    onSideEdge = onSideEdge1 or onSideEdge2

                    existingNodeIdentifier = lv_bni_base + n2 * nor + n1

                    baseNode = nodes.findNodeByIdentifier(
                        existingNodeIdentifier)
                    cache.setNode(baseNode)
                    result, base_x = coordinates.getNodeParameters(
                        cache, -1, Node.VALUE_LABEL_VALUE, 1, 3)
                    result, base_dx_ds1 = coordinates.getNodeParameters(
                        cache, -1, Node.VALUE_LABEL_D_DS1, 1, 3)
                    result, base_dx_ds2 = coordinates.getNodeParameters(
                        cache, -1, Node.VALUE_LABEL_D_DS2, 1, 3)
                    result, base_dx_ds3 = coordinates.getNodeParameters(
                        cache, -1, Node.VALUE_LABEL_D_DS3, 1, 3)
                    #print('node ', existingNodeIdentifier, 'dx_ds3', result, base_dx_ds3)
                    mag = math.sqrt(base_dx_ds3[0] * base_dx_ds3[0] +
                                    base_dx_ds3[1] * base_dx_ds3[1] +
                                    base_dx_ds3[2] * base_dx_ds3[2])
                    unitOutward = [
                        base_dx_ds3[0] / mag, base_dx_ds3[1] / mag,
                        base_dx_ds3[2] / mag
                    ]
                    baseRadiusAround = unitOutward[0] * base_x[0] + unitOutward[
                        1] * base_x[1] + unitOutward[2] * base_x[2]
                    rotateEdge = False
                    if onInside:
                        if onBottomEdge and onSideEdge:
                            offset = 0.0
                        elif onBottomEdge or onSideEdge:
                            offset = edgeOffsetInner
                            rotateEdge = True
                        else:
                            offset = RVWidth
                    else:
                        if onBottomEdge and onSideEdge:
                            offset = RVFreeWallThickness
                            #rotateEdge = True
                        elif onBottomEdge or onSideEdge:
                            offset = edgeOffsetInner  # + RVFreeWallThickness
                            rotateEdge = True
                        else:
                            offset = RVWidth + RVFreeWallThickness
                    x = [
                        base_x[0] + unitOutward[0] * offset,
                        base_x[1] + unitOutward[1] * offset,
                        base_x[2] + unitOutward[2] * offset,
                    ]
                    scale1 = (baseRadiusAround + offset) / baseRadiusAround
                    dx_ds1 = [
                        base_dx_ds1[0] * scale1, base_dx_ds1[1] * scale1,
                        base_dx_ds1[2] * scale1
                    ]
                    # GRC not sure if appropriate to use scale1 here:
                    dx_ds2 = [
                        base_dx_ds2[0] * scale1, base_dx_ds2[1] * scale1,
                        base_dx_ds2[2] * scale1
                    ]
                    scale3 = RVFreeWallThickness
                    dx_ds3 = [
                        unitOutward[0] * scale3, unitOutward[1] * scale3,
                        unitOutward[2] * scale3
                    ]

                    if rotateEdge:
                        if onSideEdge1:
                            rotatedOutward = [
                                cosEdgeRotateCrossAngle * base_dx_ds3[0] -
                                sinEdgeRotateCrossAngle * base_dx_ds1[0],
                                cosEdgeRotateCrossAngle * base_dx_ds3[1] -
                                sinEdgeRotateCrossAngle * base_dx_ds1[1],
                                cosEdgeRotateCrossAngle * base_dx_ds3[2] -
                                sinEdgeRotateCrossAngle * base_dx_ds1[2]
                            ]
                        elif onSideEdge2:
                            rotatedOutward = [
                                cosEdgeRotateCrossAngle * base_dx_ds3[0] +
                                sinEdgeRotateCrossAngle * base_dx_ds1[0],
                                cosEdgeRotateCrossAngle * base_dx_ds3[1] +
                                sinEdgeRotateCrossAngle * base_dx_ds1[1],
                                cosEdgeRotateCrossAngle * base_dx_ds3[2] +
                                sinEdgeRotateCrossAngle * base_dx_ds1[2]
                            ]
                        else:  # onBottomEdge:
                            rotatedOutward = [
                                cosEdgeRotateCrossAngle * base_dx_ds3[0] -
                                sinEdgeRotateCrossAngle * base_dx_ds2[0],
                                cosEdgeRotateCrossAngle * base_dx_ds3[1] -
                                sinEdgeRotateCrossAngle * base_dx_ds2[1],
                                cosEdgeRotateCrossAngle * base_dx_ds3[2] -
                                sinEdgeRotateCrossAngle * base_dx_ds2[2]
                            ]
                        mag = math.sqrt(rotatedOutward[0] * rotatedOutward[0] +
                                        rotatedOutward[1] * rotatedOutward[1] +
                                        rotatedOutward[2] * rotatedOutward[2])
                        unitRotatedOutward = [
                            rotatedOutward[0] / mag, rotatedOutward[1] / mag,
                            rotatedOutward[2] / mag
                        ]
                        scale3r = RVFreeWallThickness  # RVFreeWallThickness + edgeOffsetInner
                        if not onInside:
                            x[0] += unitRotatedOutward[0] * scale3r
                            x[1] += unitRotatedOutward[1] * scale3r
                            x[2] += unitRotatedOutward[2] * scale3r
                        dx_ds3 = [
                            unitRotatedOutward[0] * scale3r,
                            unitRotatedOutward[1] * scale3r,
                            unitRotatedOutward[2] * scale3r
                        ]
                        if onBottomEdge:
                            rscale2 = math.sqrt(dx_ds2[0] * dx_ds2[0] +
                                                dx_ds2[1] * dx_ds2[1] +
                                                dx_ds2[2] * dx_ds2[2])
                            tang = [
                                unitRotatedOutward[1] * dx_ds1[2] -
                                unitRotatedOutward[2] * dx_ds1[1],
                                unitRotatedOutward[2] * dx_ds1[0] -
                                unitRotatedOutward[0] * dx_ds1[2],
                                unitRotatedOutward[0] * dx_ds1[1] -
                                unitRotatedOutward[1] * dx_ds1[0]
                            ]
                            rscale2 /= math.sqrt(tang[0] * tang[0] +
                                                 tang[1] * tang[1] +
                                                 tang[2] * tang[2])
                            dx_ds2 = [
                                tang[0] * rscale2, tang[1] * rscale2,
                                tang[2] * rscale2
                            ]
                        if onSideEdge:
                            rscale1 = math.sqrt(dx_ds1[0] * dx_ds1[0] +
                                                dx_ds1[1] * dx_ds1[1] +
                                                dx_ds1[2] * dx_ds1[2])
                            tang = [
                                dx_ds2[1] * unitRotatedOutward[2] -
                                dx_ds2[2] * unitRotatedOutward[1],
                                dx_ds2[2] * unitRotatedOutward[0] -
                                dx_ds2[0] * unitRotatedOutward[2],
                                dx_ds2[0] * unitRotatedOutward[1] -
                                dx_ds2[1] * unitRotatedOutward[0]
                            ]
                            rscale1 /= math.sqrt(tang[0] * tang[0] +
                                                 tang[1] * tang[1] +
                                                 tang[2] * tang[2])
                            dx_ds1 = [
                                tang[0] * rscale1, tang[1] * rscale1,
                                tang[2] * rscale1
                            ]

                    if onInside and onBottomEdge and onSideEdge:
                        node = baseNode
                    else:
                        node = nodes.createNode(nodeIdentifier, nodetemplate)
                        nodeIdentifier += 1
                    cache.setNode(node)
                    coordinates.setNodeParameters(cache, -1,
                                                  Node.VALUE_LABEL_VALUE, 1, x)
                    coordinates.setNodeParameters(cache, -1,
                                                  Node.VALUE_LABEL_D_DS1, 1,
                                                  dx_ds1)
                    coordinates.setNodeParameters(cache, -1,
                                                  Node.VALUE_LABEL_D_DS2, 1,
                                                  dx_ds2)
                    coordinates.setNodeParameters(cache, -1,
                                                  Node.VALUE_LABEL_D_DS3, 1,
                                                  dx_ds3)
                    rv_nids.append(node.getIdentifier())

        # create RV elements and modify adjoining LV element fields
        elementIdentifier = elementsCountThroughLVWall * elementsCountUp * elementsCountAround + 1
        scalefactors5 = [
            -1.0, sinCrossAngle, cosCrossAngle, sinCrossAngle, cosCrossAngle
        ]
        scalefactors9 = [
            -1.0, 0.5, 0.25, 0.125, 0.75, sinCrossAngle, cosCrossAngle,
            sinCrossAngle, cosCrossAngle
        ]
        eow = elementsCountUp * elementsCountAround
        RVSeptumElementIdBase = eow * (
            elementsCountThroughLVWall -
            1) + elementsCountBelowSeptum * elementsCountAround + 2

        # Add RV elements

        # RV LV joiner bottom elements: h-shape
        eftRVBottom = tricubichermite.createEftBasic()
        setEftScaleFactorIds(eftRVBottom, [1], [])
        # d/dxi2 is reversed on inside
        for ln in [1, 2]:
            n = ln - 1
            mapEftFunction1Node1Term(eftRVBottom, n * 8 + 3, ln,
                                     Node.VALUE_LABEL_D_DS2, 1, [1])
        # d/dxi3 = -d/dS2 on LV side
        for ln in [1, 2, 5, 6]:
            n = ln - 1
            mapEftFunction1Node1Term(eftRVBottom, n * 8 + 5, ln,
                                     Node.VALUE_LABEL_D_DS2, 1, [1])
        #print('eftRVBottom', eftRVBottom.validate())

        # RV LV joiner side 1 elements: h-shape
        eftRVSide1 = tricubichermite.createEftBasic()
        setEftScaleFactorIds(eftRVSide1, [1], [])
        # d/dxi1 is reversed on inside
        for ln in [1, 3]:
            n = ln - 1
            mapEftFunction1Node1Term(eftRVSide1, n * 8 + 2, ln,
                                     Node.VALUE_LABEL_D_DS1, 1, [1])
        # d/dxi3 = -d/dS1 on LV side
        for ln in [1, 3, 5, 7]:
            n = ln - 1
            mapEftFunction1Node1Term(eftRVSide1, n * 8 + 5, ln,
                                     Node.VALUE_LABEL_D_DS1, 1, [1])
        #print('eftRVSide1', eftRVSide1.validate())

        # RV LV joiner side 2 elements: h-shape
        eftRVSide2 = tricubichermite.createEftBasic()
        setEftScaleFactorIds(eftRVSide2, [1], [])
        # d/dxi1 is reversed on inside
        for ln in [2, 4]:
            n = ln - 1
            mapEftFunction1Node1Term(eftRVSide2, n * 8 + 2, ln,
                                     Node.VALUE_LABEL_D_DS1, 1, [1])
        # d/dxi3 = d/dS1 on LV side
        for ln in [2, 4, 6, 8]:
            n = ln - 1
            mapEftFunction1Node1Term(eftRVSide2, n * 8 + 5, ln,
                                     Node.VALUE_LABEL_D_DS1, 1, [])
        #print('eftRVSide2', eftRVSide2.validate())

        rv_nor = (elementsCountAcrossSeptum + 1)
        rv_now = (elementsCountUpRV + 1) * rv_nor

        for n2 in range(-1, elementsCountUpRV):

            for n1 in range(-1, elementsCountAcrossSeptum + 1):

                bni = lv_bni_base + n2 * elementsCountAround + n1
                rv_bni = n2 * rv_nor + n1
                eft1 = None

                if n2 == -1:
                    if (n1 == -1) or (n1 == elementsCountAcrossSeptum):
                        # pinched to zero thickness on 3 sides
                        eft1 = tricubichermite.createEftBasic()
                        eft1.setNumberOfLocalNodes(5)
                        if n1 == -1:
                            unpinched_n1 = 3
                            nodeIdentifiers = [
                                bni, bni + 1, bni + nor, bni + nor + 1,
                                rv_nids[rv_bni + rv_nor + rv_now + 1]
                            ]
                        else:
                            unpinched_n1 = 2
                            nodeIdentifiers = [
                                bni, bni + 1, bni + nor, bni + nor + 1,
                                rv_nids[rv_bni + rv_nor + rv_now]
                            ]
                        unpinched_n2 = unpinched_n1 + 4
                        for n in [1, 2, 3, 4]:
                            if n != unpinched_n1:
                                eft1.setFunctionNumberOfTerms(n * 8 + 5,
                                                              0)  # zero d/dxi3
                        for n in [4, 5, 6, 7]:
                            ln = 5 if (n == unpinched_n2) else n - 3
                            mapEftFunction1Node1Term(eft1, n * 8 + 1, ln,
                                                     Node.VALUE_LABEL_VALUE, 1,
                                                     [])
                            mapEftFunction1Node1Term(eft1, n * 8 + 2, ln,
                                                     Node.VALUE_LABEL_D_DS1, 1,
                                                     [])
                            mapEftFunction1Node1Term(eft1, n * 8 + 3, ln,
                                                     Node.VALUE_LABEL_D_DS2, 1,
                                                     [])
                            if n == unpinched_n2:
                                mapEftFunction1Node1Term(
                                    eft1, n * 8 + 5, ln,
                                    Node.VALUE_LABEL_D_DS3, 1, [])
                            else:
                                eft1.setFunctionNumberOfTerms(n * 8 + 5,
                                                              0)  # zero d/dxi3
                        #print('eft1 corner', eft1.validate())
                    else:
                        nodeIdentifiers = [
                            bni + nor, bni + nor + 1, rv_nids[rv_bni + rv_nor],
                            rv_nids[rv_bni + rv_nor + 1], bni, bni + 1,
                            rv_nids[rv_bni + rv_nor + rv_now],
                            rv_nids[rv_bni + rv_nor + 1 + rv_now]
                        ]
                        if n1 == 0:
                            eft1 = tricubichermite.createEftBasic()
                            # GRC to fix: uses repeated nodes instead of reducing to 7
                            #eft1.setNumberOfLocalNodes(7)
                            setEftScaleFactorIds(eft1, [1], [])
                            # d/dxi2 is zero on left side, reversed on inner right side
                            eft1.setFunctionNumberOfTerms(0 * 8 + 3, 0)
                            eft1.setFunctionNumberOfTerms(2 * 8 + 3, 0)
                            mapEftFunction1Node1Term(eft1, 1 * 8 + 3, 2,
                                                     Node.VALUE_LABEL_D_DS2, 1,
                                                     [1])
                            # d/dxi3 = -d/dS2 on LV side
                            for ln in [1, 2, 5, 6]:
                                n = ln - 1
                                mapEftFunction1Node1Term(
                                    eft1, n * 8 + 5, ln,
                                    Node.VALUE_LABEL_D_DS2, 1, [1])
                        elif n1 == (elementsCountAcrossSeptum - 1):
                            eft1 = tricubichermite.createEftBasic()
                            # GRC to fix: uses repeated nodes instead of reducing to 7
                            #eft1.setNumberOfLocalNodes(7)
                            setEftScaleFactorIds(eft1, [1], [])
                            # d/dxi2 is zero on right side, reversed on inner left side
                            eft1.setFunctionNumberOfTerms(1 * 8 + 3, 0)
                            eft1.setFunctionNumberOfTerms(3 * 8 + 3, 0)
                            mapEftFunction1Node1Term(eft1, 0 * 8 + 3, 1,
                                                     Node.VALUE_LABEL_D_DS2, 1,
                                                     [1])
                            # d/dxi3 = -d/dS2 on LV side
                            for ln in [1, 2, 5, 6]:
                                n = ln - 1
                                mapEftFunction1Node1Term(
                                    eft1, n * 8 + 5, ln,
                                    Node.VALUE_LABEL_D_DS2, 1, [1])
                        else:
                            eft1 = eftRVBottom
                else:
                    if n1 == -1:
                        nodeIdentifiers = [
                            bni + 1, rv_nids[rv_bni + 1], bni + nor + 1,
                            rv_nids[rv_bni + rv_nor + 1], bni,
                            rv_nids[rv_bni + 1 + rv_now], bni + nor,
                            rv_nids[rv_bni + rv_nor + 1 + rv_now]
                        ]
                        if n2 == 0:
                            eft1 = tricubichermite.createEftBasic()
                            # GRC to fix: uses repeated nodes instead of reducing to 7
                            #eft1.setNumberOfLocalNodes(7)
                            setEftScaleFactorIds(eft1, [1], [])
                            # d/dxi1 is zero on bottom side, reversed on inner top side
                            eft1.setFunctionNumberOfTerms(0 * 8 + 2, 0)
                            eft1.setFunctionNumberOfTerms(1 * 8 + 2, 0)
                            mapEftFunction1Node1Term(eft1, 2 * 8 + 2, 3,
                                                     Node.VALUE_LABEL_D_DS1, 1,
                                                     [1])
                            # d/dxi3 = -d/dS1 on LV side
                            for ln in [1, 3, 5, 7]:
                                n = ln - 1
                                mapEftFunction1Node1Term(
                                    eft1, n * 8 + 5, ln,
                                    Node.VALUE_LABEL_D_DS1, 1, [1])
                            #print('eft1 next', eft1.validate())
                        else:
                            eft1 = eftRVSide1
                    elif n1 == elementsCountAcrossSeptum:
                        nodeIdentifiers = [
                            rv_nids[rv_bni], bni, rv_nids[rv_bni + rv_nor],
                            bni + nor, rv_nids[rv_bni + rv_now], bni + 1,
                            rv_nids[rv_bni + rv_nor + rv_now], bni + nor + 1
                        ]
                        if n2 == 0:
                            eft1 = tricubichermite.createEftBasic()
                            # GRC to fix: uses repeated nodes instead of reducing to 7
                            #eft1.setNumberOfLocalNodes(7)
                            setEftScaleFactorIds(eft1, [1], [])
                            # d/dxi1 is zero on bottom side, reversed on inner top side
                            eft1.setFunctionNumberOfTerms(0 * 8 + 2, 0)
                            eft1.setFunctionNumberOfTerms(1 * 8 + 2, 0)
                            mapEftFunction1Node1Term(eft1, 3 * 8 + 2, 4,
                                                     Node.VALUE_LABEL_D_DS1, 1,
                                                     [1])
                            # d/dxi3 = d/dS1 on LV side
                            for ln in [2, 4, 6, 8]:
                                n = ln - 1
                                mapEftFunction1Node1Term(
                                    eft1, n * 8 + 5, ln,
                                    Node.VALUE_LABEL_D_DS1, 1, [])
                            #print('eft1 next', eft1.validate())
                        else:
                            eft1 = eftRVSide2
                    else:
                        eft1 = eft
                        nodeIdentifiers = [
                            rv_nids[rv_bni], rv_nids[rv_bni + 1],
                            rv_nids[rv_bni + rv_nor],
                            rv_nids[rv_bni + rv_nor + 1],
                            rv_nids[rv_bni + rv_now],
                            rv_nids[rv_bni + 1 + rv_now],
                            rv_nids[rv_bni + rv_nor + rv_now],
                            rv_nids[rv_bni + rv_nor + 1 + rv_now]
                        ]

                useElementtemplate = mesh.createElementtemplate()
                useElementtemplate.setElementShapeType(Element.SHAPE_TYPE_CUBE)
                result1 = useElementtemplate.defineField(coordinates, -1, eft1)
                element = mesh.createElement(elementIdentifier,
                                             useElementtemplate)
                result2 = element.setNodesByIdentifier(eft1, nodeIdentifiers)
                if eft1.getNumberOfLocalScaleFactors() == 1:
                    element.setScaleFactors(eft1, [-1.0])
                #print('RV element create', elementIdentifier, result1, result2, nodeIdentifiers)
                elementIdentifier += 1

        fm.endChange()