def generateOstiumMesh(region, options, trackSurface, centrePosition, axis1, startNodeIdentifier = 1, startElementIdentifier = 1,
        vesselMeshGroups = None):
    '''
    :param vesselMeshGroups: List (over number of vessels) of list of mesh groups to add vessel elements to.
    :return: nextNodeIdentifier, nextElementIdentifier, Ostium points tuple
    (ox[n3][n1][c], od1[n3][n1][c], od2[n3][n1][c], od3[n3][n1][c], oNodeId[n3][n1], oPositions).
    '''
    vesselsCount = options['Number of vessels']
    elementsCountAroundOstium = options['Number of elements around ostium']
    elementsCountAcross = options['Number of elements across common']
    elementsCountsAroundVessels, elementsCountAroundMid = getOstiumElementsCountsAroundVessels(elementsCountAroundOstium, elementsCountAcross, vesselsCount)
    elementsCountAroundEnd = (elementsCountAroundOstium - 2*elementsCountAroundMid)//2
    #print('\nvesselsCount', vesselsCount, 'elementsCountsAroundOstium', elementsCountAroundOstium, 'elementsCountAcross', elementsCountAcross)
    #print('--> elementsCountsAroundVessels', elementsCountsAroundVessels, 'elementsCountAroundMid', elementsCountAroundMid)
    elementsCountAlong = options['Number of elements along']
    elementsCountThroughWall = options['Number of elements through wall']
    unitScale = options['Unit scale']

    isOutlet = options['Outlet']
    ostiumRadius = 0.5*unitScale*options['Ostium diameter']
    ostiumLength = unitScale*options['Ostium length']
    ostiumWallThickness = unitScale*options['Ostium wall thickness']
    interVesselHeight = unitScale*options['Ostium inter-vessel height']
    interVesselDistance = unitScale*options['Ostium inter-vessel distance'] if (vesselsCount > 1) else 0.0
    halfInterVesselDistance = 0.5*interVesselDistance
    useCubicHermiteThroughOstiumWall = not(options['Use linear through ostium wall'])
    vesselEndDerivative = ostiumLength*options['Vessel end length factor']/elementsCountAlong
    vesselInnerRadius = 0.5*unitScale*options['Vessel inner diameter']
    vesselWallThickness = unitScale*options['Vessel wall thickness']
    vesselOuterRadius = vesselInnerRadius + vesselWallThickness
    vesselAngle1Radians = math.radians(options['Vessel angle 1 degrees'])
    vesselAngle1SpreadRadians = math.radians(options['Vessel angle 1 spread degrees'])
    vesselAngle2Radians = math.radians(options['Vessel angle 2 degrees'])
    useCubicHermiteThroughVesselWall = not(options['Use linear through vessel wall'])
    useCrossDerivatives = False  # options['Use cross derivatives']  # not implemented

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

    # track points in shape of ostium

    # get directions in plane of surface at centre:
    cx, cd1, cd2 = trackSurface.evaluateCoordinates(centrePosition, True)
    trackDirection1, trackDirection2, centreNormal = calculate_surface_axes(cd1, cd2, axis1)
    trackDirection2reverse = [ -d for d in trackDirection2 ]

    halfCircumference = math.pi*ostiumRadius
    circumference = 2.0*halfCircumference
    distance = 0.0
    elementLengthAroundOstiumMid = 0.0
    vesselsSpanAll = interVesselDistance*(vesselsCount - 1)
    vesselsSpanMid = interVesselDistance*(vesselsCount - 2)
    if vesselsCount == 1:
        elementLengthAroundOstiumEnd = circumference/elementsCountAroundOstium
        vesselOstiumPositions = [ centrePosition ]
        ocx  = [ cx ]
        ocd1 = [ trackDirection1 ]
        ocd2 = [ trackDirection2 ]
        ocd3 = [ centreNormal ]
    else:
        elementLengthAroundOstiumEnd = (circumference + 2.0*interVesselDistance)/(elementsCountAroundOstium - 2*elementsCountAroundMid)
        if elementsCountAroundMid > 0:
            elementLengthAroundOstiumMid = interVesselDistance*(vesselsCount - 2)/elementsCountAroundMid
        vesselOstiumPositions = []
        ocx  = []
        ocd1 = []
        ocd2 = []
        ocd3 = []
        for v in range(vesselsCount):
            vesselOstiumPositions.append(trackSurface.trackVector(centrePosition, trackDirection1, (v/(vesselsCount - 1) - 0.5)*vesselsSpanAll))
            x, d1, d2 = trackSurface.evaluateCoordinates(vesselOstiumPositions[-1], -1)
            d1, d2, d3 = calculate_surface_axes(d1, d2, trackDirection1)
            ocx .append(x)
            ocd1.append(d1)
            ocd2.append(d2)
            ocd3.append(d3)

    # coordinates around ostium
    ox = [ [], [] ]
    od1 = [ [], [] ]
    od2 = [ [], [] ]
    od3 = [ [], [] ]
    oPositions = []
    for n1 in range(elementsCountAroundOstium):
        elementLength = elementLengthAroundOstiumEnd
        if distance <= (vesselsSpanMid + halfInterVesselDistance):
            position = trackSurface.trackVector(centrePosition, trackDirection1, 0.5*vesselsSpanMid - distance)
            sideDirection = trackDirection2reverse
            if n1 < elementsCountAroundMid:
                elementLength = elementLengthAroundOstiumMid
        elif distance < (vesselsSpanMid + halfInterVesselDistance + halfCircumference):
            position = vesselOstiumPositions[0]
            angleRadians = (distance - (vesselsSpanMid + halfInterVesselDistance))/ostiumRadius
            w1 = -math.sin(angleRadians)
            w2 = -math.cos(angleRadians)
            sideDirection = [ (w1*trackDirection1[c] + w2*trackDirection2[c]) for c in range(3) ]
        elif distance < (2.0*vesselsSpanMid + halfInterVesselDistance + halfCircumference + interVesselDistance):
            position = trackSurface.trackVector(centrePosition, trackDirection1, distance - (1.5*vesselsSpanMid + interVesselDistance + halfCircumference))
            sideDirection = trackDirection2
            if 0 <= (n1 - elementsCountAroundEnd - elementsCountAroundMid) < elementsCountAroundMid:
                elementLength = elementLengthAroundOstiumMid
        elif distance < (2.0*vesselsSpanMid + halfInterVesselDistance + circumference + interVesselDistance):
            position = vesselOstiumPositions[-1]
            angleRadians = (distance - (2.0*vesselsSpanMid + halfInterVesselDistance + halfCircumference + interVesselDistance))/ostiumRadius
            w1 = math.sin(angleRadians)
            w2 = math.cos(angleRadians)
            sideDirection = [ (w1*trackDirection1[c] + w2*trackDirection2[c]) for c in range(3) ]
        else:
            position = trackSurface.trackVector(centrePosition, trackDirection1, 0.5*vesselsSpanMid + (circumference + 2.0*(vesselsSpanMid + interVesselDistance)) - distance)
            sideDirection = trackDirection2reverse
        position = trackSurface.trackVector(position, sideDirection, ostiumRadius)
        oPositions.append(position)
        px, d1, d2 = trackSurface.evaluateCoordinates(position, True)
        pd2, pd1, pd3 = calculate_surface_axes(d1, d2, sideDirection)
        # get outer coordinates
        opx = px
        opd1 = vector.setMagnitude([ -d for d in pd1 ], elementLengthAroundOstiumEnd)
        opd2 = vector.setMagnitude(pd2, elementLengthAroundOstiumEnd)  # smoothed later
        opd3 = vector.setMagnitude(pd3, ostiumWallThickness)
        # set inner and outer coordinates (use copy to avoid references to same list later)
        ox [0].append([ (opx[c] - opd3[c]) for c in range(3) ])
        od1[0].append(copy.copy(opd1))
        od2[0].append(copy.copy(opd2))
        ox [1].append(opx)
        od1[1].append(opd1)
        od2[1].append(opd2)
        if useCubicHermiteThroughOstiumWall:
            od3[0].append(copy.copy(opd3))
            od3[1].append(opd3)
        distance += elementLength
    for n3 in range(2):
        od1[n3] = interp.smoothCubicHermiteDerivativesLoop(ox[n3], od1[n3], fixAllDirections = True)

    xx  = []
    xd1 = []
    xd2 = []
    xd3 = []
    # coordinates across common ostium, between vessels
    nodesCountFreeEnd = elementsCountsAroundVessels[0] + 1 - elementsCountAcross
    oinc = 0 if (vesselsCount <= 2) else elementsCountAroundMid//(vesselsCount - 2)
    for iv in range(vesselsCount - 1):
        xx .append([ None, None ])
        xd1.append([ None, None ])
        xd2.append([ None, None ])
        xd3.append([ None, None ])
        oa = elementsCountAroundMid - iv*oinc
        ob = elementsCountAroundMid + nodesCountFreeEnd - 1 + iv*oinc
        nx = [ ox[1][oa], ox[1][ob] ]
        nd1 = [ [ -d for d in od1[1][oa] ], od1[1][ob] ]
        nd2 = [ [ -d for d in od2[1][oa] ], od2[1][ob] ]
        if elementsCountAcross > 1:
            # add centre point, displaced by interVesselHeight
            if vesselsCount == 2:
                position = centrePosition
            else:
                position = trackSurface.trackVector(centrePosition, trackDirection1, (iv/(vesselsCount - 2) - 0.5)*vesselsSpanMid)
            mx, d1, d2 = trackSurface.evaluateCoordinates(position, derivatives = True)
            md1, md2, md3 = calculate_surface_axes(d1, d2, trackDirection1)
            nx .insert(1, [ (mx[c] + interVesselHeight*md3[c]) for c in range(3) ])
            nd1.insert(1, vector.setMagnitude(md1, elementLengthAroundOstiumMid if (0 < iv < (vesselsCount - 2)) else elementLengthAroundOstiumEnd))
            nd2.insert(1, vector.setMagnitude(md2, ostiumRadius))
        nd2 = interp.smoothCubicHermiteDerivativesLine(nx, nd2, fixAllDirections = True)
        px, pd2, pe, pxi = interp.sampleCubicHermiteCurves(nx, nd2, elementsCountAcross)[0:4]
        pd1 = interp.interpolateSampleLinear(nd1, pe, pxi)
        pd3 = [ vector.setMagnitude(vector.crossproduct3(pd1[n2], pd2[n2]), ostiumWallThickness) for n2 in range(elementsCountAcross + 1) ]
        lx = [ ([ (px[n2][c] - pd3[n2][c]) for c in range(3) ]) for n2 in range(elementsCountAcross + 1) ]
        ld2 = interp.smoothCubicHermiteDerivativesLine(lx, pd2, fixAllDirections = True)
        xx [iv][0] = lx [1:elementsCountAcross]
        xd1[iv][0] = copy.deepcopy(pd1[1:elementsCountAcross])  # to be smoothed later
        xd2[iv][0] = ld2[1:elementsCountAcross]
        xx [iv][1] = px [1:elementsCountAcross]
        xd1[iv][1] = pd1[1:elementsCountAcross]  # to be smoothed later
        xd2[iv][1] = pd2[1:elementsCountAcross]
        if useCubicHermiteThroughOstiumWall:
            xd3[iv][0] = copy.deepcopy(pd3[1:elementsCountAcross])
            xd3[iv][1] = pd3[1:elementsCountAcross]
        # set smoothed d2 on ostium circumference
        od2[0][oa] = [ -d for d in ld2[0] ]
        od2[1][oa] = [ -d for d in pd2[0] ]
        od2[0][ob] = ld2[-1]
        od2[1][ob] = pd2[-1]

    # get positions of vessel end centres and rings
    vcx = []
    vcd1 = []
    vcd2 = []
    vcd3 = []
    vox = []
    vod1 = []
    vod2 = []
    vod3 = []
    for v in range(vesselsCount):
        elementsCountAroundVessel = elementsCountsAroundVessels[v]
        radiansPerElementVessel = 2.0*math.pi/elementsCountAroundVessel
        useVesselAngleRadians = vesselAngle1Radians
        if vesselsCount > 1:
            useVesselAngleRadians += (v/(vesselsCount - 1) - 0.5)*vesselAngle1SpreadRadians
        vx, vd1, vd2, vd3 = getCircleProjectionAxes(ocx[v], ocd1[v], ocd2[v], ocd3[v], ostiumLength, useVesselAngleRadians, vesselAngle2Radians)
        vd1 = [    vesselOuterRadius*d for d in vd1 ]
        vd2 = [   -vesselOuterRadius*d for d in vd2 ]
        vd3 = [ -vesselEndDerivative*d for d in vd3 ]
        vcx.append(vx)
        vcd1.append(vd1)
        vcd2.append(vd2)
        vcd3.append(vd3)
        vox.append([])
        vod1.append([])
        vod2.append([])
        vod3.append([])
        for n3 in range(2):
            radius = vesselInnerRadius if (n3 == 0) else vesselOuterRadius
            vAxis1 = vector.setMagnitude(vd1, radius)
            vAxis2 = vector.setMagnitude(vd2, radius)
            if vesselsCount == 1:
                startRadians = 0.5*math.pi
            else:
                startRadians = 0.5*radiansPerElementVessel*elementsCountAcross
                if v == (vesselsCount - 1):
                    startRadians -= math.pi
            px, pd1 = createCirclePoints(vx, vAxis1, vAxis2, elementsCountAroundVessel, startRadians)
            vox [-1].append(px)
            vod1[-1].append(pd1)
            vod2[-1].append([ vd3 ]*elementsCountAroundVessel)
            if useCubicHermiteThroughVesselWall:
                vod3[-1].append([ vector.setMagnitude(vector.crossproduct3(d1, vd3), vesselWallThickness) for d1 in pd1 ])

    # calculate common ostium vessel node derivatives map
    mvPointsx = [ None ]*vesselsCount
    mvPointsd1 = [ None ]*vesselsCount
    mvPointsd2 = [ None ]*vesselsCount
    mvPointsd3 = [ None ]*vesselsCount
    mvDerivativesMap = [ None ]*vesselsCount
    mvMeanCount = [ None ]*vesselsCount  # stores 1 if first reference to common point between vessels, 2 if second. Otherwise 0.
    for v in range(vesselsCount):
        if vesselsCount == 1:
            mvPointsx[v], mvPointsd1[v], mvPointsd2[v], mvPointsd3[v], mvDerivativesMap[v] = \
                ox, od1, od2, od3 if useCubicHermiteThroughOstiumWall else None, None
            mvMeanCount[v] = [ 0 ]*elementsCountsAroundVessels[v]
        else:
            iv = max(0, v - 1)
            oa = elementsCountAroundMid - iv*oinc
            ob = elementsCountAroundMid + nodesCountFreeEnd - 1 + iv*oinc
            mvPointsx [v] = []
            mvPointsd1[v] = []
            mvPointsd2[v] = []
            mvPointsd3[v] = [] if useCubicHermiteThroughOstiumWall else None
            mvDerivativesMap[v] = []
            for n3 in range(2):
                mvPointsd1[v].append([])
                mvPointsd2[v].append([])
                mvPointsx [v].append([])
                if useCubicHermiteThroughOstiumWall:
                    mvPointsd3[v].append([])
                mvDerivativesMap[v].append([])
                if v == 0:  # first end vessel
                    mvPointsd1[v][n3] += od1[n3][oa:ob + 1]
                    mvPointsd2[v][n3] += od2[n3][oa:ob + 1]
                    mvPointsx [v][n3] += ox [n3][oa:ob + 1]
                    if useCubicHermiteThroughOstiumWall:
                        mvPointsd3[v][n3] += od3[n3][oa:ob + 1]
                    mvDerivativesMap[v][n3].append( ( (0, 1, 0), (-1, 1, 0), None, (1, 0, 0) ) )
                    for i in range(nodesCountFreeEnd - 2):
                        mvDerivativesMap[v][n3].append( ( None, None, None ) )
                    mvDerivativesMap[v][n3].append( ( (1, 0, 0), (1, 1, 0), None, (0, -1, 0) ) )
                    mvPointsx [v][n3] += reversed(xx [iv][n3])
                    mvPointsd1[v][n3] += reversed(xd1[iv][n3])
                    mvPointsd2[v][n3] += reversed(xd2[iv][n3])
                    if useCubicHermiteThroughOstiumWall:
                        mvPointsd3[v][n3] += reversed(xd3[iv][n3])
                    for i in range(elementsCountAcross - 1):
                        mvDerivativesMap[v][n3].append( ( (0, -1, 0), (1, 0, 0), None ) )
                    if n3 == 0:
                        mvMeanCount[v] = [ 1 ] + [ 0 ]*(nodesCountFreeEnd - 2) + [ 1 ]*elementsCountAcross
                elif v < (vesselsCount - 1):  # middle vessels
                    # left:
                    mvPointsx [v][n3] += ox [n3][oa - oinc:oa + 1]
                    mvPointsd1[v][n3] += od1[n3][oa - oinc:oa + 1]
                    mvPointsd2[v][n3] += od2[n3][oa - oinc:oa + 1]
                    if useCubicHermiteThroughOstiumWall:
                        mvPointsd3[v][n3] += od3[n3][oa - oinc:oa + 1]
                    mvDerivativesMap[v][n3].append( ( (0, 1, 0), (-1, 1, 0), None, (1, 0, 0) ) )
                    for i in range(oinc - 1):
                        mvDerivativesMap[v][n3].append( ( None, None, None ) )
                    mvDerivativesMap[v][n3].append( ( (1, 0, 0), (1, 1, 0), None, (0, -1, 0) ) )
                    # across
                    mvPointsx [v][n3] += xx [iv][n3]
                    mvPointsd1[v][n3] += xd1[iv][n3]
                    mvPointsd2[v][n3] += xd2[iv][n3]
                    if useCubicHermiteThroughOstiumWall:
                        mvPointsd3[v][n3] += xd3[iv][n3]
                    for i in range(elementsCountAcross - 1):
                        mvDerivativesMap[v][n3].append( ( (0, 1, 0), (-1, 0, 0), None ) )
                    # right
                    mvPointsx [v][n3] += ox [n3][ob:ob + oinc + 1]
                    mvPointsd1[v][n3] += od1[n3][ob:ob + oinc + 1]
                    mvPointsd2[v][n3] += od2[n3][ob:ob + oinc + 1]
                    if useCubicHermiteThroughOstiumWall:
                        mvPointsd3[v][n3] += od3[n3][ob:ob + oinc + 1]
                    mvDerivativesMap[v][n3].append( ( (0, 1, 0), (-1, 1, 0), None, (1, 0, 0) ) )
                    for i in range(oinc - 1):
                        mvDerivativesMap[v][n3].append( ( None, None, None ) )
                    mvDerivativesMap[v][n3].append( ( (1, 0, 0), (1, 1, 0), None, (0, -1, 0) ) )
                    # across reverse
                    mvPointsx [v][n3] += reversed(xx [iv + 1][n3])
                    mvPointsd1[v][n3] += reversed(xd1[iv + 1][n3])
                    mvPointsd2[v][n3] += reversed(xd2[iv + 1][n3])
                    if useCubicHermiteThroughOstiumWall:
                        mvPointsd3[v][n3] += reversed(xd3[iv + 1][n3])
                    for i in range(elementsCountAcross - 1):
                        mvDerivativesMap[v][n3].append( ( (0, -1, 0), (1, 0, 0), None ) )
                    if n3 == 0:
                        mvMeanCount[v] = [ 1 ] + [ 0 ]*(oinc - 1) + [ 2 ]*(elementsCountAcross + 1) + [ 0 ]*(oinc - 1) + [ 1 ]*elementsCountAcross
                else:  # last end vessel
                    mvPointsx [v][n3] += ox [n3][ob:] + [ ox [n3][0] ]
                    mvPointsd1[v][n3] += od1[n3][ob:] + [ od1[n3][0] ]
                    mvPointsd2[v][n3] += od2[n3][ob:] + [ od2[n3][0] ]
                    if useCubicHermiteThroughOstiumWall:
                        mvPointsd3[v][n3] += od3[n3][ob:] + [ od3[n3][0] ]
                    mvDerivativesMap[v][n3].append( ( (0, 1, 0), (-1, 1, 0), None, (1, 0, 0) ) )
                    for i in range(nodesCountFreeEnd - 2):
                        mvDerivativesMap[v][n3].append( ( None, None, None ) )
                    mvDerivativesMap[v][n3].append( ( (1, 0, 0), (1, 1, 0), None, (0, -1, 0) ) )
                    mvPointsx [v][n3] += xx [iv][n3]
                    mvPointsd1[v][n3] += xd1[iv][n3]
                    mvPointsd2[v][n3] += xd2[iv][n3]
                    if useCubicHermiteThroughOstiumWall:
                        mvPointsd3[v][n3] += xd3[iv][n3]
                    for i in range(elementsCountAcross - 1):
                        mvDerivativesMap[v][n3].append( ( (0, 1, 0), (-1, 0, 0), None ) )
                    if n3 == 0:
                        mvMeanCount[v] = [ 2 ] + [ 0 ]*(nodesCountFreeEnd - 2) + [ 2 ]*elementsCountAcross

    # calculate derivative 2 around free sides of inlets to fit vessel derivatives
    for v in range(vesselsCount):
        for n3 in range(2):
            #print('v',v,'n3',n3,'elementsAround',elementsCountsAroundVessels[v])
            #print('mvPointsx [v][n3]', mvPointsx [v][n3])
            #print('mvPointsd1[v][n3]', mvPointsd1[v][n3])
            #print('mvPointsd2[v][n3]', mvPointsd2[v][n3])
            #print('mvDerivativesMap[v][n3]', mvDerivativesMap[v][n3])
            for n1 in range(elementsCountsAroundVessels[v]):
                d2Map = mvDerivativesMap[v][n3][n1][1] if (mvDerivativesMap[v] and mvDerivativesMap[v][n3][n1]) else None
                sf1 = d2Map[0] if d2Map else 0.0
                sf2 = d2Map[1] if d2Map else 1.0
                nx = [ vox[v][n3][n1], mvPointsx[v][n3][n1] ]
                nd2 = [ [ d*elementsCountAlong for d in vod2[v][n3][n1] ], [ (sf1*mvPointsd1[v][n3][n1][c] + sf2*mvPointsd2[v][n3][n1][c]) for c in range(3) ] ]
                nd2f = interp.smoothCubicHermiteDerivativesLine(nx, nd2, fixStartDerivative = True, fixEndDirection = True)
                ndf = [ d/elementsCountAlong for d in nd2f[1] ]
                # assign components to set original values:
                if sf1 == 0:
                    for c in range(3):
                        mvPointsd2[v][n3][n1][c] = sf2*ndf[c]
                elif sf2 == 0:
                    if mvMeanCount[v][n1] < 2:
                        for c in range(3):
                            mvPointsd1[v][n3][n1][c] = sf1*ndf[c]
                    else:
                        # take mean of values from this and last vessel
                        for c in range(3):
                            mvPointsd1[v][n3][n1][c] = 0.5*(mvPointsd1[v][n3][n1][c] + sf1*ndf[c])
                else:
                    #print('v', v, 'n3', n3, 'n1', n1, ':', vector.magnitude(ndf), 'vs.', vector.magnitude(nd2[1]), 'd2Map', d2Map)
                    pass

    if isOutlet:
        # reverse directions of d1 and d2 on vessels and ostium base
        for c in range(3):
            for n3 in range(2):
                for n1 in range(elementsCountAroundOstium):
                    od1[n3][n1][c] = -od1[n3][n1][c]
                    od2[n3][n1][c] = -od2[n3][n1][c]
                for iv in range(vesselsCount - 1):
                    for n1 in range(elementsCountAcross - 1):
                        xd1[iv][n3][n1][c] = -xd1[iv][n3][n1][c]
                        xd2[iv][n3][n1][c] = -xd2[iv][n3][n1][c]
                for v in range(vesselsCount):
                    for n1 in range(elementsCountsAroundVessels[v]):
                        vod1[v][n3][n1][c] = -vod1[v][n3][n1][c]
            # d2 is referenced all around, so only change once per vessel
            for v in range(vesselsCount):
                vod2[v][0][0][c] = -vod2[v][0][0][c]

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

    nodes = fm.findNodesetByFieldDomainType(Field.DOMAIN_TYPE_NODES)

    nodetemplate = nodes.createNodetemplate()
    nodetemplate.defineField(coordinates)
    nodetemplate.setValueNumberOfVersions(coordinates, -1, Node.VALUE_LABEL_VALUE, 1)
    nodetemplate.setValueNumberOfVersions(coordinates, -1, Node.VALUE_LABEL_D_DS1, 1)
    nodetemplate.setValueNumberOfVersions(coordinates, -1, Node.VALUE_LABEL_D_DS2, 1)
    nodetemplate.setValueNumberOfVersions(coordinates, -1, Node.VALUE_LABEL_D_DS3, 1)
    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)

    nodeIdentifier = startNodeIdentifier

    oNodeId = []
    for n3 in range(2):
        oNodeId.append([])
        for n1 in range(elementsCountAroundOstium):
            node = nodes.createNode(nodeIdentifier, nodetemplate if useCubicHermiteThroughOstiumWall else nodetemplateLinearS3)
            cache.setNode(node)
            coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_VALUE, 1, ox [n3][n1])
            coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS1, 1, od1[n3][n1])
            coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS2, 1, od2[n3][n1])
            if useCubicHermiteThroughOstiumWall:
                coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS3, 1, od3[n3][n1])
            oNodeId[n3].append(nodeIdentifier)
            nodeIdentifier += 1

    xNodeId = []
    for iv in range(vesselsCount - 1):
        xNodeId.append([])
        for n3 in range(2):
            xNodeId[iv].append([])
            for n2 in range(elementsCountAcross - 1):
                node = nodes.createNode(nodeIdentifier, nodetemplate if useCubicHermiteThroughOstiumWall else nodetemplateLinearS3)
                cache.setNode(node)
                coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_VALUE, 1, xx [iv][n3][n2])
                coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS1, 1, xd1[iv][n3][n2])
                coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS2, 1, xd2[iv][n3][n2])
                if useCubicHermiteThroughOstiumWall:
                    coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS3, 1, xd3[iv][n3][n2])
                xNodeId[iv][n3].append(nodeIdentifier)
                nodeIdentifier += 1

    #for v in range(vesselsCount):
    #    node = nodes.createNode(nodeIdentifier, nodetemplate)
    #    cache.setNode(node)
    #    coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_VALUE, 1, vcx [v])
    #    coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS1, 1, vcd1[v])
    #    coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS2, 1, vcd2[v])
    #    coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS3, 1, vcd3[v])
    #    nodeIdentifier += 1
    #    for n3 in range(2):
    #        for n1 in range(elementsCountsAroundVessels[v]):
    #            node = nodes.createNode(nodeIdentifier, nodetemplate if useCubicHermiteThroughVesselWall else nodetemplateLinearS3)
    #            cache.setNode(node)
    #            coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_VALUE, 1, vox [v][n3][n1])
    #            coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS1, 1, vod1[v][n3][n1])
    #            coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS2, 1, vod2[v][n3][n1])
    #            if useCubicHermiteThroughVesselWall:
    #                coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS3, 1, vod3[v][n3][n1])
    #            #vNodeId.append(nodeIdentifier)
    #            nodeIdentifier += 1

    # get identifiers of nodes around each vessel at ostium end
    mvNodeId = [ None ]*vesselsCount
    for v in range(vesselsCount):
        if vesselsCount == 1:
            mvNodeId[v] = oNodeId
        else:
            iv = max(0, v - 1)
            mvNodeId[v] = [ None, None ]
            oa = elementsCountAroundMid - iv*oinc
            ob = elementsCountAroundMid + nodesCountFreeEnd - 1 + iv*oinc
            for n3 in range(2):
                if v == 0:  # first end vessel
                    mvNodeId[v][n3] = oNodeId[n3][oa:ob + 1] + (list(reversed(xNodeId[iv][n3])) if (v == 0) else xNodeId[iv][n3])
                elif v == (vesselsCount - 1):  # last end vessels
                    mvNodeId[v][n3] = oNodeId[n3][ob:] + [ oNodeId[n3][0] ] + (list(reversed(xNodeId[iv][n3])) if (v == 0) else xNodeId[iv][n3])
                else:  # mid vessels
                    mvNodeId[v][n3] = oNodeId[n3][oa - oinc:oa + 1] + xNodeId[iv][n3] + oNodeId[n3][ob:ob + oinc + 1] + list(reversed(xNodeId[iv + 1][n3]))

    #################
    # Create elementss
    #################

    mesh = fm.findMeshByDimension(3)
    elementIdentifier = startElementIdentifier

    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)

    #elementtemplateX = mesh.createElementtemplate()
    #elementtemplateX.setElementShapeType(Element.SHAPE_TYPE_CUBE)

    for v in range(vesselsCount):
        if isOutlet:
            startPointsx, startPointsd1, startPointsd2, startPointsd3, startNodeId, startDerivativesMap = \
                mvPointsx[v], mvPointsd1[v], mvPointsd2[v], mvPointsd3[v], mvNodeId[v], mvDerivativesMap[v]
            endPointsx, endPointsd1, endPointsd2, endPointsd3, endNodeId, endDerivativesMap = \
                vox[v], vod1[v], vod2[v], vod3[v] if useCubicHermiteThroughVesselWall else None, None, None
            # reverse order of nodes around:
            for px in [ startPointsx, startPointsd1, startPointsd2, startPointsd3, startNodeId, startDerivativesMap, \
                        endPointsx, endPointsd1, endPointsd2, endPointsd3, endNodeId, endDerivativesMap ]:
                if px:
                    for n3 in range(2):
                        px[n3] = [ px[n3][0] ] + px[n3][len(px[n3]) - 1:0:-1]
            if vesselsCount > 1:
                # must switch in and out xi1 maps around corners in startDerivativesMap
                for n3 in range(2):
                    for n1 in range(elementsCountsAroundVessels[v]):
                        derivativesMap = startDerivativesMap[n3][n1]
                        if len(derivativesMap) == 4:
                            startDerivativesMap[n3][n1] = derivativesMap[3], derivativesMap[1], derivativesMap[2], derivativesMap[0]
        else:
            startPointsx, startPointsd1, startPointsd2, startPointsd3, startNodeId, startDerivativesMap = \
                vox[v], vod1[v], vod2[v], vod3[v] if useCubicHermiteThroughVesselWall else None, None, None
            endPointsx, endPointsd1, endPointsd2, endPointsd3, endNodeId, endDerivativesMap = \
                mvPointsx[v], mvPointsd1[v], mvPointsd2[v], mvPointsd3[v], mvNodeId[v], mvDerivativesMap[v]
        #print('endPointsx ', endPointsx )
        #print('endPointsd1', endPointsd1)
        #print('endPointsd2', endPointsd2)
        #print('endPointsd3', endPointsd3)
        #print('endNodeId', endNodeId)
        #print('endDerivativesMap', endDerivativesMap)
        nodeIdentifier, elementIdentifier = createAnnulusMesh3d(
            nodes, mesh, nodeIdentifier, elementIdentifier,
            startPointsx, startPointsd1, startPointsd2, startPointsd3, startNodeId, startDerivativesMap,
            endPointsx, endPointsd1, endPointsd2, endPointsd3, endNodeId, endDerivativesMap,
            forceMidLinearXi3 = not useCubicHermiteThroughVesselWall,
            elementsCountRadial = elementsCountAlong,
            meshGroups = vesselMeshGroups[v] if vesselMeshGroups else [])

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

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

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

        mesh = fm.findMeshByDimension(3)

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

        fm.endChange()
        return annotationGroups
예제 #3
0
    def getTriplePoints(self, n3):
        '''
        Compute coordinates and derivatives of points where 3 square elements merge.
        :param n3: Index of through-wall coordinates to use.
        '''
        n1a = self.elementsCountRim
        n1b = n1a + 1
        n1c = n1a + 2
        m1a = self.elementsCountAcross - self.elementsCountRim
        m1b = m1a - 1
        m1c = m1a - 2
        n2a = self.elementsCountRim
        n2b = n2a + 1
        n2c = n2a + 2
        # left
        ltx = []

        if self._type == ShieldRimDerivativeMode.SHIELD_RIM_DERIVATIVE_MODE_AROUND:
            tx, td1 = sampleCubicHermiteCurves(
                [self.px[n3][n2a][n1c], self.px[n3][n2c][n1b]],
                [[(-self.pd1[n3][n2a][n1c][c] - self.pd3[n3][n2a][n1c][c])
                  for c in range(3)], self.pd1[n3][n2c][n1b]],
                2,
                arcLengthDerivatives=True)[0:2]
            ltx.append(tx[1])
            tx, td1 = sampleCubicHermiteCurves(
                [self.px[n3][n2a][n1b], self.px[n3][n2c][n1c]],
                [[-self.pd3[n3][n2a][n1b][c] for c in range(3)],
                 [(self.pd1[n3][n2c][n1c][c] + self.pd3[n3][n2c][n1c][c])
                  for c in range(3)]],
                2,
                arcLengthDerivatives=True)[0:2]
            ltx.append(tx[1])
            tx, td1 = sampleCubicHermiteCurves(
                [self.px[n3][n2c][n1a], self.px[n3][n2b][n1c]],
                [[(self.pd1[n3][n2c][n1a][c] - self.pd3[n3][n2c][n1a][c])
                  for c in range(3)], self.pd3[n3][n2b][n1c]],
                2,
                arcLengthDerivatives=True)[0:2]
            ltx.append(tx[1])
        elif self._type == ShieldRimDerivativeMode.SHIELD_RIM_DERIVATIVE_MODE_REGULAR:
            tx, td1 = sampleCubicHermiteCurves(
                [self.px[n3][n2a][n1c], self.px[n3][n2c][n1b]],
                [[(-self.pd1[n3][n2a][n1c][c] + self.pd2[n3][n2a][n1c][c])
                  for c in range(3)], self.pd2[n3][n2c][n1b]],
                2,
                arcLengthDerivatives=True)[0:2]
            ltx.append(tx[1])
            tx, td1 = sampleCubicHermiteCurves(
                [self.px[n3][n2a][n1b], self.px[n3][n2c][n1c]], [
                    self.pd2[n3][n2a][n1b],
                    [(self.pd1[n3][n2c][n1c][c] + self.pd2[n3][n2c][n1c][c])
                     for c in range(3)]
                ],
                2,
                arcLengthDerivatives=True)[0:2]
            ltx.append(tx[1])
            tx, td1 = sampleCubicHermiteCurves(
                [self.px[n3][n2c][n1a], self.px[n3][n2b][n1c]],
                [[(self.pd1[n3][n2c][n1a][c] - self.pd2[n3][n2c][n1a][c])
                  for c in range(3)], self.pd1[n3][n2b][n1c]],
                2,
                arcLengthDerivatives=True)[0:2]
            ltx.append(tx[1])
        #x = [ (ltx[0][c] + ltx[1][c] + ltx[2][c])/3.0 for c in range(3) ]
        x = [(ltx[0][c] + ltx[2][c]) / 2.0 for c in range(3)]
        if self.trackSurface:
            p = self.trackSurface.findNearestPosition(
                x,
                startPosition=self.trackSurface.createPositionProportion(
                    *(self.pProportions[n2b][n1c])))
            self.pProportions[n2b][n1b] = self.trackSurface.getProportion(p)
            x, sd1, sd2 = self.trackSurface.evaluateCoordinates(
                p, derivatives=True)
            d1, d2, d3 = calculate_surface_axes(sd1, sd2,
                                                vector.normalise(sd1))
            self.pd3[n3][n2b][n1b] = d3
        self.px[n3][n2b][n1b] = x
        if self._type == ShieldRimDerivativeMode.SHIELD_RIM_DERIVATIVE_MODE_AROUND:
            self.pd3[n3][n2b][n1b] = [
                (self.px[n3][n2b][n1c][c] - self.px[n3][n2b][n1b][c])
                for c in range(3)
            ]
            self.pd1[n3][n2b][n1b] = [
                (self.px[n3][n2c][n1b][c] - self.px[n3][n2b][n1b][c])
                for c in range(3)
            ]
        elif self._type == ShieldRimDerivativeMode.SHIELD_RIM_DERIVATIVE_MODE_REGULAR:
            self.pd1[n3][n2b][n1b] = [
                (self.px[n3][n2b][n1c][c] - self.px[n3][n2b][n1b][c])
                for c in range(3)
            ]
            self.pd2[n3][n2b][n1b] = [
                (self.px[n3][n2c][n1b][c] - self.px[n3][n2b][n1b][c])
                for c in range(3)
            ]
        if not self.trackSurface:
            if self._type == ShieldRimDerivativeMode.SHIELD_RIM_DERIVATIVE_MODE_AROUND:
                self.pd2[n3][n2b][n1b] = vector.normalise(
                    vector.crossproduct3(self.pd3[n3][n2b][n1b],
                                         self.pd1[n3][n2b][n1b]))
            elif self._type == ShieldRimDerivativeMode.SHIELD_RIM_DERIVATIVE_MODE_REGULAR:
                self.pd3[n3][n2b][n1b] = vector.normalise(
                    vector.crossproduct3(self.pd1[n3][n2b][n1b],
                                         self.pd2[n3][n2b][n1b]))
        # right
        rtx = []
        if self._type == ShieldRimDerivativeMode.SHIELD_RIM_DERIVATIVE_MODE_AROUND:
            tx, td1 = sampleCubicHermiteCurves(
                [self.px[n3][n2a][m1c], self.px[n3][n2c][m1b]],
                [[(self.pd1[n3][n2a][m1c][c] - self.pd3[n3][n2a][m1c][c])
                  for c in range(3)], self.pd1[n3][n2c][m1b]],
                2,
                arcLengthDerivatives=True)[0:2]
            rtx.append(tx[1])
            tx, td1 = sampleCubicHermiteCurves(
                [self.px[n3][n2a][m1b], self.px[n3][n2c][m1c]],
                [[-self.pd3[n3][n2a][m1b][c] for c in range(3)],
                 [(-self.pd3[n3][n2c][m1c][c] + self.pd1[n3][n2c][m1c][c])
                  for c in range(3)]],
                2,
                arcLengthDerivatives=True)[0:2]
            rtx.append(tx[1])
            tx, td1 = sampleCubicHermiteCurves(
                [self.px[n3][n2c][m1a], self.px[n3][n2b][m1c]],
                [[(-self.pd1[n3][n2c][m1a][c] - self.pd3[n3][n2c][m1a][c])
                  for c in range(3)], [-d for d in self.pd3[n3][n2b][m1c]]],
                2,
                arcLengthDerivatives=True)[0:2]
            rtx.append(tx[1])
        elif self._type == ShieldRimDerivativeMode.SHIELD_RIM_DERIVATIVE_MODE_REGULAR:
            tx, td1 = sampleCubicHermiteCurves(
                [self.px[n3][n2a][m1c], self.px[n3][n2c][m1b]],
                [[(self.pd1[n3][n2a][m1c][c] + self.pd2[n3][n2a][m1c][c])
                  for c in range(3)], self.pd2[n3][n2c][m1b]],
                2,
                arcLengthDerivatives=True)[0:2]
            rtx.append(tx[1])
            tx, td1 = sampleCubicHermiteCurves(
                [self.px[n3][n2a][m1b], self.px[n3][n2c][m1c]], [
                    self.pd2[n3][n2a][m1b],
                    [(-self.pd1[n3][n2c][m1c][c] + self.pd2[n3][n2c][m1c][c])
                     for c in range(3)]
                ],
                2,
                arcLengthDerivatives=True)[0:2]
            rtx.append(tx[1])
            tx, td1 = sampleCubicHermiteCurves(
                [self.px[n3][n2c][m1a], self.px[n3][n2b][m1c]],
                [[(-self.pd1[n3][n2c][m1a][c] - self.pd2[n3][n2c][m1a][c])
                  for c in range(3)], [-d for d in self.pd1[n3][n2b][m1c]]],
                2,
                arcLengthDerivatives=True)[0:2]
            rtx.append(tx[1])
        #x = [ (rtx[0][c] + rtx[1][c] + rtx[2][c])/3.0 for c in range(3) ]
        x = [(rtx[0][c] + rtx[2][c]) / 2.0 for c in range(3)]
        if self.trackSurface:
            p = self.trackSurface.findNearestPosition(
                x,
                startPosition=self.trackSurface.createPositionProportion(
                    *(self.pProportions[n2b][m1c])))
            self.pProportions[n2b][m1b] = self.trackSurface.getProportion(p)
            x, sd1, sd2 = self.trackSurface.evaluateCoordinates(
                p, derivatives=True)
            d1, d2, d3 = calculate_surface_axes(sd1, sd2,
                                                vector.normalise(sd1))
            self.pd3[n3][n2b][m1b] = d3
        self.px[n3][n2b][m1b] = x
        if self._type == ShieldRimDerivativeMode.SHIELD_RIM_DERIVATIVE_MODE_AROUND:
            self.pd3[n3][n2b][m1b] = [
                (self.px[n3][n2b][m1b][c] - self.px[n3][n2b][m1c][c])
                for c in range(3)
            ]
            self.pd1[n3][n2b][m1b] = [
                (self.px[n3][n2c][m1b][c] - self.px[n3][n2b][m1b][c])
                for c in range(3)
            ]
        elif self._type == ShieldRimDerivativeMode.SHIELD_RIM_DERIVATIVE_MODE_REGULAR:
            self.pd1[n3][n2b][m1b] = [
                (self.px[n3][n2b][m1b][c] - self.px[n3][n2b][m1c][c])
                for c in range(3)
            ]
            self.pd2[n3][n2b][m1b] = [
                (self.px[n3][n2c][m1b][c] - self.px[n3][n2b][m1b][c])
                for c in range(3)
            ]
        if not self.trackSurface:
            if self._type == ShieldRimDerivativeMode.SHIELD_RIM_DERIVATIVE_MODE_AROUND:
                self.pd2[n3][n2b][m1b] = vector.normalise(
                    vector.crossproduct3(self.pd3[n3][n2b][m1b],
                                         self.pd1[n3][n2b][m1b]))
            elif self._type == ShieldRimDerivativeMode.SHIELD_RIM_DERIVATIVE_MODE_REGULAR:
                self.pd3[n3][n2b][m1b] = vector.normalise(
                    vector.crossproduct3(self.pd1[n3][n2b][m1b],
                                         self.pd2[n3][n2b][m1b]))