def __init__(self, region, centralPath, elementsCount): """ :param region: Zinc region to define model in. :param centralPath: Central path subscaffold comes from meshtype_1d_path1 and used to calculate ellipse radii. :param elementsCount: Number of elements needs to be sampled along the central path. """ tmpRegion = region.createRegion() centralPath.generate(tmpRegion) cx, cd1, cd2, cd3, cd12, cd13 = extractPathParametersFromRegion( tmpRegion, [ Node.VALUE_LABEL_VALUE, Node.VALUE_LABEL_D_DS1, Node.VALUE_LABEL_D_DS2, Node.VALUE_LABEL_D_DS3, Node.VALUE_LABEL_D2_DS1DS2, Node.VALUE_LABEL_D2_DS1DS3 ]) del tmpRegion # for i in range(len(cx)): # print(i, '[', cx[i], ',', cd1[i], ',', cd2[i], ',', cd12[i], ',', cd3[i], ',', cd13[i], '],') sx, sd1, se, sxi, ssf = sampleCubicHermiteCurves( cx, cd1, elementsCount) sd2, sd12 = interpolateSampleCubicHermite(cd2, cd12, se, sxi, ssf) sd3, sd13 = interpolateSampleCubicHermite(cd3, cd13, se, sxi, ssf) self.centres = sx self.majorRadii = [vector.magnitude(a) for a in sd2] self.majorAxis = sd2 self.minorRadii = [vector.magnitude(a) for a in sd3] self.minorAxis = sd3 self.alongAxis = sd1
def setRimNodes(self): """ Set nodes around the shell outer layer and core boundary in order needed for creating a shield mesh. """ btx = self.px btd1 = self.pd1 btd2 = self.pd2 btd3 = self.pd3 # elementsCountShell = self.elementsCountAcrossShell for n in range(self.elementsCountAround + 1): if self.elementsCountAcrossShell > 1: n1, n2 = self.__shield.convertRimIndex(n) n1c, n2c = self.__shield.convertRimIndex( n, self.elementsCountAcrossShell) tx, td3, pe, pxi, psf = sampleCubicHermiteCurves( [btx[n2c][n1c], btx[n2][n1]], [btd3[n2c][n1c], btd3[n2][n1]], elementsCountShell, arcLengthDerivatives=True) for rx in range(1, self.elementsCountAcrossShell): n1rx, n2rx = self.__shield.convertRimIndex(n, rx) btx[n2rx][n1rx] = tx[-rx + elementsCountShell] btd1[n2rx][n1rx] = btd1[n2][n1] btd2[n2rx][n1rx] = btd2[n2][n1] btd3[n2rx][n1rx] = btd3[n2][n1] for rx in range(1, self.elementsCountAcrossShell): self.__shield.smoothDerivativesAroundRim(0, n3d=None, rx=rx)
def getCenterLine(options): if 'Tube type' in options: if options['Tube type'] == 1: # Straight tube cx = [[-4.0, 1.0, 3.0], [1.0, 2.0, 0.0]] cd1 = [[5.0, 1.0, -3.0], [5.0, 1.0, -3.0]] elif options['Tube type'] == 2: # Human colon in x-y plane cx = [[0.0, 0.0, 0.0], [0.0, 10.0, 0.0], [5.0, 9.0, 0.0], [10.0, 10.0, 0.0], [10.0, -2.0, 0.0], [7.0, -4.0, 0.0]] cd1 = [[0.0, 10.0, 0.0], [5.0, 5.0, 0.0], [5.0, 0.0, 0.0], [5.0, -5.0, 0.0], [-3.0, -5.0, 0.0], [-3.0, 0.0, 0.0]] elif options['Tube type'] == 3: # Human colon in 3D cx = [[0.0, 0.0, 0.0], [0.0, 10.0, 3.0], [5.0, 9.0, 0.0], [10.0, 10.0, 2.0], [15.0, 15.0, 7.0], [20.0, -2.0, 0.0], [10.0, -4.0, -0.0]] cd1 = [[0.0, 10.0, 3.0], [5.0, 5.0, 0.0], [5.0, 0.0, 0.0], [10.0, -5.0, 0.0], [12.0, 12.0, 0.0], [5.0, -12.0, -5.0], [-8.0, 0.0, 0.0]] else: return None else: return None points = sampleCubicHermiteCurves(cx, cd1, 300)[0] if isinstance(points, list): pathDict = {} pathDict["CameraPath"] = [] pathDict["NumberOfPoints"] = len(points) for i in range(0, len(points)): pathDict["CameraPath"].extend(points[i]) return pathDict return None
def createRegularRowCurves(self, rscx, rscd1, rscd3): """ generate curves along regular rows using the mirror curve obtained from createMirrorCurve. :param rscx: Coordinates of the nodes on the middle curve. :param rscd1: d1 derivatives of the nodes on the middle curve. :param rscd3: d3 derivatives of the nodes on the middle curve. """ btx = self.px btd1 = self.pd1 btd2 = self.pd2 btd3 = self.pd3 elementsCountRim = 0 for n2 in range(elementsCountRim + 2, self.elementsCountUp + 1): btx[n2], btd3[n2], pe, pxi, psf = sampleCubicHermiteCurves( [btx[n2][0], rscx[n2], btx[n2][-1]], [ vector.setMagnitude(btd3[n2][0], -1.0), rscd3[n2], btd3[n2][-1] ], self.elementsCountAcrossMinor, lengthFractionStart=1, lengthFractionEnd=1, arcLengthDerivatives=True) btd1[n2] = interpolateSampleCubicHermite( [[-btd1[n2][0][c] for c in range(3)], rscd1[n2], btd1[n2][-1]], [[0.0, 0.0, 0.0]] * 3, pe, pxi, psf)[0] if n2 == self.elementsCountUp: for n1 in range(1, self.elementsCountAcrossMinor): btd1[n2][n1] = vector.setMagnitude( btd1[self.elementsCountUp][-1], 1.0) btd3[n2][0] = [-btd3[n2][0][c] for c in range(3)] btd1[n2][0] = [-btd1[n2][0][c] for c in range(3)]
def createRegularColumnCurves(self): """ up regular columns of shield: get d1, initial d3 below regular rows """ btx = self.px btd1 = self.pd1 btd2 = self.pd2 btd3 = self.pd3 elementsCountRim = self.elementsCountAcrossRim n1d = elementsCountRim + 2 n1x = self.elementsCountAcrossMinor - elementsCountRim - 1 n2a = self.elementsCountAcrossShell n2b = elementsCountRim n2d = 2 + n2b n2m = self.elementsCountUp for n1 in range(n1d, n1x): txm, td1m, pe, pxi, psf = sampleCubicHermiteCurves( [btx[n2a][n1], btx[n2d][n1]], [[-btd3[n2a][n1][c] for c in range(3)], btd1[n2d][n1]], 2 + self.elementsCountAcrossTransition - 1, arcLengthDerivatives=True) td3m = interpolateSampleCubicHermite( [btd1[n2a][n1], btd3[n2d][n1]], [[0.0, 0.0, 0.0]] * 2, pe, pxi, psf)[0] tx = [] td1 = [] for n2 in range(n2m + 1): if n2 <= n2a: tx.append(btx[n2][n1]) td1.append([-btd3[n2][n1][c] for c in range(3)]) elif (n2 > n2a) and (n2 < n2d): tx.append(txm[n2 - n2a]) td1.append(td1m[n2 - n2a]) else: tx.append(btx[n2][n1]) td1.append(btd1[n2][n1]) td1 = smoothCubicHermiteDerivativesLine(tx, td1, fixStartDirection=True, fixEndDirection=True) for n2 in range(n2m + 1): if n2 <= n2a: btd3[n2][n1] = [-td1[n2][c] for c in range(3)] elif (n2 > n2a) and (n2 < n2d): btx[n2][n1] = tx[n2] if n2 <= n2b: btd1[n2][n1] = td3m[n2 - n2a] btd3[n2][n1] = [-d for d in td1[n2]] else: btd1[n2][n1] = td1[n2] btd3[n2][n1] = td3m[n2 - n2a] else: btd1[n2][n1] = td1[n2]
def smoothTriplePointsCurves(self): """ Smooth row and column curves passing triple points (i.e., row 1 and columns 1 and -2). """ btx = self.px btd1 = self.pd1 btd2 = self.pd2 btd3 = self.pd3 n1c = 1 + self.elementsCountAcrossRim n1y = self.elementsCountAcrossMinor - self.elementsCountAcrossRim n1x = n1y - 1 n2a = self.elementsCountAcrossShell n2c = 1 + self.elementsCountAcrossRim n2m = self.elementsCountUp # smooth shield row n2c btd3[n2c][n1c:n1y] = smoothCubicHermiteDerivativesLine( btx[n2c][n1c:n1y], btd3[n2c][n1c:n1y]) # smooth Shield columns n1c, n1x for n1 in [n1c, n1x]: tx = [] td1 = [] for n2 in range(n2c, n2m + 1): tx.append(btx[n2][n1]) td1.append(btd1[n2][n1]) td1 = smoothCubicHermiteDerivativesLine(tx, td1, fixEndDirection=True, fixStartDerivative=True) for n in range(n2m - n2c + 1): btd1[n + n2c][n1] = td1[n] # sample nodes to triple points for n1 in [n1c, n1x]: c1 = 1 if n1 == n1c else -1 txm, td3m, pe, pxi, psf = sampleCubicHermiteCurves( [btx[n2a][n1], btx[n2c][n1]], [[-btd3[n2a][n1][c] for c in range(3)], [btd1[n2c][n1][c] + c1 * btd3[n2c][n1][c] for c in range(3)]], 1 + self.elementsCountAcrossTransition - 1, arcLengthDerivatives=True) td1m = interpolateSampleCubicHermite( [btd1[n2a][n1], [-c1 * d for d in btd1[n2c][n1]]], [[0.0, 0.0, 0.0]] * 2, pe, pxi, psf)[0] for n2 in range(n2a + 1, n2c): btx[n2][n1] = txm[n2 - n2a] btd1[n2][n1] = td1m[n2 - n2a] btd3[n2][n1] = [-d for d in td3m[n2 - n2a]] # smooth self.__shield.smoothDerivativesToTriplePoints(0, fixAllDirections=True)
def createMirrorCurve(self): """ generate coordinates and derivatives for the mirror curve :return: Coordinates and derivatives for the mirror curve """ btx = self.px btd1 = self.pd1 btd2 = self.pd2 btd3 = self.pd3 n2a = self.elementsCountAcrossShell rcx = [] tmdx = btx[n2a][self.elementsCountAcrossMinor // 2] tmdd3 = btd3[n2a][self.elementsCountAcrossMinor // 2] tmux = [ 0.5 * (btx[self.elementsCountUp][0][c] + btx[self.elementsCountUp][self.elementsCountAcrossMinor][c]) for c in range(3) ] rcx.append(tmdx) rcx.append(tmux) rcd3 = [vector.setMagnitude(tmdd3, -1), vector.setMagnitude(tmdd3, -1)] rscx, rscd1 = sampleCubicHermiteCurves(rcx, rcd3, self.elementsCountUp - n2a, arcLengthDerivatives=True)[0:2] # get d2, d3 rscd2 = [] rscd3 = [] for n in range(len(rscx)): d3 = vector.normalise([ btx[self.elementsCountUp][self.elementsCountAcrossMinor][c] - btx[self.elementsCountUp][0][c] for c in range(3) ]) d2 = vector.normalise(vector.crossproduct3(d3, rscd1[n])) rscd2.append(d2) rscd3.append(d3) return rscx, rscd1, rscd2, rscd3
def createRegularColumnCurves(self): """ up regular columns of shield: get d1, initial d3 below regular rows """ btx = self.px btd1 = self.pd1 btd2 = self.pd2 btd3 = self.pd3 for n1 in range(2, self.elementsCountAcrossMinor - 1): tx, td1, pe, pxi, psf = sampleCubicHermiteCurves( [btx[0][n1], btx[2][n1]], [[-btd3[0][n1][c] for c in range(3)], btd1[2][n1]], 2, lengthFractionStart=1, arcLengthDerivatives=True) for n2 in range(3, self.elementsCountUp + 1): tx.append(btx[n2][n1]) td1.append(btd1[n2][n1]) td1 = smoothCubicHermiteDerivativesLine(tx, td1, fixStartDirection=True, fixEndDirection=True) td3 = \ interpolateSampleCubicHermite([btd1[0][n1], btd3[2][n1]], [[0.0, 0.0, 0.0]] * 2, pe, pxi, psf)[ 0] for n2 in range(self.elementsCountUp + 1): if n2 < 2: btx[n2][n1] = tx[n2] if n2 == 0: btd3[n2][n1] = [-td1[0][c] for c in range(3)] else: btd3[n2][n1] = td3[n2] if n2 == 0: btd1[n2][n1] = td3[n2] else: btd1[n2][n1] = td1[n2]
def createRegularRowCurves(self, rscx, rscd1, rscd3): """ generate curves along regular rows using the mirror curve obtained from createMirrorCurve. :param rscx: Coordinates of the nodes on the middle curve. :param rscd1: d1 derivatives of the nodes on the middle curve. :param rscd3: d3 derivatives of the nodes on the middle curve. """ btx = self.px btd1 = self.pd1 btd2 = self.pd2 btd3 = self.pd3 elementsCountRim = self.elementsCountAcrossRim n2a = self.elementsCountAcrossShell n2b = elementsCountRim n2d = n2b + 2 n2m = self.elementsCountUp n1a = self.elementsCountAcrossShell n1b = elementsCountRim n1z = self.elementsCountAcrossMinor - self.elementsCountAcrossShell for n2 in range(n2d, n2m + 1): txm, td3m, pe, pxi, psf = sampleCubicHermiteCurves( [btx[n2][n1a], rscx[n2 - n2a], btx[n2][n1z]], [ vector.setMagnitude(btd3[n2][n1a], -1.0), rscd3[n2 - n2a], btd3[n2][n1z] ], self.elementsCountAcrossMinor - 2 * self.elementsCountAcrossShell, arcLengthDerivatives=True) td1m = interpolateSampleCubicHermite( [[-btd1[n2][n1a][c] for c in range(3)], rscd1[n2 - n2a], btd1[n2][n1z]], [[0.0, 0.0, 0.0]] * 3, pe, pxi, psf)[0] tx = [] td3 = [] for n1 in range(self.elementsCountAcrossMinor + 1): if n1 <= n1a: tx.append(btx[n2][n1]) td3.append([-btd3[n2][n1][c] for c in range(3)]) elif (n1 > n1a) and (n1 < n1z): tx.append(txm[n1 - n1a]) td3.append(td3m[n1 - n1a]) else: tx.append(btx[n2][n1]) td3.append((btd3[n2][n1])) td3 = smoothCubicHermiteDerivativesLine(tx, td3, fixStartDirection=True, fixEndDirection=True) for n1 in range(self.elementsCountAcrossMinor + 1): if n1 <= n1a: btd3[n2][n1] = [-td3[n1][c] for c in range(3)] elif (n1 > n1a) and (n1 < n1z): btx[n2][n1] = tx[n1] if n2 == n2m: if n1 <= n1b: btd1[n2][n1] = vector.setMagnitude( btd1[n2m][-1], -1.0) else: btd1[n2][n1] = vector.setMagnitude( btd1[n2m][-1], 1.0) else: if n1 <= n1b: btd1[n2][n1] = [-d for d in td1m[n1 - n1a]] else: btd1[n2][n1] = td1m[n1 - n1a] if n1 <= n1b: btd3[n2][n1] = [-d for d in td3[n1]] else: btd3[n2][n1] = td3[n1] else: btd3[n2][n1] = td3[n1]
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)
def createHermiteCurvePoints(self, aProportion1, aProportion2, bProportion1, bProportion2, elementsCount, derivativeStart=None, derivativeEnd=None, curveMode=HermiteCurveMode.SMOOTH): ''' Create hermite curve points between two points a and b on the surface, each defined by their proportions over the surface in directions 1 and 2. Also returns cross direction 2 in plane of surface with similar magnitude to curve derivative 1, and unit surface normals. :param derivativeStart, derivativeEnd: Optional derivative vectors in 3-D world coordinates to match at the start and end of the curves. If omitted, fits in with other derivative or is in a straight line from a to b. :param elementsCount: Number of elements out. :return: nx[], nd1[], nd2[], nd3[], nProportions[] ''' #print('createHermiteCurvePoints', aProportion1, aProportion2, bProportion1, bProportion2, elementsCount, derivativeStart, derivativeEnd) if derivativeStart: position = self.createPositionProportion(aProportion1, aProportion2) _, sd1, sd2 = self.evaluateCoordinates(position, derivatives=True) delta_xi1, delta_xi2 = calculate_surface_delta_xi( sd1, sd2, derivativeStart) dp1Start = delta_xi1 / self.elementsCount1 if self.loop1: dp1Start *= 2.0 dp2Start = delta_xi2 / self.elementsCount2 derivativeMagnitudeStart = math.sqrt(dp1Start * dp1Start + dp2Start * dp2Start) dp1Start *= elementsCount dp2Start *= elementsCount #print('start delta_xi1', delta_xi1, 'delta_xi2', delta_xi2) #print('dp1Start', dp1Start, 'dp2Start', dp2Start) if derivativeEnd: position = self.createPositionProportion(bProportion1, bProportion2) _, sd1, sd2 = self.evaluateCoordinates(position, derivatives=True) delta_xi1, delta_xi2 = calculate_surface_delta_xi( sd1, sd2, derivativeEnd) dp1End = delta_xi1 / self.elementsCount1 dp2End = delta_xi2 / self.elementsCount2 derivativeMagnitudeEnd = math.sqrt(dp1End * dp1End + dp2End * dp2End) dp1End *= elementsCount if self.loop1: dp1End *= 2.0 dp2End *= elementsCount #print('end delta_xi1', delta_xi1, 'delta_xi2', delta_xi2) #print('dp1End', dp1End, 'dp2End', dp2End) if not derivativeStart: if derivativeEnd: dp1Start, dp2Start = interp.interpolateLagrangeHermiteDerivative( [aProportion1, aProportion2], [bProportion1, bProportion2], [dp1End, dp2End], 0.0) else: dp1Start = bProportion1 - aProportion1 dp2Start = bProportion2 - aProportion2 derivativeMagnitudeStart = math.sqrt( dp1Start * dp1Start + dp2Start * dp2Start) / elementsCount if not derivativeEnd: if derivativeStart: dp1End, dp2End = interp.interpolateHermiteLagrangeDerivative( [aProportion1, aProportion2], [dp1Start, dp2Start], [bProportion1, bProportion2], 1.0) else: dp1End = bProportion1 - aProportion1 dp2End = bProportion2 - aProportion2 derivativeMagnitudeEnd = math.sqrt(dp1End * dp1End + dp2End * dp2End) / elementsCount maxProportion1 = 2.0 if self.loop1 else 1.0 #print('derivativeMagnitudeStart', derivativeMagnitudeStart, 'derivativeMagnitudeEnd', derivativeMagnitudeEnd) proportions, dproportions = interp.sampleCubicHermiteCurvesSmooth([ [ aProportion1, aProportion2 ], [ bProportion1, bProportion2 ] ], \ [ [ dp1Start, dp2Start ], [ dp1End, dp2End ] ], elementsCount, derivativeMagnitudeStart, derivativeMagnitudeEnd)[0:2] if curveMode != self.HermiteCurveMode.SMOOTH: if derivativeStart and (curveMode in [ self.HermiteCurveMode.TRANSITION_START, self.HermiteCurveMode.TRANSITION_START_AND_END ]): addLengthStart = 0.5 * derivativeMagnitudeStart lengthFractionStart = 0.5 else: addLengthStart = 0.0 lengthFractionStart = 1.0 if derivativeEnd and (curveMode in [ self.HermiteCurveMode.TRANSITION_END, self.HermiteCurveMode.TRANSITION_START_AND_END ]): addLengthEnd = 0.5 * derivativeMagnitudeEnd lengthFractionEnd = 0.5 else: addLengthEnd = 0.0 lengthFractionEnd = 1.0 proportions, dproportions = interp.sampleCubicHermiteCurves( proportions, dproportions, elementsCount, addLengthStart, addLengthEnd, lengthFractionStart, lengthFractionEnd)[0:2] #print(' proportions', proportions) #print('dproportions', dproportions) nx = [] nd1 = [] nd2 = [] nd3 = [] for n in range(0, elementsCount + 1): position = self.createPositionProportion(proportions[n][0], proportions[n][1]) x, sd1, sd2 = self.evaluateCoordinates(position, derivatives=True) f1 = dproportions[n][0] * self.elementsCount1 / maxProportion1 f2 = dproportions[n][1] * self.elementsCount2 d1 = [(f1 * sd1[c] + f2 * sd2[c]) for c in range(3)] d3 = vector.crossproduct3(sd1, sd2) # handle zero magnitude of d3 mag = math.sqrt(sum(d3[c] * d3[c] for c in range(3))) if mag > 0.0: d3 = [(d3[c] / mag) for c in range(3)] d2 = vector.crossproduct3(d3, d1) nx.append(x) nd2.append(d2) nd1.append(d1) nd3.append(d3) #print('createHermiteCurvePoints end \n nx', nx,'\nnd1',nd1,'\nnd2',nd2,'\nnd3',nd3) return nx, nd1, nd2, nd3, proportions
def generateBaseMesh(cls, region, options): """ Generate the base tricubic Hermite mesh. See also generateMesh(). :param region: Zinc region to define model in. Must be empty. :param options: Dict containing options. See getDefaultOptions(). :return: annotationGroups """ centralPath = options['Central path'] segmentCount = options['Number of segments'] elementsCountAround = options['Number of elements around'] elementsCountAlongSegment = options['Number of elements along segment'] elementsCountThroughWall = options['Number of elements through wall'] duodenumLength = options['Duodenum length'] jejunumLength = options['Jejunum length'] duodenumInnerRadius = options['Duodenum inner radius'] duodenumJejunumInnerRadius = options['Duodenum-jejunum inner radius'] jejunumIleumInnerRadius = options['Jejunum-ileum inner radius'] ileumInnerRadius = options['Ileum inner radius'] wallThickness = options['Wall thickness'] useCrossDerivatives = options['Use cross derivatives'] useCubicHermiteThroughWall = not(options['Use linear through wall']) elementsCountAlong = int(elementsCountAlongSegment*segmentCount) startPhase = 0.0 firstNodeIdentifier = 1 firstElementIdentifier = 1 # Central path tmpRegion = region.createRegion() centralPath.generate(tmpRegion) cx, cd1, cd2, cd12 = extractPathParametersFromRegion(tmpRegion) # for i in range(len(cx)): # print(i, '[', cx[i], ',', cd1[i], ',', cd2[i],',', cd12[i], '],') del tmpRegion # find arclength of colon length = 0.0 elementsCountIn = len(cx) - 1 sd1 = interp.smoothCubicHermiteDerivativesLine(cx, cd1, fixAllDirections = True, magnitudeScalingMode = interp.DerivativeScalingMode.HARMONIC_MEAN) for e in range(elementsCountIn): arcLength = interp.getCubicHermiteArcLength(cx[e], sd1[e], cx[e + 1], sd1[e + 1]) # print(e+1, arcLength) length += arcLength segmentLength = length / segmentCount elementAlongLength = length / elementsCountAlong # print('Length = ', length) # Sample central path sx, sd1, se, sxi, ssf = interp.sampleCubicHermiteCurves(cx, cd1, elementsCountAlongSegment*segmentCount) sd2, sd12 = interp.interpolateSampleCubicHermite(cd2, cd12, se, sxi, ssf) # Generate variation of radius & tc width along length lengthList = [0.0, duodenumLength, duodenumLength + jejunumLength, length] innerRadiusList = [duodenumInnerRadius, duodenumJejunumInnerRadius, jejunumIleumInnerRadius, ileumInnerRadius] innerRadiusSegmentList, dInnerRadiusSegmentList = interp.sampleParameterAlongLine(lengthList, innerRadiusList, segmentCount) # Create annotation groups for small intestine sections elementsAlongDuodenum = round(duodenumLength / elementAlongLength) elementsAlongJejunum = round(jejunumLength / elementAlongLength) elementsAlongIleum = elementsCountAlong - elementsAlongDuodenum - elementsAlongJejunum elementsCountAlongGroups = [elementsAlongDuodenum, elementsAlongJejunum, elementsAlongIleum] smallintestineGroup = AnnotationGroup(region, get_smallintestine_term("small intestine")) duodenumGroup = AnnotationGroup(region, get_smallintestine_term("duodenum")) jejunumGroup = AnnotationGroup(region, get_smallintestine_term("jejunum")) ileumGroup = AnnotationGroup(region, get_smallintestine_term("ileum")) annotationGroupAlong = [[smallintestineGroup, duodenumGroup], [smallintestineGroup, jejunumGroup], [smallintestineGroup, ileumGroup]] annotationGroupsAlong = [] for i in range(len(elementsCountAlongGroups)): elementsCount = elementsCountAlongGroups[i] for n in range(elementsCount): annotationGroupsAlong.append(annotationGroupAlong[i]) annotationGroupsAround = [] for i in range(elementsCountAround): annotationGroupsAround.append([ ]) annotationGroupsThroughWall = [] for i in range(elementsCountThroughWall): annotationGroupsThroughWall.append([ ]) xExtrude = [] d1Extrude = [] d2Extrude = [] d3UnitExtrude = [] # Create object smallIntestineSegmentTubeMeshInnerPoints = CylindricalSegmentTubeMeshInnerPoints( elementsCountAround, elementsCountAlongSegment, segmentLength, wallThickness, innerRadiusSegmentList, dInnerRadiusSegmentList, startPhase) for nSegment in range(segmentCount): # Create inner points xInner, d1Inner, d2Inner, transitElementList, segmentAxis, radiusAlongSegmentList = \ smallIntestineSegmentTubeMeshInnerPoints.getCylindricalSegmentTubeMeshInnerPoints(nSegment) # Project reference point for warping onto central path start = nSegment*elementsCountAlongSegment end = (nSegment + 1)*elementsCountAlongSegment + 1 sxRefList, sd1RefList, sd2ProjectedListRef, zRefList = \ tubemesh.getPlaneProjectionOnCentralPath(xInner, elementsCountAround, elementsCountAlongSegment, segmentLength, sx[start:end], sd1[start:end], sd2[start:end], sd12[start:end]) # Warp segment points xWarpedList, d1WarpedList, d2WarpedList, d3WarpedUnitList = tubemesh.warpSegmentPoints( xInner, d1Inner, d2Inner, segmentAxis, sxRefList, sd1RefList, sd2ProjectedListRef, elementsCountAround, elementsCountAlongSegment, zRefList, radiusAlongSegmentList, closedProximalEnd=False) # Store points along length xExtrude = xExtrude + (xWarpedList if nSegment == 0 else xWarpedList[elementsCountAround:]) d1Extrude = d1Extrude + (d1WarpedList if nSegment == 0 else d1WarpedList[elementsCountAround:]) # Smooth d2 for nodes between segments and recalculate d3 if nSegment == 0: d2Extrude = d2Extrude + (d2WarpedList[:-elementsCountAround]) d3UnitExtrude = d3UnitExtrude + (d3WarpedUnitList[:-elementsCountAround]) else: xSecondFace = xWarpedList[elementsCountAround:elementsCountAround*2] d2SecondFace = d2WarpedList[elementsCountAround:elementsCountAround*2] for n1 in range(elementsCountAround): nx = [xLastTwoFaces[n1], xLastTwoFaces[n1 + elementsCountAround], xSecondFace[n1]] nd2 = [d2LastTwoFaces[n1], d2LastTwoFaces[n1 + elementsCountAround], d2SecondFace[n1]] d2 = interp.smoothCubicHermiteDerivativesLine(nx, nd2, fixStartDerivative = True, fixEndDerivative = True)[1] d2Extrude.append(d2) d3Unit = vector.normalise(vector.crossproduct3(vector.normalise(d1LastTwoFaces[n1 + elementsCountAround]), vector.normalise(d2))) d3UnitExtrude.append(d3Unit) d2Extrude = d2Extrude + \ (d2WarpedList[elementsCountAround:-elementsCountAround] if nSegment < segmentCount - 1 else d2WarpedList[elementsCountAround:]) d3UnitExtrude = d3UnitExtrude + \ (d3WarpedUnitList[elementsCountAround:-elementsCountAround] if nSegment < segmentCount - 1 else d3WarpedUnitList[elementsCountAround:]) xLastTwoFaces = xWarpedList[-elementsCountAround*2:] d1LastTwoFaces = d1WarpedList[-elementsCountAround*2:] d2LastTwoFaces = d2WarpedList[-elementsCountAround*2:] # Create coordinates and derivatives xList, d1List, d2List, d3List, curvatureList = tubemesh.getCoordinatesFromInner(xExtrude, d1Extrude, d2Extrude, d3UnitExtrude, [wallThickness]*(elementsCountAlong+1), elementsCountAround, elementsCountAlong, elementsCountThroughWall, transitElementList) flatWidthList, xiList = smallIntestineSegmentTubeMeshInnerPoints.getFlatWidthAndXiList() # Create flat and texture coordinates xFlat, d1Flat, d2Flat, xTexture, d1Texture, d2Texture = tubemesh.createFlatAndTextureCoordinates( xiList, flatWidthList, length, wallThickness, elementsCountAround, elementsCountAlong, elementsCountThroughWall, transitElementList) # Create nodes and elements nextNodeIdentifier, nextElementIdentifier, annotationGroups = tubemesh.createNodesAndElements( region, xList, d1List, d2List, d3List, xFlat, d1Flat, d2Flat, xTexture, d1Texture, d2Texture, elementsCountAround, elementsCountAlong, elementsCountThroughWall, annotationGroupsAround, annotationGroupsAlong, annotationGroupsThroughWall, firstNodeIdentifier, firstElementIdentifier, useCubicHermiteThroughWall, useCrossDerivatives, closedProximalEnd=False) return annotationGroups
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]))
def generateBaseMesh(region, options): """ Generate the base tricubic Hermite mesh. See also generateMesh(). :param region: Zinc region to define model in. Must be empty. :param options: Dict containing options. See getDefaultOptions(). :return: annotationGroups """ centralPath = options['Central path'] segmentProfile = options['Segment profile'] segmentCount = options['Number of segments'] startPhase = options['Start phase'] % 360.0 proximalLength = options['Proximal length'] transverseLength = options['Transverse length'] distalLength = options['Distal length'] proximalInnerRadius = options['Proximal inner radius'] proximalTCWidth = options['Proximal tenia coli width'] proximalTransverseInnerRadius = options[ 'Proximal-transverse inner radius'] proximalTransverseTCWidth = options[ 'Proximal-transverse tenia coli width'] transverseDistalInnerRadius = options['Transverse-distal inner radius'] transverseDistalTCWidth = options['Transverse-distal tenia coli width'] distalInnerRadius = options['Distal inner radius'] distalTCWidth = options['Distal tenia coli width'] segmentSettings = segmentProfile.getScaffoldSettings() elementsCountAroundTC = segmentSettings[ 'Number of elements around tenia coli'] elementsCountAroundHaustrum = segmentSettings[ 'Number of elements around haustrum'] cornerInnerRadiusFactor = segmentSettings['Corner inner radius factor'] haustrumInnerRadiusFactor = segmentSettings[ 'Haustrum inner radius factor'] segmentLengthEndDerivativeFactor = segmentSettings[ 'Segment length end derivative factor'] segmentLengthMidDerivativeFactor = segmentSettings[ 'Segment length mid derivative factor'] tcCount = segmentSettings['Number of tenia coli'] tcThickness = segmentSettings['Tenia coli thickness'] elementsCountAround = (elementsCountAroundTC + elementsCountAroundHaustrum) * tcCount elementsCountAlongSegment = segmentSettings[ 'Number of elements along segment'] elementsCountThroughWall = segmentSettings[ 'Number of elements through wall'] wallThickness = segmentSettings['Wall thickness'] useCrossDerivatives = segmentSettings['Use cross derivatives'] useCubicHermiteThroughWall = not ( segmentSettings['Use linear through wall']) elementsCountAlong = int(elementsCountAlongSegment * segmentCount) firstNodeIdentifier = 1 firstElementIdentifier = 1 # Central path tmpRegion = region.createRegion() centralPath.generate(tmpRegion) cx, cd1, cd2, cd12 = extractPathParametersFromRegion(tmpRegion) # for i in range(len(cx)): # print(i, '[', cx[i], ',', cd1[i], ',', cd2[i], ',', cd12[i], '],') del tmpRegion # find arclength of colon length = 0.0 elementsCountIn = len(cx) - 1 sd1 = interp.smoothCubicHermiteDerivativesLine( cx, cd1, fixAllDirections=True, magnitudeScalingMode=interp.DerivativeScalingMode.HARMONIC_MEAN) for e in range(elementsCountIn): arcLength = interp.getCubicHermiteArcLength( cx[e], sd1[e], cx[e + 1], sd1[e + 1]) # print(e+1, arcLength) length += arcLength segmentLength = length / segmentCount # print('Length = ', length) # Sample central path sx, sd1, se, sxi, ssf = interp.sampleCubicHermiteCurves( cx, cd1, elementsCountAlongSegment * segmentCount) sd2 = interp.interpolateSampleCubicHermite(cd2, cd12, se, sxi, ssf)[0] # Generate variation of radius & tc width along length lengthList = [ 0.0, proximalLength, proximalLength + transverseLength, length ] innerRadiusList = [ proximalInnerRadius, proximalTransverseInnerRadius, transverseDistalInnerRadius, distalInnerRadius ] innerRadiusAlongElementList, dInnerRadiusAlongElementList = interp.sampleParameterAlongLine( lengthList, innerRadiusList, elementsCountAlong) tcWidthList = [ proximalTCWidth, proximalTransverseTCWidth, transverseDistalTCWidth, distalTCWidth ] tcWidthAlongElementList, dTCWidthAlongElementList = interp.sampleParameterAlongLine( lengthList, tcWidthList, elementsCountAlong) xExtrude = [] d1Extrude = [] d2Extrude = [] d3UnitExtrude = [] # Create object colonSegmentTubeMeshInnerPoints = ColonSegmentTubeMeshInnerPoints( region, elementsCountAroundTC, elementsCountAroundHaustrum, elementsCountAlongSegment, tcCount, segmentLengthEndDerivativeFactor, segmentLengthMidDerivativeFactor, segmentLength, wallThickness, cornerInnerRadiusFactor, haustrumInnerRadiusFactor, innerRadiusAlongElementList, dInnerRadiusAlongElementList, tcWidthAlongElementList, startPhase) for nSegment in range(segmentCount): # Create inner points xInner, d1Inner, d2Inner, transitElementList, segmentAxis, annotationGroups, annotationArray, \ faceMidPointsZ = colonSegmentTubeMeshInnerPoints.getColonSegmentTubeMeshInnerPoints(nSegment) # Warp segment points xWarpedList, d1WarpedList, d2WarpedList, d3WarpedUnitList = tubemesh.warpSegmentPoints( xInner, d1Inner, d2Inner, segmentAxis, segmentLength, sx, sd1, sd2, elementsCountAround, elementsCountAlongSegment, nSegment, faceMidPointsZ) # Store points along length xExtrude = xExtrude + (xWarpedList if nSegment == 0 else xWarpedList[elementsCountAround:]) d1Extrude = d1Extrude + (d1WarpedList if nSegment == 0 else d1WarpedList[elementsCountAround:]) d2Extrude = d2Extrude + (d2WarpedList if nSegment == 0 else d2WarpedList[elementsCountAround:]) d3UnitExtrude = d3UnitExtrude + ( d3WarpedUnitList if nSegment == 0 else d3WarpedUnitList[elementsCountAround:]) contractedWallThicknessList = colonSegmentTubeMeshInnerPoints.getContractedWallThicknessList( ) # Create coordinates and derivatives xList, d1List, d2List, d3List, curvatureList = tubemesh.getCoordinatesFromInner( xExtrude, d1Extrude, d2Extrude, d3UnitExtrude, contractedWallThicknessList, elementsCountAround, elementsCountAlong, elementsCountThroughWall, transitElementList) relaxedLengthList, xiList = colonSegmentTubeMeshInnerPoints.getRelaxedLengthAndXiList( ) if tcThickness > 0: tubeTCWidthList = colonSegmentTubeMeshInnerPoints.getTubeTCWidthList( ) xList, d1List, d2List, d3List, annotationGroups, annotationArray = getTeniaColi( region, xList, d1List, d2List, d3List, curvatureList, tcCount, elementsCountAroundTC, elementsCountAroundHaustrum, elementsCountAlong, elementsCountThroughWall, tubeTCWidthList, tcThickness, sx, annotationGroups, annotationArray) # Create flat and texture coordinates xFlat, d1Flat, d2Flat, xTexture, d1Texture, d2Texture = createFlatAndTextureCoordinatesTeniaColi( xiList, relaxedLengthList, length, wallThickness, tcCount, tcThickness, elementsCountAroundTC, elementsCountAroundHaustrum, elementsCountAlong, elementsCountThroughWall, transitElementList) # Create nodes and elements nextNodeIdentifier, nextElementIdentifier, annotationGroups = createNodesAndElementsTeniaColi( region, xList, d1List, d2List, d3List, xFlat, d1Flat, d2Flat, xTexture, d1Texture, d2Texture, elementsCountAroundTC, elementsCountAroundHaustrum, elementsCountAlong, elementsCountThroughWall, tcCount, annotationGroups, annotationArray, firstNodeIdentifier, firstElementIdentifier, useCubicHermiteThroughWall, useCrossDerivatives) else: # Create flat and texture coordinates xFlat, d1Flat, d2Flat, xTexture, d1Texture, d2Texture = tubemesh.createFlatAndTextureCoordinates( xiList, relaxedLengthList, length, wallThickness, elementsCountAround, elementsCountAlong, elementsCountThroughWall, transitElementList) # Create nodes and elements nextNodeIdentifier, nextElementIdentifier, annotationGroups = tubemesh.createNodesAndElements( region, xList, d1List, d2List, d3List, xFlat, d1Flat, d2Flat, xTexture, d1Texture, d2Texture, elementsCountAround, elementsCountAlong, elementsCountThroughWall, annotationGroups, annotationArray, firstNodeIdentifier, firstElementIdentifier, useCubicHermiteThroughWall, useCrossDerivatives) return annotationGroups
def generateBaseMesh(cls, region, options): """ Generate the base tricubic Hermite mesh. See also generateMesh(). :param region: Zinc region to define model in. Must be empty. :param options: Dict containing options. See getDefaultOptions(). :return: annotationGroups """ centralPath = options['Central path'] elementsCountAround = options['Number of elements around'] elementsCountAlong = options['Number of elements along'] elementsCountThroughWall = options['Number of elements through wall'] wallThickness = options['Wall thickness'] mucosaRelThickness = options['Mucosa relative thickness'] submucosaRelThickness = options['Submucosa relative thickness'] circularRelThickness = options[ 'Circular muscle layer relative thickness'] longitudinalRelThickness = options[ 'Longitudinal muscle layer relative thickness'] useCrossDerivatives = options['Use cross derivatives'] useCubicHermiteThroughWall = not (options['Use linear through wall']) firstNodeIdentifier = 1 firstElementIdentifier = 1 # Central path esophagusTermsAlong = [ None, 'cervical part of esophagus', 'thoracic part of esophagus', 'abdominal part of esophagus' ] arcLengthOfGroupsAlong = [] for i in range(len(esophagusTermsAlong)): tmpRegion = region.createRegion() centralPath.generate(tmpRegion) cxGroup, cd1Group, cd2Group, cd3Group, cd12Group, cd13Group = \ extractPathParametersFromRegion(tmpRegion, [Node.VALUE_LABEL_VALUE, Node.VALUE_LABEL_D_DS1, Node.VALUE_LABEL_D_DS2, Node.VALUE_LABEL_D_DS3, Node.VALUE_LABEL_D2_DS1DS2, Node.VALUE_LABEL_D2_DS1DS3], groupName=esophagusTermsAlong[i]) arcLength = 0.0 for e in range(len(cxGroup) - 1): arcLength += interp.getCubicHermiteArcLength( cxGroup[e], cd1Group[e], cxGroup[e + 1], cd1Group[e + 1]) arcLengthOfGroupsAlong.append(arcLength) if i == 0: cx = cxGroup cd1 = cd1Group cd2 = cd2Group cd3 = cd3Group cd12 = cd12Group cd13 = cd13Group del tmpRegion # Sample central path sx, sd1, se, sxi, ssf = interp.sampleCubicHermiteCurves( cx, cd1, elementsCountAlong) sd2, sd12 = interp.interpolateSampleCubicHermite( cd2, cd12, se, sxi, ssf) sd3, sd13 = interp.interpolateSampleCubicHermite( cd3, cd13, se, sxi, ssf) centralPathLength = arcLengthOfGroupsAlong[0] elementAlongLength = centralPathLength / elementsCountAlong elementsCountAlongGroups = [] groupLength = 0.0 e = 0 elementsCount = 1 length = elementAlongLength for i in range(1, len(esophagusTermsAlong)): groupLength += arcLengthOfGroupsAlong[i] if e == elementsCountAlong - 2: elementsCount += 1 elementsCountAlongGroups.append(elementsCount) else: while length < groupLength: elementsCount += 1 e += 1 length += elementAlongLength # check which end is grouplength closer to distToUpperEnd = abs(length - groupLength) distToLowerEnd = abs(groupLength - (length - elementsCountAlong)) if distToLowerEnd < distToUpperEnd: elementsCount -= 1 elementsCountAlongGroups.append(elementsCount) e -= 1 length -= elementAlongLength else: elementsCountAlongGroups.append(elementsCount) elementsCount = 0 majorRadiusElementList = sd2 minorRadiusElementList = sd3 # Create annotation groups along esophagus esophagusGroup = AnnotationGroup(region, get_esophagus_term("esophagus")) cervicalGroup = AnnotationGroup( region, get_esophagus_term("cervical part of esophagus")) thoracicGroup = AnnotationGroup( region, get_esophagus_term("thoracic part of esophagus")) abdominalGroup = AnnotationGroup( region, get_esophagus_term("abdominal part of esophagus")) annotationGroupAlong = [[esophagusGroup, cervicalGroup], [esophagusGroup, thoracicGroup], [esophagusGroup, abdominalGroup]] annotationGroupsAlong = [] for i in range(len(elementsCountAlongGroups)): elementsCount = elementsCountAlongGroups[i] for n in range(elementsCount): annotationGroupsAlong.append(annotationGroupAlong[i]) annotationGroupsAround = [] for i in range(elementsCountAround): annotationGroupsAround.append([]) # Groups through wall longitudinalMuscleGroup = AnnotationGroup( region, get_esophagus_term("esophagus smooth muscle longitudinal layer")) circularMuscleGroup = AnnotationGroup( region, get_esophagus_term("esophagus smooth muscle circular layer")) submucosaGroup = AnnotationGroup( region, get_esophagus_term("submucosa of esophagus")) mucosaGroup = AnnotationGroup(region, get_esophagus_term("esophagus mucosa")) if elementsCountThroughWall == 1: relativeThicknessList = [1.0] annotationGroupsThroughWall = [[]] else: relativeThicknessList = [ mucosaRelThickness, submucosaRelThickness, circularRelThickness, longitudinalRelThickness ] annotationGroupsThroughWall = [[mucosaGroup], [submucosaGroup], [circularMuscleGroup], [longitudinalMuscleGroup]] xToSample = [] d1ToSample = [] for n2 in range(elementsCountAlong + 1): # Create inner points cx = [0.0, 0.0, elementAlongLength * n2] axis1 = [vector.magnitude(majorRadiusElementList[n2]), 0.0, 0.0] axis2 = [0.0, vector.magnitude(minorRadiusElementList[n2]), 0.0] xInner, d1Inner = geometry.createEllipsePoints(cx, 2 * math.pi, axis1, axis2, elementsCountAround, startRadians=0.0) xToSample += xInner d1ToSample += d1Inner d2ToSample = [[0.0, 0.0, elementAlongLength] ] * (elementsCountAround * (elementsCountAlong + 1)) # Sample along length xInnerRaw = [] d2InnerRaw = [] xToWarp = [] d1ToWarp = [] d2ToWarp = [] flatWidthList = [] xiList = [] for n1 in range(elementsCountAround): xForSamplingAlong = [] d2ForSamplingAlong = [] for n2 in range(elementsCountAlong + 1): idx = n2 * elementsCountAround + n1 xForSamplingAlong.append(xToSample[idx]) d2ForSamplingAlong.append(d2ToSample[idx]) xSampled, d2Sampled = interp.sampleCubicHermiteCurves( xForSamplingAlong, d2ForSamplingAlong, elementsCountAlong, arcLengthDerivatives=True)[0:2] xInnerRaw.append(xSampled) d2InnerRaw.append(d2Sampled) # Re-arrange sample order & calculate dx_ds1 and dx_ds3 from dx_ds2 for n2 in range(elementsCountAlong + 1): xAround = [] d2Around = [] for n1 in range(elementsCountAround): x = xInnerRaw[n1][n2] d2 = d2InnerRaw[n1][n2] xAround.append(x) d2Around.append(d2) d1Around = [] for n1 in range(elementsCountAround): v1 = xAround[n1] v2 = xAround[(n1 + 1) % elementsCountAround] d1 = d2 = [v2[c] - v1[c] for c in range(3)] arcLengthAround = interp.computeCubicHermiteArcLength( v1, d1, v2, d2, True) dx_ds1 = [c * arcLengthAround for c in vector.normalise(d1)] d1Around.append(dx_ds1) d1Smoothed = interp.smoothCubicHermiteDerivativesLoop( xAround, d1Around) xToWarp += xAround d1ToWarp += d1Smoothed d2ToWarp += d2Around # Flat width and xi flatWidth = 0.0 xiFace = [] for n1 in range(elementsCountAround): v1 = xAround[n1] d1 = d1Smoothed[n1] v2 = xAround[(n1 + 1) % elementsCountAround] d2 = d1Smoothed[(n1 + 1) % elementsCountAround] flatWidth += interp.getCubicHermiteArcLength(v1, d1, v2, d2) flatWidthList.append(flatWidth) for n1 in range(elementsCountAround + 1): xi = 1.0 / elementsCountAround * n1 xiFace.append(xi) xiList.append(xiFace) # Project reference point for warping onto central path sxRefList, sd1RefList, sd2ProjectedListRef, zRefList = \ tubemesh.getPlaneProjectionOnCentralPath(xToWarp, elementsCountAround, elementsCountAlong, centralPathLength, sx, sd1, sd2, sd12) # Warp points segmentAxis = [0.0, 0.0, 1.0] closedProximalEnd = False innerRadiusAlong = [] for n2 in range(elementsCountAlong + 1): firstNodeAlong = xToWarp[n2 * elementsCountAround] midptSegmentAxis = [0.0, 0.0, elementAlongLength * n2] radius = vector.magnitude(firstNodeAlong[c] - midptSegmentAxis[c] for c in range(3)) innerRadiusAlong.append(radius) xWarpedList, d1WarpedList, d2WarpedList, d3WarpedUnitList = \ tubemesh.warpSegmentPoints(xToWarp, d1ToWarp, d2ToWarp, segmentAxis, sxRefList, sd1RefList, sd2ProjectedListRef, elementsCountAround, elementsCountAlong, zRefList, innerRadiusAlong, closedProximalEnd) # Create coordinates and derivatives transitElementList = [0] * elementsCountAround xList, d1List, d2List, d3List, curvatureList = \ tubemesh.getCoordinatesFromInner(xWarpedList, d1WarpedList, d2WarpedList, d3WarpedUnitList, [wallThickness]*(elementsCountAlong+1), relativeThicknessList, elementsCountAround, elementsCountAlong, elementsCountThroughWall, transitElementList) # Create flat coordinates xFlat, d1Flat, d2Flat = tubemesh.createFlatCoordinates( xiList, flatWidthList, length, wallThickness, relativeThicknessList, elementsCountAround, elementsCountAlong, elementsCountThroughWall, transitElementList) # Create nodes and elements xOrgan = [] d1Organ = [] d2Organ = [] nodeIdentifier, elementIdentifier, annotationGroups = \ tubemesh.createNodesAndElements(region, xList, d1List, d2List, d3List, xFlat, d1Flat, d2Flat, xOrgan, d1Organ, d2Organ, None, elementsCountAround, elementsCountAlong, elementsCountThroughWall, annotationGroupsAround, annotationGroupsAlong, annotationGroupsThroughWall, firstNodeIdentifier, firstElementIdentifier, useCubicHermiteThroughWall, useCrossDerivatives, closedProximalEnd) # annotation fiducial points fm = region.getFieldmodule() fm.beginChange() mesh = fm.findMeshByDimension(3) cache = fm.createFieldcache() markerGroup = findOrCreateFieldGroup(fm, "marker") markerName = findOrCreateFieldStoredString(fm, name="marker_name") markerLocation = findOrCreateFieldStoredMeshLocation( fm, mesh, name="marker_location") nodes = fm.findNodesetByFieldDomainType(Field.DOMAIN_TYPE_NODES) markerPoints = findOrCreateFieldNodeGroup(markerGroup, nodes).getNodesetGroup() markerTemplateInternal = nodes.createNodetemplate() markerTemplateInternal.defineField(markerName) markerTemplateInternal.defineField(markerLocation) markerNames = [ "proximodorsal midpoint on serosa of upper esophageal sphincter", "proximoventral midpoint on serosa of upper esophageal sphincter", "distal point of lower esophageal sphincter serosa on the greater curvature of stomach", "distal point of lower esophageal sphincter serosa on the lesser curvature of stomach" ] totalElements = elementIdentifier radPerElementAround = math.pi * 2.0 / elementsCountAround elementAroundHalfPi = int(0.25 * elementsCountAround) xi1HalfPi = (math.pi * 0.5 - radPerElementAround * elementAroundHalfPi) / radPerElementAround elementAroundPi = int(0.5 * elementsCountAround) xi1Pi = (math.pi - radPerElementAround * elementAroundPi) / radPerElementAround markerElementIdentifiers = [ elementsCountAround * elementsCountThroughWall - elementAroundHalfPi, elementAroundHalfPi + 1 + elementsCountAround * (elementsCountThroughWall - 1), totalElements - elementsCountAround, totalElements - elementsCountAround + elementAroundPi ] markerXis = [[1.0 - xi1HalfPi, 0.0, 1.0], [xi1HalfPi, 0.0, 1.0], [0.0, 1.0, 1.0], [xi1Pi, 1.0, 1.0]] for n in range(len(markerNames)): markerGroup = findOrCreateAnnotationGroupForTerm( annotationGroups, region, get_esophagus_term(markerNames[n])) markerElement = mesh.findElementByIdentifier( markerElementIdentifiers[n]) markerXi = markerXis[n] cache.setMeshLocation(markerElement, markerXi) markerPoint = markerPoints.createNode(nodeIdentifier, markerTemplateInternal) nodeIdentifier += 1 cache.setNode(markerPoint) markerName.assignString(cache, markerGroup.getName()) markerLocation.assignMeshLocation(cache, markerElement, markerXi) for group in [esophagusGroup, markerGroup]: group.getNodesetGroup(nodes).addNode(markerPoint) fm.endChange() return annotationGroups
def generateBaseMesh(cls, region, options): """ Generate the base tricubic Hermite mesh. See also generateMesh(). :param region: Zinc region to define model in. Must be empty. :param options: Dict containing options. See getDefaultOptions(). :return: annotationGroups """ centralPath = options['Central path'] segmentProfile = options['Segment profile'] segmentCount = options['Number of segments'] startPhase = options['Start phase'] % 360.0 proximalLength = options['Proximal length'] transverseLength = options['Transverse length'] proximalInnerRadius = options['Proximal inner radius'] proximalTCWidth = options['Proximal tenia coli width'] proximalTransverseInnerRadius = options['Proximal-transverse inner radius'] proximalTransverseTCWidth = options['Proximal-transverse tenia coli width'] transverseDistalInnerRadius = options['Transverse-distal inner radius'] transverseDistalTCWidth = options['Transverse-distal tenia coli width'] distalInnerRadius = options['Distal inner radius'] distalTCWidth = options['Distal tenia coli width'] segmentSettings = segmentProfile.getScaffoldSettings() elementsCountAroundTC = segmentSettings['Number of elements around tenia coli'] elementsCountAroundHaustrum = segmentSettings['Number of elements around haustrum'] cornerInnerRadiusFactor = segmentSettings['Corner inner radius factor'] haustrumInnerRadiusFactor = segmentSettings['Haustrum inner radius factor'] segmentLengthEndDerivativeFactor = segmentSettings['Segment length end derivative factor'] segmentLengthMidDerivativeFactor = segmentSettings['Segment length mid derivative factor'] tcCount = segmentSettings['Number of tenia coli'] tcThickness = segmentSettings['Tenia coli thickness'] elementsCountAround = (elementsCountAroundTC + elementsCountAroundHaustrum)*tcCount elementsCountAlongSegment = segmentSettings['Number of elements along segment'] elementsCountThroughWall = segmentSettings['Number of elements through wall'] wallThickness = segmentSettings['Wall thickness'] useCrossDerivatives = segmentSettings['Use cross derivatives'] useCubicHermiteThroughWall = not(segmentSettings['Use linear through wall']) elementsCountAlong = int(elementsCountAlongSegment*segmentCount) firstNodeIdentifier = 1 firstElementIdentifier = 1 # Central path tmpRegion = region.createRegion() centralPath.generate(tmpRegion) cx, cd1, cd2, cd12 = extractPathParametersFromRegion(tmpRegion) # for i in range(len(cx)): # print(i, '[', cx[i], ',', cd1[i], ',', cd2[i], ',', cd12[i], '],') del tmpRegion # find arclength of colon length = 0.0 elementsCountIn = len(cx) - 1 sd1 = interp.smoothCubicHermiteDerivativesLine(cx, cd1, fixAllDirections = True, magnitudeScalingMode = interp.DerivativeScalingMode.HARMONIC_MEAN) for e in range(elementsCountIn): arcLength = interp.getCubicHermiteArcLength(cx[e], sd1[e], cx[e + 1], sd1[e + 1]) # print(e+1, arcLength) length += arcLength segmentLength = length / segmentCount # print('Length = ', length) elementAlongLength = length / elementsCountAlong # Sample central path sx, sd1, se, sxi, ssf = interp.sampleCubicHermiteCurves(cx, cd1, elementsCountAlong) sd2, sd12 = interp.interpolateSampleCubicHermite(cd2, cd12, se, sxi, ssf) # Generate variation of radius & tc width along length lengthList = [0.0, proximalLength, proximalLength + transverseLength, length] innerRadiusList = [proximalInnerRadius, proximalTransverseInnerRadius, transverseDistalInnerRadius, distalInnerRadius] innerRadiusAlongElementList, dInnerRadiusAlongElementList = interp.sampleParameterAlongLine(lengthList, innerRadiusList, elementsCountAlong) tcWidthList = [proximalTCWidth, proximalTransverseTCWidth, transverseDistalTCWidth, distalTCWidth] tcWidthAlongElementList, dTCWidthAlongElementList = interp.sampleParameterAlongLine(lengthList, tcWidthList, elementsCountAlong) # Account for reduced haustrum appearance in transverse and distal pig colon if tcCount == 2: haustrumInnerRadiusFactorList = [haustrumInnerRadiusFactor, haustrumInnerRadiusFactor*0.75, haustrumInnerRadiusFactor*0.5, haustrumInnerRadiusFactor*0.2] haustrumInnerRadiusFactorAlongElementList = \ interp.sampleParameterAlongLine(lengthList, haustrumInnerRadiusFactorList, elementsCountAlong)[0] else: haustrumInnerRadiusFactorAlongElementList = [haustrumInnerRadiusFactor]*(elementsCountAlong+1) # Create annotation groups for colon sections elementsAlongInProximal = round(proximalLength/elementAlongLength) elementsAlongInTransverse = round(transverseLength/elementAlongLength) elementsAlongInDistal = elementsCountAlong - elementsAlongInProximal - elementsAlongInTransverse elementsCountAlongGroups = [elementsAlongInProximal, elementsAlongInTransverse, elementsAlongInDistal] colonGroup = AnnotationGroup(region, get_colon_term("colon")) if tcCount == 1: proximalGroup = AnnotationGroup(region, get_colon_term("proximal colon")) transverseGroup = AnnotationGroup(region, get_colon_term("transverse colon")) distalGroup = AnnotationGroup(region, get_colon_term("distal colon")) annotationGroupAlong = [[colonGroup, proximalGroup], [colonGroup, transverseGroup], [colonGroup, distalGroup]] elif tcCount == 2: spiralGroup = AnnotationGroup(region, get_colon_term("spiral colon")) transverseGroup = AnnotationGroup(region, get_colon_term("transverse colon")) distalGroup = AnnotationGroup(region, get_colon_term("distal colon")) annotationGroupAlong = [[colonGroup, spiralGroup], [colonGroup, transverseGroup], [colonGroup, distalGroup]] elif tcCount == 3: ascendingGroup = AnnotationGroup(region, get_colon_term("ascending colon")) transverseGroup = AnnotationGroup(region, get_colon_term("transverse colon")) descendingGroup = AnnotationGroup(region, get_colon_term("descending colon")) annotationGroupAlong = [[colonGroup, ascendingGroup], [colonGroup, transverseGroup], [colonGroup, descendingGroup]] annotationGroupsAlong = [] for i in range(len(elementsCountAlongGroups)): elementsCount = elementsCountAlongGroups[i] for n in range(elementsCount): annotationGroupsAlong.append(annotationGroupAlong[i]) annotationGroupsThroughWall = [] for i in range(elementsCountThroughWall): annotationGroupsThroughWall.append([ ]) xExtrude = [] d1Extrude = [] d2Extrude = [] d3UnitExtrude = [] sxRefExtrudeList = [] # Create object colonSegmentTubeMeshInnerPoints = ColonSegmentTubeMeshInnerPoints( region, elementsCountAroundTC, elementsCountAroundHaustrum, elementsCountAlongSegment, tcCount, segmentLengthEndDerivativeFactor, segmentLengthMidDerivativeFactor, segmentLength, wallThickness, cornerInnerRadiusFactor, haustrumInnerRadiusFactorAlongElementList, innerRadiusAlongElementList, dInnerRadiusAlongElementList, tcWidthAlongElementList, startPhase) for nSegment in range(segmentCount): # Create inner points xInner, d1Inner, d2Inner, transitElementList, segmentAxis, annotationGroupsAround \ = colonSegmentTubeMeshInnerPoints.getColonSegmentTubeMeshInnerPoints(nSegment) # Project reference point for warping onto central path start = nSegment * elementsCountAlongSegment end = (nSegment + 1) * elementsCountAlongSegment + 1 sxRefList, sd1RefList, sd2ProjectedListRef, zRefList = \ tubemesh.getPlaneProjectionOnCentralPath(xInner, elementsCountAround, elementsCountAlongSegment, segmentLength, sx[start:end], sd1[start:end], sd2[start:end], sd12[start:end]) # Warp segment points xWarpedList, d1WarpedList, d2WarpedList, d3WarpedUnitList = tubemesh.warpSegmentPoints( xInner, d1Inner, d2Inner, segmentAxis, sxRefList, sd1RefList, sd2ProjectedListRef, elementsCountAround, elementsCountAlongSegment, zRefList, innerRadiusAlongElementList[start:end], closedProximalEnd=False) # Store points along length xExtrude += xWarpedList if nSegment == 0 else xWarpedList[elementsCountAround:] d1Extrude += d1WarpedList if nSegment == 0 else d1WarpedList[elementsCountAround:] d2Extrude += d2WarpedList if nSegment == 0 else d2WarpedList[elementsCountAround:] d3UnitExtrude += d3WarpedUnitList if nSegment == 0 else d3WarpedUnitList[elementsCountAround:] sxRefExtrudeList += sxRefList if nSegment == 0 else sxRefList[elementsCountAround:] contractedWallThicknessList = colonSegmentTubeMeshInnerPoints.getContractedWallThicknessList() # Create coordinates and derivatives xList, d1List, d2List, d3List, curvatureList = tubemesh.getCoordinatesFromInner(xExtrude, d1Extrude, d2Extrude, d3UnitExtrude, contractedWallThicknessList, elementsCountAround, elementsCountAlong, elementsCountThroughWall, transitElementList) relaxedLengthList, xiList = colonSegmentTubeMeshInnerPoints.getRelaxedLengthAndXiList() closedProximalEnd = False if tcThickness > 0: tubeTCWidthList = colonSegmentTubeMeshInnerPoints.getTubeTCWidthList() xList, d1List, d2List, d3List, annotationArrayAround = getTeniaColi( region, xList, d1List, d2List, d3List, curvatureList, tcCount, elementsCountAroundTC, elementsCountAroundHaustrum, elementsCountAlong, elementsCountThroughWall, tubeTCWidthList, tcThickness, sxRefExtrudeList, annotationGroupsAround, closedProximalEnd) # Create flat and texture coordinates xFlat, d1Flat, d2Flat, xTexture, d1Texture, d2Texture = createFlatAndTextureCoordinatesTeniaColi( xiList, relaxedLengthList, length, wallThickness, tcCount, tcThickness, elementsCountAroundTC, elementsCountAroundHaustrum, elementsCountAlong, elementsCountThroughWall, transitElementList, closedProximalEnd) # Create nodes and elements nextNodeIdentifier, nextElementIdentifier, annotationGroups = createNodesAndElementsTeniaColi( region, xList, d1List, d2List, d3List, xFlat, d1Flat, d2Flat, xTexture, d1Texture, d2Texture, elementsCountAroundTC, elementsCountAroundHaustrum, elementsCountAlong, elementsCountThroughWall, tcCount, annotationGroupsAround, annotationGroupsAlong, annotationGroupsThroughWall, firstNodeIdentifier, firstElementIdentifier, useCubicHermiteThroughWall, useCrossDerivatives, closedProximalEnd) else: # Create flat and texture coordinates xFlat, d1Flat, d2Flat, xTexture, d1Texture, d2Texture = tubemesh.createFlatAndTextureCoordinates( xiList, relaxedLengthList, length, wallThickness, elementsCountAround, elementsCountAlong, elementsCountThroughWall, transitElementList) # Create nodes and elements nextNodeIdentifier, nextElementIdentifier, annotationGroups = tubemesh.createNodesAndElements( region, xList, d1List, d2List, d3List, xFlat, d1Flat, d2Flat, xTexture, d1Texture, d2Texture, elementsCountAround, elementsCountAlong, elementsCountThroughWall, annotationGroupsAround, annotationGroupsAlong, annotationGroupsThroughWall, firstNodeIdentifier, firstElementIdentifier, useCubicHermiteThroughWall, useCrossDerivatives, closedProximalEnd) return annotationGroups
def generateBaseMesh(cls, region, options, baseCentre=[0.0, 0.0, 0.0], axisSide1=[0.0, -1.0, 0.0], axisUp=[0.0, 0.0, 1.0]): """ Generate the base bicubic-linear Hermite mesh. See also generateMesh(). Optional extra parameters allow centre and axes to be set. :param region: Zinc region to define model in. Must be empty. :param options: Dict containing options. See getDefaultOptions(). :param baseCentre: Centre of valve on ventriculo-arterial junction. :param axisSide: Unit vector in first side direction where angle around starts. :param axisUp: Unit vector in outflow direction of valve. :return: list of AnnotationGroup """ unitScale = options['Unit scale'] outerHeight = unitScale * options['Outer height'] innerDepth = unitScale * options['Inner depth'] cuspHeight = unitScale * options['Cusp height'] innerRadius = unitScale * 0.5 * options['Inner diameter'] sinusRadialDisplacement = unitScale * options[ 'Sinus radial displacement'] wallThickness = unitScale * options['Wall thickness'] cuspThickness = unitScale * options['Cusp thickness'] aorticNotPulmonary = options['Aortic not pulmonary'] useCrossDerivatives = False fm = region.getFieldmodule() fm.beginChange() coordinates = zinc_utils.getOrCreateCoordinateField(fm) cache = fm.createFieldcache() if aorticNotPulmonary: arterialRootGroup = AnnotationGroup(region, 'root of aorta', FMANumber=3740, lyphID='Lyph ID unknown') cuspGroups = [ AnnotationGroup(region, 'posterior cusp of aortic valve', FMANumber=7253, lyphID='Lyph ID unknown'), AnnotationGroup(region, 'right cusp of aortic valve', FMANumber=7252, lyphID='Lyph ID unknown'), AnnotationGroup(region, 'left cusp of aortic valve', FMANumber=7251, lyphID='Lyph ID unknown') ] else: arterialRootGroup = AnnotationGroup(region, 'root of pulmonary trunk', FMANumber=8612, lyphID='Lyph ID unknown') cuspGroups = [ AnnotationGroup(region, 'right cusp of pulmonary valve', FMANumber=7250, lyphID='Lyph ID unknown'), AnnotationGroup(region, 'anterior cusp of pulmonary valve', FMANumber=7249, lyphID='Lyph ID unknown'), AnnotationGroup(region, 'left cusp of pulmonary valve', FMANumber=7247, lyphID='Lyph ID unknown') ] allGroups = [arterialRootGroup ] # groups that all elements in scaffold will go in annotationGroups = allGroups + cuspGroups # annotation fiducial points fiducialGroup = zinc_utils.getOrCreateGroupField(fm, 'fiducial') fiducialCoordinates = zinc_utils.getOrCreateCoordinateField( fm, 'fiducial_coordinates') fiducialLabel = zinc_utils.getOrCreateLabelField(fm, 'fiducial_label') #fiducialElementXi = zinc_utils.getOrCreateElementXiField(fm, 'fiducial_element_xi') datapoints = fm.findNodesetByFieldDomainType( Field.DOMAIN_TYPE_DATAPOINTS) fiducialPoints = zinc_utils.getOrCreateNodesetGroup( fiducialGroup, datapoints) datapointTemplateExternal = datapoints.createNodetemplate() datapointTemplateExternal.defineField(fiducialCoordinates) datapointTemplateExternal.defineField(fiducialLabel) ################# # 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) # most nodes in this scaffold do not have a DS3 derivative nodetemplateLinearS3 = nodes.createNodetemplate() nodetemplateLinearS3.defineField(coordinates) nodetemplateLinearS3.setValueNumberOfVersions(coordinates, -1, Node.VALUE_LABEL_VALUE, 1) nodetemplateLinearS3.setValueNumberOfVersions(coordinates, -1, Node.VALUE_LABEL_D_DS1, 1) nodetemplateLinearS3.setValueNumberOfVersions(coordinates, -1, Node.VALUE_LABEL_D_DS2, 1) # several only have a DS1 derivative nodetemplateLinearS2S3 = nodes.createNodetemplate() nodetemplateLinearS2S3.defineField(coordinates) nodetemplateLinearS2S3.setValueNumberOfVersions( coordinates, -1, Node.VALUE_LABEL_VALUE, 1) nodetemplateLinearS2S3.setValueNumberOfVersions( coordinates, -1, Node.VALUE_LABEL_D_DS1, 1) nodeIdentifier = max(1, zinc_utils.getMaximumNodeIdentifier(nodes) + 1) elementsCountAround = 6 radiansPerElementAround = 2.0 * math.pi / elementsCountAround axisSide2 = vector.crossproduct3(axisUp, axisSide1) outerRadius = innerRadius + wallThickness cuspOuterLength2 = 0.5 * getApproximateEllipsePerimeter( innerRadius, cuspHeight) cuspOuterWallArcLength = cuspOuterLength2 * innerRadius / ( innerRadius + cuspHeight) noduleOuterAxialArcLength = cuspOuterLength2 - cuspOuterWallArcLength noduleOuterRadialArcLength = innerRadius cuspOuterWalld1 = interp.interpolateLagrangeHermiteDerivative( [innerRadius, outerHeight + innerDepth - cuspHeight], [0.0, 0.0], [-innerRadius, 0.0], 0.0) sin60 = math.sin(math.pi / 3.0) cuspThicknessLowerFactor = 4.5 # GRC fudge factor cuspInnerLength2 = 0.5 * getApproximateEllipsePerimeter( innerRadius - cuspThickness / sin60, cuspHeight - cuspThicknessLowerFactor * cuspThickness) noduleInnerAxialArcLength = cuspInnerLength2 * ( cuspHeight - cuspThicknessLowerFactor * cuspThickness) / ( innerRadius - cuspThickness / sin60 + cuspHeight - cuspThicknessLowerFactor * cuspThickness) noduleInnerRadialArcLength = innerRadius - cuspThickness / math.tan( math.pi / 3.0) nMidCusp = 0 if aorticNotPulmonary else 1 # lower points ix, id1 = createCirclePoints( [(baseCentre[c] - axisUp[c] * innerDepth) for c in range(3)], [axisSide1[c] * innerRadius for c in range(3)], [axisSide2[c] * innerRadius for c in range(3)], elementsCountAround) ox, od1 = getSemilunarValveSinusPoints(baseCentre, axisSide1, axisSide2, outerRadius, sinusRadialDisplacement, startMidCusp=aorticNotPulmonary) lowerx, lowerd1 = [ix, ox], [id1, od1] # upper points topCentre = [(baseCentre[c] + axisUp[c] * outerHeight) for c in range(3)] # twice as many on inner: ix, id1 = createCirclePoints( topCentre, [axisSide1[c] * innerRadius for c in range(3)], [axisSide2[c] * innerRadius for c in range(3)], elementsCountAround * 2) # tweak inner points so elements attached to cusps are narrower cuspRadiansFactor = 0.25 # GRC fudge factor midDerivativeFactor = 1.0 + 0.5 * (1.0 - cuspRadiansFactor ) # GRC test compromise cuspAttachmentRadians = cuspRadiansFactor * radiansPerElementAround cuspAttachmentRadialDisplacement = wallThickness * 0.333 # GRC fudge factor cuspAttachmentRadius = innerRadius - cuspAttachmentRadialDisplacement for cusp in range(3): n1 = cusp * 2 - 1 + nMidCusp n2 = n1 * 2 id1[n2 + 2] = [2.0 * d for d in id1[n2 + 2]] # side 1 radiansAround = n1 * radiansPerElementAround + cuspAttachmentRadians rcosRadiansAround = cuspAttachmentRadius * math.cos(radiansAround) rsinRadiansAround = cuspAttachmentRadius * math.sin(radiansAround) ix[n2 + 1] = [(topCentre[c] + rcosRadiansAround * axisSide1[c] + rsinRadiansAround * axisSide2[c]) for c in range(3)] id1[n2 + 1] = interp.interpolateLagrangeHermiteDerivative( ix[n2 + 1], ix[n2 + 2], id1[n2 + 2], 0.0) # side 2 n1 = ((cusp + 1) * 2 - 1 + nMidCusp) % elementsCountAround n2 = n1 * 2 radiansAround = n1 * radiansPerElementAround - cuspAttachmentRadians rcosRadiansAround = cuspAttachmentRadius * math.cos(radiansAround) rsinRadiansAround = cuspAttachmentRadius * math.sin(radiansAround) ix[n2 - 1] = [(topCentre[c] + rcosRadiansAround * axisSide1[c] + rsinRadiansAround * axisSide2[c]) for c in range(3)] id1[n2 - 1] = interp.interpolateHermiteLagrangeDerivative( ix[n2 - 2], id1[n2 - 2], ix[n2 - 1], 1.0) ox, od1 = createCirclePoints( topCentre, [axisSide1[c] * outerRadius for c in range(3)], [axisSide2[c] * outerRadius for c in range(3)], elementsCountAround) upperx, upperd1 = [ix, ox], [id1, od1] # get lower and upper derivative 2 zero = [0.0, 0.0, 0.0] upperd2factor = outerHeight upd2 = [d * upperd2factor for d in axisUp] lowerOuterd2 = interp.smoothCubicHermiteDerivativesLine( [lowerx[1][nMidCusp], upperx[1][nMidCusp]], [upd2, upd2], fixStartDirection=True, fixEndDerivative=True)[0] lowerd2factor = 2.0 * (outerHeight + innerDepth) - upperd2factor lowerInnerd2 = [d * lowerd2factor for d in axisUp] lowerd2 = [[lowerInnerd2] * elementsCountAround, [lowerOuterd2] * elementsCountAround ] # some lowerd2[0] to be fitted below upperd2 = [[upd2] * (elementsCountAround * 2), [upd2] * elementsCountAround] # get lower and upper derivative 1 or 2 pointing to/from cusps for n1 in range(elementsCountAround): radiansAround = n1 * radiansPerElementAround cosRadiansAround = math.cos(radiansAround) sinRadiansAround = math.sin(radiansAround) if (n1 % 2) == nMidCusp: lowerd2[0][n1] = [ -cuspOuterWallArcLength * (cosRadiansAround * axisSide1[c] + sinRadiansAround * axisSide2[c]) for c in range(3) ] else: upperd1[0][n1 * 2] = [ (cuspOuterWalld1[0] * (cosRadiansAround * axisSide1[c] + sinRadiansAround * axisSide2[c]) + cuspOuterWalld1[1] * axisUp[c]) for c in range(3) ] # inner wall and mid sinus points; only every second one is used sinusDepth = innerDepth - cuspThicknessLowerFactor * cuspThickness # GRC test sinusCentre = [(baseCentre[c] - sinusDepth * axisUp[c]) for c in range(3)] sinusx, sinusd1 = createCirclePoints( sinusCentre, [axisSide1[c] * innerRadius for c in range(3)], [axisSide2[c] * innerRadius for c in range(3)], elementsCountAround) # get sinusd2, parallel to lower inclined lines sd2 = interp.smoothCubicHermiteDerivativesLine( [[innerRadius, -sinusDepth], [innerRadius, outerHeight]], [[wallThickness + sinusRadialDisplacement, innerDepth], [0.0, upperd2factor]], fixStartDirection=True, fixEndDerivative=True)[0] sinusd2 = [None] * elementsCountAround for cusp in range(3): n1 = cusp * 2 + nMidCusp radiansAround = n1 * radiansPerElementAround cosRadiansAround = math.cos(radiansAround) sinRadiansAround = math.sin(radiansAround) sinusd2[n1] = [(sd2[0] * (cosRadiansAround * axisSide1[c] + sinRadiansAround * axisSide2[c]) + sd2[1] * axisUp[c]) for c in range(3)] # get points on arc between mid sinus and upper cusp points arcx = [] arcd1 = [] scaled1 = 2.5 # GRC fudge factor for cusp in range(3): n1 = cusp * 2 + nMidCusp n1m = n1 - 1 n1p = (n1 + 1) % elementsCountAround n2m = n1m * 2 + 1 n2p = n1p * 2 - 1 ax, ad1 = interp.sampleCubicHermiteCurves( [upperx[0][n2m], sinusx[n1]], [[-scaled1 * d for d in upperd2[0][n2m]], [scaled1 * d for d in sinusd1[n1]]], elementsCountOut=2, addLengthStart=0.5 * vector.magnitude(upperd2[0][n2m]), lengthFractionStart=0.5, addLengthEnd=0.5 * vector.magnitude(sinusd1[n1]), lengthFractionEnd=0.5, arcLengthDerivatives=False)[0:2] arcx.append(ax[1]) arcd1.append(ad1[1]) ax, ad1 = interp.sampleCubicHermiteCurves( [ sinusx[n1], upperx[0][n2p], ], [[scaled1 * d for d in sinusd1[n1]], [scaled1 * d for d in upperd2[0][n2p]]], elementsCountOut=2, addLengthStart=0.5 * vector.magnitude(sinusd1[n1]), lengthFractionStart=0.5, addLengthEnd=0.5 * vector.magnitude(upperd2[0][n2p]), lengthFractionEnd=0.5, arcLengthDerivatives=False)[0:2] arcx.append(ax[1]) arcd1.append(ad1[1]) if nMidCusp == 0: arcx.append(arcx.pop(0)) arcd1.append(arcd1.pop(0)) # cusp nodule points noduleCentre = [(baseCentre[c] + axisUp[c] * (cuspHeight - innerDepth)) for c in range(3)] nodulex = [[], []] noduled1 = [[], []] noduled2 = [[], []] noduled3 = [[], []] cuspRadialThickness = cuspThickness / sin60 for i in range(3): nodulex[0].append(noduleCentre) n1 = i * 2 + nMidCusp radiansAround = n1 * radiansPerElementAround cosRadiansAround = math.cos(radiansAround) sinRadiansAround = math.sin(radiansAround) nodulex[1].append([(noduleCentre[c] + cuspRadialThickness * (cosRadiansAround * axisSide1[c] + sinRadiansAround * axisSide2[c])) for c in range(3)]) n1 = i * 2 - 1 + nMidCusp radiansAround = n1 * radiansPerElementAround cosRadiansAround = math.cos(radiansAround) sinRadiansAround = math.sin(radiansAround) noduled1[0].append([ noduleOuterRadialArcLength * (cosRadiansAround * axisSide1[c] + sinRadiansAround * axisSide2[c]) for c in range(3) ]) noduled1[1].append( vector.setMagnitude(noduled1[0][i], noduleInnerRadialArcLength)) n1 = i * 2 + 1 + nMidCusp radiansAround = n1 * radiansPerElementAround cosRadiansAround = math.cos(radiansAround) sinRadiansAround = math.sin(radiansAround) noduled2[0].append([ noduleOuterRadialArcLength * (cosRadiansAround * axisSide1[c] + sinRadiansAround * axisSide2[c]) for c in range(3) ]) noduled2[1].append( vector.setMagnitude(noduled2[0][i], noduleInnerRadialArcLength)) noduled3[0].append( [noduleOuterAxialArcLength * axisUp[c] for c in range(3)]) noduled3[1].append( [noduleInnerAxialArcLength * axisUp[c] for c in range(3)]) # Create nodes lowerNodeId = [[], []] for n3 in range(2): for n1 in range(elementsCountAround): node = nodes.createNode(nodeIdentifier, nodetemplateLinearS3) cache.setNode(node) coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_VALUE, 1, lowerx[n3][n1]) coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS1, 1, lowerd1[n3][n1]) coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS2, 1, lowerd2[n3][n1]) lowerNodeId[n3].append(nodeIdentifier) nodeIdentifier += 1 sinusNodeId = [] for n1 in range(elementsCountAround): if (n1 % 2) != nMidCusp: sinusNodeId.append(None) continue node = nodes.createNode(nodeIdentifier, nodetemplateLinearS3) cache.setNode(node) coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_VALUE, 1, sinusx[n1]) coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS1, 1, sinusd1[n1]) coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS2, 1, sinusd2[n1]) sinusNodeId.append(nodeIdentifier) nodeIdentifier += 1 arcNodeId = [] for n1 in range(elementsCountAround): node = nodes.createNode(nodeIdentifier, nodetemplateLinearS2S3) cache.setNode(node) coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_VALUE, 1, arcx[n1]) coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS1, 1, arcd1[n1]) arcNodeId.append(nodeIdentifier) nodeIdentifier += 1 noduleNodeId = [[], []] for n3 in range(2): for n1 in range(3): node = nodes.createNode(nodeIdentifier, nodetemplate) cache.setNode(node) coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_VALUE, 1, nodulex[n3][n1]) coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS1, 1, noduled1[n3][n1]) coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS2, 1, noduled2[n3][n1]) coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS3, 1, noduled3[n3][n1]) noduleNodeId[n3].append(nodeIdentifier) nodeIdentifier += 1 upperNodeId = [[], []] for n3 in range(2): for n1 in range(len(upperx[n3])): node = nodes.createNode(nodeIdentifier, nodetemplateLinearS3) cache.setNode(node) coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_VALUE, 1, upperx[n3][n1]) coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS1, 1, upperd1[n3][n1]) coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS2, 1, upperd2[n3][n1]) upperNodeId[n3].append(nodeIdentifier) nodeIdentifier += 1 ################# # Create elements ################# mesh = fm.findMeshByDimension(3) allMeshGroups = [allGroup.getMeshGroup(mesh) for allGroup in allGroups] cuspMeshGroups = [ cuspGroup.getMeshGroup(mesh) for cuspGroup in cuspGroups ] linearHermiteLinearBasis = fm.createElementbasis( 3, Elementbasis.FUNCTION_TYPE_LINEAR_LAGRANGE) linearHermiteLinearBasis.setFunctionType( 2, Elementbasis.FUNCTION_TYPE_CUBIC_HERMITE) hermiteLinearLinearBasis = fm.createElementbasis( 3, Elementbasis.FUNCTION_TYPE_LINEAR_LAGRANGE) hermiteLinearLinearBasis.setFunctionType( 1, Elementbasis.FUNCTION_TYPE_CUBIC_HERMITE) bicubichermitelinear = eftfactory_bicubichermitelinear( mesh, useCrossDerivatives) eftDefault = bicubichermitelinear.createEftNoCrossDerivatives() elementIdentifier = max( 1, zinc_utils.getMaximumElementIdentifier(mesh) + 1) elementtemplate1 = mesh.createElementtemplate() elementtemplate1.setElementShapeType(Element.SHAPE_TYPE_CUBE) # wall elements for cusp in range(3): n1 = cusp * 2 - 1 + nMidCusp n2 = n1 * 2 for e in range(6): eft1 = None scalefactors = None if (e == 0) or (e == 5): # 6 node linear-hermite-linear collapsed wedge element expanding from zero width on outer wall of root, attaching to vertical part of cusp eft1 = mesh.createElementfieldtemplate( linearHermiteLinearBasis) # switch mappings to use DS2 instead of default DS1 remapEftNodeValueLabel(eft1, [1, 2, 3, 4, 5, 6, 7, 8], Node.VALUE_LABEL_D_DS1, [(Node.VALUE_LABEL_D_DS2, [])]) if e == 0: nids = [ lowerNodeId[0][n1], arcNodeId[n1], upperNodeId[0][n2], upperNodeId[0][n2 + 1], lowerNodeId[1][n1], upperNodeId[1][n1] ] setEftScaleFactorIds(eft1, [1], []) scalefactors = [-1.0] remapEftNodeValueLabel(eft1, [2], Node.VALUE_LABEL_D_DS2, [(Node.VALUE_LABEL_D_DS1, [1])]) else: nids = [ arcNodeId[n1 + 1], lowerNodeId[0][n1 - 4], upperNodeId[0][n2 + 3], upperNodeId[0][n2 - 8], lowerNodeId[1][n1 - 4], upperNodeId[1][n1 - 4] ] remapEftNodeValueLabel(eft1, [1], Node.VALUE_LABEL_D_DS2, [(Node.VALUE_LABEL_D_DS1, [])]) ln_map = [1, 2, 3, 4, 5, 5, 6, 6] remapEftLocalNodes(eft1, 6, ln_map) elif (e == 1) or (e == 4): # 6 node hermite-linear-linear collapsed wedge element on lower wall eft1 = mesh.createElementfieldtemplate( hermiteLinearLinearBasis) if e == 1: nids = [ lowerNodeId[0][n1], lowerNodeId[0][n1 + 1], arcNodeId[n1], sinusNodeId[n1 + 1], lowerNodeId[1][n1], lowerNodeId[1][n1 + 1] ] else: nids = [ lowerNodeId[0][n1 + 1], lowerNodeId[0][n1 - 4], sinusNodeId[n1 + 1], arcNodeId[n1 + 1], lowerNodeId[1][n1 + 1], lowerNodeId[1][n1 - 4] ] ln_map = [1, 2, 3, 4, 5, 6, 5, 6] remapEftLocalNodes(eft1, 6, ln_map) else: # 8 node elements with wedges on two sides if e == 2: eft1 = bicubichermitelinear.createEftNoCrossDerivatives( ) setEftScaleFactorIds(eft1, [1], []) scalefactors = [-1.0] nids = [ arcNodeId[n1], sinusNodeId[n1 + 1], upperNodeId[0][n2 + 1], upperNodeId[0][n2 + 2], lowerNodeId[1][n1], lowerNodeId[1][n1 + 1], upperNodeId[1][n1], upperNodeId[1][n1 + 1] ] remapEftNodeValueLabel(eft1, [1], Node.VALUE_LABEL_D_DS2, [(Node.VALUE_LABEL_D_DS1, [1])]) else: eft1 = eftDefault nids = [ sinusNodeId[n1 + 1], arcNodeId[n1 + 1], upperNodeId[0][n2 + 2], upperNodeId[0][n2 + 3], lowerNodeId[1][n1 + 1], lowerNodeId[1][n1 - 4], upperNodeId[1][n1 + 1], upperNodeId[1][n1 - 4] ] remapEftNodeValueLabel(eft1, [2], Node.VALUE_LABEL_D_DS2, [(Node.VALUE_LABEL_D_DS1, [])]) result = elementtemplate1.defineField(coordinates, -1, eft1) element = mesh.createElement(elementIdentifier, elementtemplate1) result2 = element.setNodesByIdentifier(eft1, nids) if scalefactors: result3 = element.setScaleFactors(eft1, scalefactors) else: result3 = 7 #print('create arterial root wall', cusp, e, 'element',elementIdentifier, result, result2, result3, nids) elementIdentifier += 1 for meshGroup in allMeshGroups: meshGroup.addElement(element) # cusps (leaflets) for cusp in range(3): n1 = cusp * 2 - 1 + nMidCusp n2 = n1 * 2 meshGroups = allMeshGroups + [cuspMeshGroups[cusp]] for e in range(2): eft1 = bicubichermitelinear.createEftNoCrossDerivatives() setEftScaleFactorIds(eft1, [1], []) scalefactors = [-1.0] if e == 0: nids = [ lowerNodeId[0][n1], lowerNodeId[0][n1 + 1], upperNodeId[0][n2], noduleNodeId[0][cusp], arcNodeId[n1], sinusNodeId[n1 + 1], upperNodeId[0][n2 + 1], noduleNodeId[1][cusp] ] remapEftNodeValueLabel(eft1, [4, 8], Node.VALUE_LABEL_D_DS1, [(Node.VALUE_LABEL_D_DS1, [1])]) remapEftNodeValueLabel(eft1, [4, 8], Node.VALUE_LABEL_D_DS2, [(Node.VALUE_LABEL_D_DS3, [])]) remapEftNodeValueLabel(eft1, [5], Node.VALUE_LABEL_D_DS2, [(Node.VALUE_LABEL_D_DS1, [1])]) remapEftNodeValueLabel(eft1, [6], Node.VALUE_LABEL_D_DS2, [(Node.VALUE_LABEL_D_DS2, [1])]) remapEftNodeValueLabel(eft1, [7], Node.VALUE_LABEL_D_DS1, [(Node.VALUE_LABEL_D_DS1, [1])]) else: nids = [ lowerNodeId[0][n1 + 1], lowerNodeId[0][n1 - 4], noduleNodeId[0][cusp], upperNodeId[0][n2 - 8], sinusNodeId[n1 + 1], arcNodeId[n1 + 1], noduleNodeId[1][cusp], upperNodeId[0][n2 + 3] ] remapEftNodeValueLabel(eft1, [3, 7], Node.VALUE_LABEL_D_DS2, [(Node.VALUE_LABEL_D_DS3, [])]) remapEftNodeValueLabel(eft1, [3, 7], Node.VALUE_LABEL_D_DS1, [(Node.VALUE_LABEL_D_DS2, [])]) remapEftNodeValueLabel(eft1, [4, 8], Node.VALUE_LABEL_D_DS1, [(Node.VALUE_LABEL_D_DS1, [1])]) remapEftNodeValueLabel(eft1, [5], Node.VALUE_LABEL_D_DS2, [(Node.VALUE_LABEL_D_DS2, [1])]) remapEftNodeValueLabel(eft1, [6], Node.VALUE_LABEL_D_DS2, [(Node.VALUE_LABEL_D_DS1, [])]) result = elementtemplate1.defineField(coordinates, -1, eft1) element = mesh.createElement(elementIdentifier, elementtemplate1) result2 = element.setNodesByIdentifier(eft1, nids) if scalefactors: result3 = element.setScaleFactors(eft1, scalefactors) else: result3 = 7 #print('create semilunar cusp', cusp, e, 'element',elementIdentifier, result, result2, result3, nids) elementIdentifier += 1 for meshGroup in meshGroups: meshGroup.addElement(element) # create annotation points datapoint = fiducialPoints.createNode(-1, datapointTemplateExternal) cache.setNode(datapoint) fiducialCoordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_VALUE, 1, noduleCentre) fiducialLabel.assignString( cache, 'aortic valve ctr' if aorticNotPulmonary else 'pulmonary valve ctr') fm.endChange() return annotationGroups