def getCircleProjectionAxes(ax, ad1, ad2, ad3, length, angle1radians, angle2radians, angle3radians=None): ''' Project coordinates and orthogonal unit axes ax, ad1, ad2, ad3 by length in direction of ad3, rotated towards ad1 by angle1radians and ad2 by angle2radians such that it conforms to a circular arc of the given length, tangential to ad3 at the start and the final bd3 at the end. All vectors must be length 3. Assumes angles 1 and 2 are not large e.g. less than 90 degrees. Note: not robust for all inputs. :param angle3radians: Optional final rotation of projection axes about bd3. :return: Final coordinates and orthogonal unit axes: bx, bd1, bd2, bd3 ''' if (math.fabs(angle1radians) < 0.000001) and (math.fabs(angle2radians) < 0.000001): bx = [(ax[c] + length * ad3[c]) for c in range(3)] bd1 = copy.deepcopy(ad1) bd2 = copy.deepcopy(ad2) bd3 = copy.deepcopy(ad3) else: cosAngle1 = math.cos(angle1radians) sinAngle1 = math.sin(angle1radians) cosAngle2 = math.cos(angle2radians) sinAngle2 = math.sin(angle2radians) f1 = sinAngle1 * cosAngle2 f2 = cosAngle1 * sinAngle2 f3 = cosAngle1 * cosAngle2 angleAroundRadians = math.atan2(f2, f1) fh = math.sqrt(f1 * f1 + f2 * f2) arcAngleRadians = 0.5 * math.pi - math.atan2(f3, fh) arcRadius = length / arcAngleRadians br = arcRadius * (1.0 - math.cos(arcAngleRadians)) w1 = br * math.cos(angleAroundRadians) # f1/fh w2 = br * math.sin(angleAroundRadians) # f2/fh w3 = arcRadius * math.sin(arcAngleRadians) bx = [(ax[c] + w1 * ad1[c] + w2 * ad2[c] + w3 * ad3[c]) for c in range(3)] bd3 = vector.normalise([(f1 * ad1[c] + f2 * ad2[c] + f3 * ad3[c]) for c in range(3)]) bd1 = vector.normalise(vector.crossproduct3(ad2, bd3)) bd2 = vector.crossproduct3(bd3, bd1) if angle3radians: cosAngle3 = math.cos(angle3radians) sinAngle3 = math.sin(angle3radians) bd1, bd2 = [ (cosAngle3 * bd1[c] + sinAngle3 * bd2[c]) for c in range(3) ], [(cosAngle3 * bd2[c] - sinAngle3 * bd1[c]) for c in range(3)] return bx, bd1, bd2, bd3
def calculate_surface_axes(d1, d2, direction): ''' :return: Vectors ax1, ax2, ax3: ax1 in-plane in 3-D vector direction, ax2 in-plane normal to a and ax3 normal to the surface plane. Vectors all have unit magnitude. ''' ax3 = vector.normalise(vector.crossproduct3(d1, d2)) delta_xi1, delta_xi2 = calculate_surface_delta_xi(d1, d2, direction) ax1 = vector.normalise([ delta_xi1*d1[c] + delta_xi2*d2[c] for c in range(3) ]) ax2 = vector.normalise(vector.crossproduct3(ax3, ax1)) return ax1, ax2, ax3
def makeSideDerivativesNormal(cls, region, options, functionOptions, editGroupName): makeD2Normal = options['D2 derivatives'] and functionOptions['Make D2 normal'] makeD3Normal = options['D3 derivatives'] and functionOptions['Make D3 normal'] if not (makeD2Normal or makeD3Normal): return False, False valueLabels = [ Node.VALUE_LABEL_D_DS1 ] if options['D2 derivatives']: valueLabels.append(Node.VALUE_LABEL_D_DS2) if options['D3 derivatives']: valueLabels.append(Node.VALUE_LABEL_D_DS3) parameters = extractPathParametersFromRegion(region, valueLabels) d1 = parameters[0] modifyParameters = [] modifyValueLabels = [] if makeD2Normal: d2 = parameters[1] for c in range(len(d1)): td2 = vector.vectorRejection(d2[c], d1[c]) d2[c] = vector.setMagnitude(td2, vector.magnitude(d2[c])) modifyParameters.append(d2) modifyValueLabels.append(Node.VALUE_LABEL_D_DS2) if makeD3Normal: d3 = parameters[-1] if options['D2 derivatives']: d2 = parameters[1] for c in range(len(d1)): d3[c] = vector.setMagnitude(vector.crossproduct3(d1[c], d2[c]), vector.magnitude(d3[c])) else: for c in range(len(d1)): td3 = vector.vectorRejection(d3[c], d1[c]) d3[c] = vector.setMagnitude(td3, vector.magnitude(d3[c])) modifyParameters.append(d3) modifyValueLabels.append(Node.VALUE_LABEL_D_DS3) setPathParameters(region, modifyValueLabels, modifyParameters, editGroupName) return False, True # settings not changed, nodes changed
def __init__(self, elementsCountAcrossMajor, elementsCountAcrossMinor, centre=None, alongAxis=None, majorAxis=None, minorRadius=None): """ :param elementsCountAcrossMajor: Number of elements across major axis. Must be at least 2 + elementsCountRim for half and 4 + elementsCountRim for full cylinder. :param elementsCountAcrossMinor: Number of elements across minor axis. :param centre: Centre of the ellipse. :param alongAxis: The cylinder axis that the base is extruded along. :param majorAxis: The major axis of the base. Should be perpendicular to alongAxis :param minorRadius: The minor radius of the ellipse. """ self._centre = centre self._alongAxis = alongAxis self._majorAxis = majorAxis self._minorRadius = minorRadius if alongAxis: self._minorAxis = vector.setMagnitude( vector.crossproduct3(alongAxis, majorAxis), minorRadius) self._elementsCountAcrossMinor = elementsCountAcrossMinor self._elementsCountAcrossMajor = elementsCountAcrossMajor self._majorRadius = vector.magnitude(majorAxis) self.px = None self.pd1 = None self.pd2 = None self.pd3 = None
def projectHermiteCurvesThroughWall(nx, nd1, nd2, n, wallThickness, loop = False): ''' From Hermite curve nx, nd1 with cross direction nd2, project normal to wall by wall thickness to get coordinates, d1 affected by curvature etc. Assumes 3 components. :param n: Index into nx, nd1, nd2 of where to project. :param wallThickness: Use positive from in to out, negative from outside to in. :return: x, d1, d2, d3 ''' maxPointIndex = len(nx) - 1 assert (0 <= n <= maxPointIndex), 'projectHermiteCurvesThroughWall. Invalid index' unitNormal = vector.normalise(vector.crossproduct3(nd1[n], nd2[n])) x = [ (nx[n][c] + wallThickness*unitNormal[c]) for c in range(3) ] # calculate inner d1 from curvature around curvature = 0.0 count = 0 if loop or (n > 0) and (nx[n - 1]): curvature += getCubicHermiteCurvature(nx[n - 1], nd1[n - 1], nx[n], nd1[n], unitNormal, 1.0) count += 1 if loop or (n < maxPointIndex) and (nx[n - maxPointIndex]): curvature += getCubicHermiteCurvature(nx[n], nd1[n], nx[n - maxPointIndex], nd1[n - maxPointIndex], unitNormal, 0.0) count += 1 curvature /= count factor = 1.0 - curvature*wallThickness d1 = [ factor*c for c in nd1[n] ] d2 = copy.deepcopy(nd2[n]) # magnitude can't be determined here d3 = vector.setMagnitude(unitNormal, math.fabs(wallThickness)) return x, d1, d2, d3
def createEllipsoidPoints(centre, poleAxis, sideAxis, elementsCountAround, elementsCountUp, height): ''' Generate a set of points and derivatives for circle of revolution of an ellipse starting at pole poleAxis from centre. :param centre: Centre of full ellipsoid. :param poleAxis: Vector in direction of starting pole, magnitude is ellipse axis length. :param sideAxis: Vector normal to poleAxis, magnitude is ellipse side axis length. :param height: Height of arc of ellipsoid from starting pole along poleAxis. :return: Lists nx, nd1, nd2. Ordered fastest around, starting at pole. Suitable for passing to TrackSurface. ''' nx = [] nd1 = [] nd2 = [] magPoleAxis = vector.magnitude(poleAxis) magSideAxis = vector.magnitude(sideAxis) unitPoleAxis = vector.normalise(poleAxis) unitSideAxis1 = vector.normalise(sideAxis) unitSideAxis2 = vector.normalise(vector.crossproduct3(sideAxis, poleAxis)) useHeight = min(max(0.0, height), 2.0 * magPoleAxis) totalRadiansUp = getEllipseRadiansToX(magPoleAxis, 0.0, magPoleAxis - useHeight, initialTheta=0.5 * math.pi * useHeight / magPoleAxis) radiansUp = 0.0 lengthUp = getEllipseArcLength(magPoleAxis, magSideAxis, radiansUp, totalRadiansUp) elementLengthUp = lengthUp / elementsCountUp radiansPerElementAround = 2.0 * math.pi / elementsCountAround for n2 in range(elementsCountUp + 1): cosRadiansUp = math.cos(radiansUp) sinRadiansUp = math.sin(radiansUp) radius = sinRadiansUp * magSideAxis d2r, d2z = vector.setMagnitude( [cosRadiansUp * magSideAxis, sinRadiansUp * magPoleAxis], elementLengthUp) cx = [(centre[c] + cosRadiansUp * poleAxis[c]) for c in range(3)] elementLengthAround = radius * radiansPerElementAround radiansAround = 0.0 for n in range(elementsCountAround): cosRadiansAround = math.cos(radiansAround) sinRadiansAround = math.sin(radiansAround) nx.append([(cx[c] + radius * (cosRadiansAround * unitSideAxis1[c] + sinRadiansAround * unitSideAxis2[c])) for c in range(3)]) nd1.append([ (elementLengthAround * (-sinRadiansAround * unitSideAxis1[c] + cosRadiansAround * unitSideAxis2[c])) for c in range(3) ]) nd2.append([(d2r * (cosRadiansAround * unitSideAxis1[c] + sinRadiansAround * unitSideAxis2[c]) - d2z * unitPoleAxis[c]) for c in range(3)]) radiansAround += radiansPerElementAround radiansUp = updateEllipseAngleByArcLength(magPoleAxis, magSideAxis, radiansUp, elementLengthUp) return nx, nd1, nd2
def normalToEllipse(v1, v2): """ Find unit normal vector of an ellipse using two vectors in the ellipse. The direction is v1xv2 :param v1: vector 1. :param v2: vector 2. :return: """ nte = vector.normalise(vector.crossproduct3(v1, v2)) return nte
def getSurfaceProjectionAxes(ax, ad1, ad2, ad3, angle1radians, angle2radians, length): ''' Project coordinates and orthogonal unit axes ax, ad1, ad2, ad3 in direction of ad3, rotated towards ad1 by angle1radians and ad2 by angle2radians by simple rotation. All vectors are/must be length 3. Assumes angles are not large e.g. less than 90 degrees. Note: not robust for all inputs. :return: Final coordinates and orthogonal unit axes: bx, bd1, bd2, bd3 ''' cosAngle1 = math.cos(angle1radians) sinAngle1 = math.sin(angle1radians) cosAngle2 = math.cos(angle2radians) sinAngle2 = math.sin(angle2radians) f1 = sinAngle1*cosAngle2 f2 = cosAngle1*sinAngle2 f3 = cosAngle1*cosAngle2 bd3 = [ (f1*ad1[c] + f2*ad2[c] + f3*ad3[c]) for c in range (3) ] bx = [ (ax[c] + length*bd3[c]) for c in range(3) ] bd1 = vector.crossproduct3(ad2, bd3) bd2 = vector.crossproduct3(bd3, bd1) return bx, bd1, bd2, bd3
def interpolateNodesCubicHermite(cache, coordinates, xi, normal_scale, \ node1, derivative1, scale1, cross_derivative1, cross_scale1, \ node2, derivative2, scale2, cross_derivative2, cross_scale2): """ Interpolates position and first derivative with cubic Hermite basis. Interpolates cross derivative linearly. :param cache: Field cache to evaluate in. :param coordinates: Coordinates field. :param xi: Element coordinate to interpolate at. :param normal_scale: Magnitude of normal derivative to return. :param node1, node2: Start and end nodes. :param derivative1, derivative2: Node value label for derivatives. :param scale1, scale2: Real value scaling derivatives, to reverse if needed. :param cross_derivative1, cross_derivative2: Node value label for cross derivatives. :param cross_scale1, cross_scale2: Real value scaling cross_derivatives, to reverse if needed. :return: x, dx_ds, dx_ds_cross, dx_ds_normal """ cache.setNode(node1) result, v1 = coordinates.getNodeParameters(cache, -1, Node.VALUE_LABEL_VALUE, 1, 3) result, d1 = coordinates.getNodeParameters(cache, -1, derivative1, 1, 3) result, d1c = coordinates.getNodeParameters(cache, -1, cross_derivative1, 1, 3) d1 = [scale1 * d for d in d1] d1c = [cross_scale1 * d for d in d1c] cache.setNode(node2) result, v2 = coordinates.getNodeParameters(cache, -1, Node.VALUE_LABEL_VALUE, 1, 3) result, d2 = coordinates.getNodeParameters(cache, -1, derivative2, 1, 3) result, d2c = coordinates.getNodeParameters(cache, -1, cross_derivative2, 1, 3) d2 = [scale2 * d for d in d2] d2c = [cross_scale2 * d for d in d2c] arcLength = interp.computeCubicHermiteArcLength(v1, d1, v2, d2, True) mag = arcLength / vector.magnitude(d1) d1 = [mag * d for d in d1] mag = arcLength / vector.magnitude(d2) d2 = [mag * d for d in d2] xr = 1.0 - xi x = interp.interpolateCubicHermite(v1, d1, v2, d2, xi) dx_ds = interp.interpolateCubicHermiteDerivative(v1, d1, v2, d2, xi) scale = min(xi, xr) dx_ds = [scale * d for d in dx_ds] dx_ds_cross = [(xr * d1c[c] + xi * d2c[c]) for c in range(3)] radialVector = vector.normalise(vector.crossproduct3(dx_ds_cross, dx_ds)) dx_ds_normal = [normal_scale * d for d in radialVector] return x, dx_ds, dx_ds_cross, dx_ds_normal
def getCubicHermiteCurvatureSimple(v1, d1, v2, d2, xi): """ :param v1, v2: Values at xi = 0.0 and xi = 1.0, respectively. :param d1, d2: Derivatives w.r.t. xi at xi = 0.0 and xi = 1.0, respectively. :param xi: Position in curve, nominally in [0.0, 1.0]. :return: Scalar curvature (1/R) of the 1-D cubic Hermite curve. """ tangent = interpolateCubicHermiteDerivative(v1, d1, v2, d2, xi) dTangent = interpolateCubicHermiteSecondDerivative(v1, d1, v2, d2, xi) cp = vector.crossproduct3(tangent, dTangent) curvature = vector.magnitude(cp) / (vector.magnitude(tangent)*vector.magnitude(tangent)*vector.magnitude(tangent)) return curvature
def generateBase1DMesh(self): """ Generate nodes around the perimeter of the ellipse. """ nx, nd1 = createEllipsePerimeter(self.centre, self.majorAxis, self.minorAxis, self.elementsCountAround, self.majorRadius) nte = normalToEllipse(self.majorAxis, self.minorAxis) tbx, tbd1, tbd2, tbd3 = [], [], [], [] for n in range(self.elementsCountAround + 1): tbx.append(nx[n]) tbd1.append(nd1[n]) tbd2.append(nte) tbd3.append(vector.normalise(vector.crossproduct3(tbd1[n], nte))) self.setRimNodes(tbx, tbd1, tbd2, tbd3)
def makeD3Normal(cls, region, options, editGroupName): if not options['D3 derivatives']: return if options['D2 derivatives']: d1, d2, d3 = extractPathParametersFromRegion( region, [ Node.VALUE_LABEL_D_DS1, Node.VALUE_LABEL_D_DS2, Node.VALUE_LABEL_D_DS3 ]) for c in range(len(d1)): d3[c] = vector.setMagnitude(vector.crossproduct3(d1[c], d2[c]), vector.magnitude(d3[c])) else: d1, d3 = extractPathParametersFromRegion( region, [Node.VALUE_LABEL_D_DS1, Node.VALUE_LABEL_D_DS3]) for c in range(len(d1)): td3 = vector.vectorRejection(d3[c], d1[c]) d3[c] = vector.setMagnitude(td3, vector.magnitude(d3[c])) setPathParameters(region, [Node.VALUE_LABEL_D_DS3], [d3], editGroupName) return False, True # settings not changed, nodes changed
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 __init__(self, elementsCountAcrossMajor, elementsCountAcrossMinor, elementsCountAcrossShell=0, elementsCountAcrossTransition=1, shellProportion=1.0, centre=None, alongAxis=None, majorAxis=None, minorRadius=None): """ :param elementsCountAcrossMajor: Number of elements across major axis. Must be at least 2 + elementsCountRim for half and 4 + elementsCountRim for full cylinder. :param elementsCountAcrossMinor: Number of elements across minor axis. :param elementsCountAcrossShell: Number of elements across shell. :param elementsCountAcrossTransition: Number of elements between core boundary and inner square. :param shellProportion: Ratio of thickness of each layer in shell wrt thickness of each layer in core. :param centre: Centre of the ellipse. :param alongAxis: The cylinder axis that the base is extruded along. :param majorAxis: The major axis of the base. Should be perpendicular to alongAxis :param minorRadius: The minor radius of the ellipse. """ self._centre = centre self._alongAxis = alongAxis self._majorAxis = majorAxis self._minorRadius = minorRadius if alongAxis: self._minorAxis = vector.setMagnitude( vector.crossproduct3(alongAxis, majorAxis), minorRadius) self._elementsCountAcrossMinor = elementsCountAcrossMinor self._elementsCountAcrossMajor = elementsCountAcrossMajor self._elementsCountAcrossShell = elementsCountAcrossShell self._elementsCountAcrossTransition = elementsCountAcrossTransition self._shellProportion = shellProportion self._majorRadius = vector.magnitude(majorAxis) self.px = None self.pd1 = None self.pd2 = None self.pd3 = None
def generateBase1DMesh(self, rx): """ Generate nodes around the perimeter of the ellipse. """ btx = self.px btd1 = self.pd1 btd2 = self.pd2 btd3 = self.pd3 ratio = rx / self.elementsCountAcrossShell if self.elementsCountAcrossShell > 0 else 0 majorAxis = [ d * (1 - ratio * (1 - self.coreMajorRadius / self.majorRadius)) for d in self.majorAxis ] minorAxis = [ d * (1 - ratio * (1 - self.coreMinorRadius / self.minorRadius)) for d in self.minorAxis ] majorRadius = vector.magnitude(majorAxis) nx, nd1 = createEllipsePerimeter(self.centre, majorAxis, minorAxis, self.elementsCountAround, majorRadius) nte = normalToEllipse(self.majorAxis, self.minorAxis) tbx, tbd1, tbd2, tbd3 = [], [], [], [] for n in range(self.elementsCountAround + 1): tbx.append(nx[n]) tbd1.append(nd1[n]) tbd2.append(nte) tbd3.append(vector.normalise(vector.crossproduct3(tbd1[n], nte))) for n in range(self.elementsCountAround + 1): n1, n2 = self.__shield.convertRimIndex(n, rx) btx[n2][n1] = tbx[n] btd1[n2][n1] = tbd1[n] btd2[n2][n1] = tbd2[n] btd3[n2][n1] = tbd3[n]
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(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
def warpSegmentPoints(xList, d1List, d2List, segmentAxis, sx, sd1, sd2, elementsCountAround, elementsCountAlongSegment, refPointZ, innerRadiusAlong, closedProximalEnd): """ Warps points in segment to account for bending and twisting along central path defined by nodes sx and derivatives sd1 and sd2. :param xList: coordinates of segment points. :param d1List: derivatives around axis of segment. :param d2List: derivatives along axis of segment. :param segmentAxis: axis perpendicular to segment plane. :param sx: coordinates of points on central path. :param sd1: derivatives of points along central path. :param sd2: derivatives representing cross axes. :param elementsCountAround: Number of elements around segment. :param elementsCountAlongSegment: Number of elements along segment. :param refPointZ: z-coordinate of reference point for each element groups along the segment to be used for transformation. :param innerRadiusAlong: radius of segment along length. :param closedProximalEnd: True if proximal end of segment is a closed end. :return coordinates and derivatives of warped points. """ xWarpedList = [] d1WarpedList = [] d2WarpedList = [] d2WarpedListFinal = [] d3WarpedUnitList = [] for nAlongSegment in range(elementsCountAlongSegment + 1): xElementAlongSegment = xList[elementsCountAround * nAlongSegment:elementsCountAround * (nAlongSegment + 1)] d1ElementAlongSegment = d1List[elementsCountAround * nAlongSegment:elementsCountAround * (nAlongSegment + 1)] d2ElementAlongSegment = d2List[elementsCountAround * nAlongSegment:elementsCountAround * (nAlongSegment + 1)] centroid = [0.0, 0.0, refPointZ[nAlongSegment]] # Rotate to align segment axis with tangent of central line unitTangent = vector.normalise(sd1[nAlongSegment]) cp = vector.crossproduct3(segmentAxis, unitTangent) dp = vector.dotproduct(segmentAxis, unitTangent) if vector.magnitude( cp) > 0.0: # path tangent not parallel to segment axis axisRot = vector.normalise(cp) thetaRot = math.acos(vector.dotproduct(segmentAxis, unitTangent)) rotFrame = matrix.getRotationMatrixFromAxisAngle(axisRot, thetaRot) centroidRot = [ rotFrame[j][0] * centroid[0] + rotFrame[j][1] * centroid[1] + rotFrame[j][2] * centroid[2] for j in range(3) ] else: # path tangent parallel to segment axis (z-axis) if dp == -1.0: # path tangent opposite direction to segment axis thetaRot = math.pi axisRot = [1.0, 0, 0] rotFrame = matrix.getRotationMatrixFromAxisAngle( axisRot, thetaRot) centroidRot = [ rotFrame[j][0] * centroid[0] + rotFrame[j][1] * centroid[1] + rotFrame[j][2] * centroid[2] for j in range(3) ] else: # segment axis in same direction as unit tangent rotFrame = [[1, 0, 0], [0, 1, 0], [0, 0, 1]] centroidRot = centroid translateMatrix = [ sx[nAlongSegment][j] - centroidRot[j] for j in range(3) ] for n1 in range(elementsCountAround): x = xElementAlongSegment[n1] d1 = d1ElementAlongSegment[n1] d2 = d2ElementAlongSegment[n1] if vector.magnitude( cp) > 0.0: # path tangent not parallel to segment axis xRot1 = [ rotFrame[j][0] * x[0] + rotFrame[j][1] * x[1] + rotFrame[j][2] * x[2] for j in range(3) ] d1Rot1 = [ rotFrame[j][0] * d1[0] + rotFrame[j][1] * d1[1] + rotFrame[j][2] * d1[2] for j in range(3) ] d2Rot1 = [ rotFrame[j][0] * d2[0] + rotFrame[j][1] * d2[1] + rotFrame[j][2] * d2[2] for j in range(3) ] # xTranslate = [xRot1[j] + translateMatrix[j] for j in range(3)] else: # path tangent parallel to segment axis xRot1 = [ rotFrame[j][0] * x[0] + rotFrame[j][1] * x[1] + rotFrame[j][2] * x[2] for j in range(3) ] if dp == -1.0 else x d1Rot1 = [ rotFrame[j][0] * d1[0] + rotFrame[j][1] * d1[1] + rotFrame[j][2] * d1[2] for j in range(3) ] if dp == -1.0 else d1 d2Rot1 = [ rotFrame[j][0] * d2[0] + rotFrame[j][1] * d2[1] + rotFrame[j][2] * d2[2] for j in range(3) ] if dp == -1.0 else d2 # xTranslate = [xRot1[j] + translateMatrix[j] for j in range(3)] if n1 == 0: # Find angle between xCentroidRot and first node in the face vectorToFirstNode = [ xRot1[c] - centroidRot[c] for c in range(3) ] if vector.magnitude(vectorToFirstNode) > 0.0: cp = vector.crossproduct3( vector.normalise(vectorToFirstNode), vector.normalise(sd2[nAlongSegment])) if vector.magnitude(cp) > 1e-7: cp = vector.normalise(cp) signThetaRot2 = vector.dotproduct(unitTangent, cp) thetaRot2 = math.acos( vector.dotproduct( vector.normalise(vectorToFirstNode), sd2[nAlongSegment])) axisRot2 = unitTangent rotFrame2 = matrix.getRotationMatrixFromAxisAngle( axisRot2, signThetaRot2 * thetaRot2) else: rotFrame2 = [[1, 0, 0], [0, 1, 0], [0, 0, 1]] else: rotFrame2 = [[1, 0, 0], [0, 1, 0], [0, 0, 1]] xRot2 = [ rotFrame2[j][0] * xRot1[0] + rotFrame2[j][1] * xRot1[1] + rotFrame2[j][2] * xRot1[2] for j in range(3) ] d1Rot2 = [ rotFrame2[j][0] * d1Rot1[0] + rotFrame2[j][1] * d1Rot1[1] + rotFrame2[j][2] * d1Rot1[2] for j in range(3) ] d2Rot2 = [ rotFrame2[j][0] * d2Rot1[0] + rotFrame2[j][1] * d2Rot1[1] + rotFrame2[j][2] * d2Rot1[2] for j in range(3) ] xTranslate = [xRot2[j] + translateMatrix[j] for j in range(3)] xWarpedList.append(xTranslate) d1WarpedList.append(d1Rot2) d2WarpedList.append(d2Rot2) # Scale d2 with curvature of central path d2WarpedListScaled = [] vProjectedList = [] for nAlongSegment in range(elementsCountAlongSegment + 1): for n1 in range(elementsCountAround): n = nAlongSegment * elementsCountAround + n1 # Calculate norm sd1Normalised = vector.normalise(sd1[nAlongSegment]) v = [xWarpedList[n][c] - sx[nAlongSegment][c] for c in range(3)] dp = vector.dotproduct(v, sd1Normalised) dpScaled = [dp * c for c in sd1Normalised] vProjected = [v[c] - dpScaled[c] for c in range(3)] vProjectedList.append(vProjected) if vector.magnitude(vProjected) > 0.0: vProjectedNormlised = vector.normalise(vProjected) else: vProjectedNormlised = [0.0, 0.0, 0.0] # Calculate curvature along at each node if nAlongSegment == 0: curvature = interp.getCubicHermiteCurvature( sx[0], sd1[0], sx[1], sd1[1], vProjectedNormlised, 0.0) elif nAlongSegment == elementsCountAlongSegment: curvature = interp.getCubicHermiteCurvature( sx[-2], sd1[-2], sx[-1], sd1[-1], vProjectedNormlised, 1.0) else: curvature = 0.5 * (interp.getCubicHermiteCurvature( sx[nAlongSegment - 1], sd1[nAlongSegment - 1], sx[nAlongSegment], sd1[nAlongSegment], vProjectedNormlised, 1.0) + interp.getCubicHermiteCurvature( sx[nAlongSegment], sd1[nAlongSegment], sx[nAlongSegment + 1], sd1[nAlongSegment + 1], vProjectedNormlised, 0.0)) # Scale factor = 1.0 - curvature * innerRadiusAlong[nAlongSegment] d2 = [factor * c for c in d2WarpedList[n]] d2WarpedListScaled.append(d2) # Smooth d2 for segment smoothd2Raw = [] for n1 in range(elementsCountAround): nx = [] nd2 = [] for n2 in range(elementsCountAlongSegment + 1): n = n2 * elementsCountAround + n1 nx.append(xWarpedList[n]) nd2.append(d2WarpedListScaled[n]) smoothd2 = interp.smoothCubicHermiteDerivativesLine( nx, nd2, fixStartDerivative=True, fixEndDerivative=True) smoothd2Raw.append(smoothd2) # Re-arrange smoothd2 for n2 in range(elementsCountAlongSegment + 1): for n1 in range(elementsCountAround): d2WarpedListFinal.append(smoothd2Raw[n1][n2]) # Calculate unit d3 for n in range(len(xWarpedList)): d3Unit = vector.normalise( vector.crossproduct3(vector.normalise(d1WarpedList[n]), vector.normalise(d2WarpedListFinal[n]))) d3WarpedUnitList.append(d3Unit) return xWarpedList, d1WarpedList, d2WarpedListFinal, d3WarpedUnitList
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 createAnnulusMesh3d(nodes, mesh, nextNodeIdentifier, nextElementIdentifier, startPointsx, startPointsd1, startPointsd2, startPointsd3, startNodeId, startDerivativesMap, endPointsx, endPointsd1, endPointsd2, endPointsd3, endNodeId, endDerivativesMap, forceStartLinearXi3 = False, forceMidLinearXi3 = False, forceEndLinearXi3 = False, maxStartThickness = None, maxEndThickness = None, useCrossDerivatives = False, elementsCountRadial = 1, meshGroups = []): ''' Create an annulus mesh from a loop of start points/nodes with specified derivative mappings to a loop of end points/nodes with specified derivative mappings. Derivative d3 is through the wall. Currently limited to single element layer through wall. Points/nodes order cycles fastest around the annulus, then through the wall. Note doesn't support cross derivatives. Arrays are indexed by n3 (node through wall, size 2), n2 (node along/radial), n1 (node around, variable size) and coordinate component c. :param nodes: The nodeset to create nodes in. :param mesh: The mesh to create elements in. :param nextNodeIdentifier, nextElementIdentifier: Next identifiers to use and increment. :param startPointsx, startPointsd1, startPointsd2, startPointsd3, endPointsx, endPointsd1, endPointsd2, endPointsd3: List array[n3][n1][c] or start/point coordinates and derivatives. To linearise through the wall, pass None to d3. If both ends are linear through the wall, interior points are linear through the wall. :param startNodeId, endNodeId: List array [n3][n1] of existing node identifiers to use at start/end. Pass None for argument if no nodes are specified at end. These arguments are 'all or nothing'. :param startDerivativesMap, endDerivativesMap: List array[n3][n1] of mappings for d/dxi1, d/dxi2, d/dxi3 at start/end of form: ( (1, -1, 0), (1, 0, 0), None ) where the first tuple means d/dxi1 = d/ds1 - d/ds2. Only 0, 1 and -1 may be used. None means use default e.g. d/dxi2 = d/ds2. Pass None for the entire argument to use the defaults d/dxi1 = d/ds1, d/dxi2 = d/ds2, d/dxi3 = d/ds3. Pass a 4th mapping to apply to d/dxi1 on other side of node; if not supplied first mapping applies both sides. :param nodetemplate: Full tricubic Hermite node template, can omit cross derivatives. :param forceStartLinearXi3, forceMidLinearXi3, forceEndLinearXi3: Force start, middle or end elements to be linear through the wall, even if d3 is supplied at either end. Can only use forceMidLinearXi3 only if at least one end is linear in d3. :param maxStartThickness, maxEndThickness: Optional maximum override on start/end thicknesses. :param useCrossDerivatives: May only be True if no derivatives maps are in use. :param elementsCountRadial: Optional number of elements in radial direction between start and end. :param meshGroups: Optional list of Zinc MeshGroup for adding new elements to. :return: Final values of nextNodeIdentifier, nextElementIdentifier ''' assert (elementsCountRadial >= 1), 'createAnnulusMesh3d: Invalid number of radial elements' startLinearXi3 = (not startPointsd3) or forceStartLinearXi3 endLinearXi3 = (not endPointsd3) or forceEndLinearXi3 midLinearXi3 = (startLinearXi3 and endLinearXi3) or ((startLinearXi3 or endLinearXi3) and forceMidLinearXi3) # get list whether each row of nodes in elements is linear in Xi3 # this is for element use; start/end nodes may have d3 even if element is linear rowLinearXi3 = [ startLinearXi3 ] + [ midLinearXi3 ]*(elementsCountRadial - 1) + [ endLinearXi3 ] assert (not useCrossDerivatives) or ((not startDerivativesMap) and (not endDerivativesMap)), \ 'createAnnulusMesh3d: Cannot use cross derivatives with derivatives map' elementsCountWall = 1 nodesCountWall = elementsCountWall + 1 assert (len(startPointsx) == nodesCountWall) and (len(startPointsd1) == nodesCountWall) and (len(startPointsd2) == nodesCountWall) and \ (startLinearXi3 or (len(startPointsd3) == nodesCountWall)) and \ (len(endPointsx) == nodesCountWall) and (len(endPointsd1) == nodesCountWall) and (len(endPointsd2) == nodesCountWall) and \ (endLinearXi3 or (len(endPointsd3) == nodesCountWall)) and \ ((startNodeId is None) or (len(startNodeId) == nodesCountWall)) and \ ((endNodeId is None) or (len(endNodeId) == nodesCountWall)) and \ ((startDerivativesMap is None) or (len(startDerivativesMap) == nodesCountWall)) and \ ((endDerivativesMap is None) or (len(endDerivativesMap) == nodesCountWall)), \ 'createAnnulusMesh3d: Mismatch in number of layers through wall' elementsCountAround = nodesCountAround = len(startPointsx[0]) assert (nodesCountAround > 1), 'createAnnulusMesh3d: Invalid number of points/nodes around annulus' for n3 in range(nodesCountWall): assert (len(startPointsx[n3]) == nodesCountAround) and (len(startPointsd1[n3]) == nodesCountAround) and (len(startPointsd2[n3]) == nodesCountAround) and \ (startLinearXi3 or (len(startPointsd3[n3]) == nodesCountAround)) and \ (len(endPointsx[n3]) == nodesCountAround) and (len(endPointsd1[n3]) == nodesCountAround) and (len(endPointsd2[n3]) == nodesCountAround) and \ (endLinearXi3 or (len(endPointsd3[n3]) == nodesCountAround)) and \ ((startNodeId is None) or (len(startNodeId[n3]) == nodesCountAround)) and \ ((endNodeId is None) or (len(endNodeId[n3]) == nodesCountAround)) and \ ((startDerivativesMap is None) or (len(startDerivativesMap[n3]) == nodesCountAround)) and \ ((endDerivativesMap is None) or (len(endDerivativesMap[n3]) == nodesCountAround)), \ 'createAnnulusMesh3d: Mismatch in number of points/nodes in layers through wall' fm = mesh.getFieldmodule() fm.beginChange() cache = fm.createFieldcache() coordinates = zinc_utils.getOrCreateCoordinateField(fm) # Build arrays of points from start to end px = [ [], [] ] pd1 = [ [], [] ] pd2 = [ [], [] ] pd3 = [ [], [] ] for n3 in range(2): px [n3] = [ startPointsx [n3], endPointsx [n3] ] pd1[n3] = [ startPointsd1[n3], endPointsd1[n3] ] pd2[n3] = [ startPointsd2[n3], endPointsd2[n3] ] pd3[n3] = [ startPointsd3[n3] if (startPointsd3 is not None) else None, \ endPointsd3[n3] if (endPointsd3 is not None) else None ] if elementsCountRadial > 1: # add in-between points startPointsd = [ startPointsd1, startPointsd2, startPointsd3 ] startPointsdslimit = 2 if (startPointsd3 is None) else 3 endPointsd = [ endPointsd1, endPointsd2, endPointsd3 ] endPointsdslimit = 2 if (endPointsd3 is None) else 3 for n3 in range(2): for n2 in range(1, elementsCountRadial): px [n3].insert(n2, [ None ]*nodesCountAround) pd1[n3].insert(n2, [ None ]*nodesCountAround) pd2[n3].insert(n2, [ None ]*nodesCountAround) pd3[n3].insert(n2, None if midLinearXi3 else [ None ]*nodesCountAround) # compute on outside / n3 = 1, then map to inside using thickness thicknesses = [] thicknesses.append([ vector.magnitude([ (startPointsx[1][n1][c] - startPointsx[0][n1][c]) for c in range(3) ]) for n1 in range(nodesCountAround) ]) if maxStartThickness: for n1 in range(nodesCountAround): thicknesses[0][n1] = min(thicknesses[0][n1], maxStartThickness) for n2 in range(1, elementsCountRadial): thicknesses.append([ None ]*nodesCountAround) thicknesses.append([ vector.magnitude([ (endPointsx[1][n1][c] - endPointsx[0][n1][c]) for c in range(3) ]) for n1 in range(nodesCountAround) ]) if maxEndThickness: for n1 in range(nodesCountAround): thicknesses[-1][n1] = min(thicknesses[-1][n1], maxEndThickness) n3 == 1 for n1 in range(nodesCountAround): ax = startPointsx [n3][n1] if (startDerivativesMap is None) or (startDerivativesMap[n3][n1][0] is None): ad1 = startPointsd1[n3][n1] else: derivativesMap = startDerivativesMap[n3][n1][0] ad1 = [ 0.0, 0.0, 0.0 ] for ds in range(startPointsdslimit): if derivativesMap[ds] != 0.0: for c in range(3): ad1[c] += derivativesMap[ds]*startPointsd[ds][n3][n1][c] if len(startDerivativesMap[n3][n1]) > 3: # average with d1 map for other side derivativesMap = startDerivativesMap[n3][n1][3] ad1 = [ 0.5*d for d in ad1 ] if not derivativesMap: for c in range(3): ad1[c] += 0.5*startPointsd[0][n3][n1][c] else: for ds in range(startPointsdslimit): if derivativesMap[ds] != 0.0: for c in range(3): ad1[c] += 0.5*derivativesMap[ds]*startPointsd[ds][n3][n1][c] if (startDerivativesMap is None) or (startDerivativesMap[n3][n1][1] is None): ad2 = startPointsd2[n3][n1] else: derivativesMap = startDerivativesMap[n3][n1][1] ad2 = [ 0.0, 0.0, 0.0 ] for ds in range(startPointsdslimit): if derivativesMap[ds] != 0.0: for c in range(3): ad2[c] += derivativesMap[ds]*startPointsd[ds][n3][n1][c] bx = endPointsx [n3][n1] if (endDerivativesMap is None) or (endDerivativesMap[n3][n1][0] is None): bd1 = endPointsd1[n3][n1] else: derivativesMap = endDerivativesMap[n3][n1][0] bd1 = [ 0.0, 0.0, 0.0 ] for ds in range(endPointsdslimit): if derivativesMap[ds] != 0.0: for c in range(3): bd1[c] += derivativesMap[ds]*endPointsd[ds][n3][n1][c] if len(endDerivativesMap[n3][n1]) > 3: # average with d1 map for other side derivativesMap = endDerivativesMap[n3][n1][3] bd1 = [ 0.5*d for d in bd1 ] if not derivativesMap: for c in range(3): bd1[c] += 0.5*endPointsd[0][n3][n1][c] else: for ds in range(endPointsdslimit): if derivativesMap[ds] != 0.0: for c in range(3): bd1[c] += 0.5*derivativesMap[ds]*endPointsd[ds][n3][n1][c] if (endDerivativesMap is None) or (endDerivativesMap[n3][n1][1] is None): bd2 = endPointsd2[n3][n1] else: derivativesMap = endDerivativesMap[n3][n1][1] bd2 = [ 0.0, 0.0, 0.0 ] for ds in range(endPointsdslimit): if derivativesMap[ds] != 0.0: for c in range(3): bd2[c] += derivativesMap[ds]*endPointsd[ds][n3][n1][c] # scaling end derivatives to arc length gives even curvature along the curve arcLength = interp.computeCubicHermiteArcLength(ax, ad2, bx, bd2, rescaleDerivatives = False) scaledDerivatives = [ vector.setMagnitude(d2, arcLength) for d2 in [ ad2, bd2 ]] mx, md2, me, mxi = interp.sampleCubicHermiteCurvesSmooth([ ax, bx ], scaledDerivatives, elementsCountRadial, derivativeMagnitudeStart = vector.magnitude(ad2), derivativeMagnitudeEnd = vector.magnitude(bd2))[0:4] md1 = interp.interpolateSampleLinear([ ad1, bd1 ], me, mxi) thi = interp.interpolateSampleLinear([ thicknesses[0][n1], thicknesses[-1][n1] ], me, mxi) #md2 = interp.smoothCubicHermiteDerivativesLine(mx, md2, fixStartDerivative = True, fixEndDerivative = True) for n2 in range(1, elementsCountRadial): px [n3][n2][n1] = mx [n2] pd1[n3][n2][n1] = md1[n2] pd2[n3][n2][n1] = md2[n2] thicknesses[n2][n1] = thi[n2] # now get inner positions from normal and thickness, derivatives from curvature for n2 in range(1, elementsCountRadial): # first smooth derivative 1 around outer loop pd1[1][n2] = interp.smoothCubicHermiteDerivativesLoop(px[1][n2], pd1[1][n2], magnitudeScalingMode = interp.DerivativeScalingMode.HARMONIC_MEAN) for n1 in range(nodesCountAround): normal = vector.normalise(vector.crossproduct3(pd1[1][n2][n1], pd2[1][n2][n1])) thickness = thicknesses[n2][n1] d3 = [ d*thickness for d in normal ] px [0][n2][n1] = [ (px [1][n2][n1][c] - d3[c]) for c in range(3) ] # calculate inner d1 from curvature around n1m = n1 - 1 n1p = (n1 + 1)%nodesCountAround curvature = 0.5*( interp.getCubicHermiteCurvature(px[1][n2][n1m], pd1[1][n2][n1m], px[1][n2][n1 ], pd1[1][n2][n1 ], normal, 1.0) + interp.getCubicHermiteCurvature(px[1][n2][n1 ], pd1[1][n2][n1 ], px[1][n2][n1p], pd1[1][n2][n1p], normal, 0.0)) factor = 1.0 + curvature*thickness pd1[0][n2][n1] = [ factor*d for d in pd1[1][n2][n1] ] # calculate inner d2 from curvature radially n2m = n2 - 1 n2p = n2 + 1 curvature = 0.5*( interp.getCubicHermiteCurvature(px[1][n2m][n1], pd2[1][n2m][n1], px[1][n2 ][n1], pd2[1][n2 ][n1], normal, 1.0) + interp.getCubicHermiteCurvature(px[1][n2 ][n1], pd2[1][n2 ][n1], px[1][n2p][n1], pd2[1][n2p][n1], normal, 0.0)) factor = 1.0 + curvature*thickness pd2[0][n2][n1] = [ factor*d for d in pd2[1][n2][n1] ] if not midLinearXi3: pd3[0][n2][n1] = pd3[1][n2][n1] = d3 # smooth derivative 1 around inner loop pd1[0][n2] = interp.smoothCubicHermiteDerivativesLoop(px[0][n2], pd1[0][n2], magnitudeScalingMode = interp.DerivativeScalingMode.HARMONIC_MEAN) for n3 in range(0, 1): # was (0, nodesCountWall) # smooth derivative 2 radially/along annulus for n1 in range(nodesCountAround): sd2 = interp.smoothCubicHermiteDerivativesLine( [ px [n3][n2][n1] for n2 in range(elementsCountRadial + 1) ], [ pd2[n3][n2][n1] for n2 in range(elementsCountRadial + 1) ], fixAllDirections = True, fixStartDerivative = True, fixEndDerivative = True, magnitudeScalingMode = interp.DerivativeScalingMode.HARMONIC_MEAN) for n2 in range(elementsCountRadial + 1): pd2[n3][n2][n1] = sd2[n2] ############## # Create nodes ############## nodetemplate = nodes.createNodetemplate() nodetemplate.defineField(coordinates) nodetemplate.setValueNumberOfVersions(coordinates, -1, Node.VALUE_LABEL_VALUE, 1) nodetemplate.setValueNumberOfVersions(coordinates, -1, Node.VALUE_LABEL_D_DS1, 1) nodetemplate.setValueNumberOfVersions(coordinates, -1, Node.VALUE_LABEL_D_DS2, 1) if useCrossDerivatives: nodetemplate.setValueNumberOfVersions(coordinates, -1, Node.VALUE_LABEL_D2_DS1DS2, 1) nodetemplate.setValueNumberOfVersions(coordinates, -1, Node.VALUE_LABEL_D_DS3, 1) if useCrossDerivatives: nodetemplate.setValueNumberOfVersions(coordinates, -1, Node.VALUE_LABEL_D2_DS1DS3, 1) nodetemplate.setValueNumberOfVersions(coordinates, -1, Node.VALUE_LABEL_D2_DS2DS3, 1) nodetemplate.setValueNumberOfVersions(coordinates, -1, Node.VALUE_LABEL_D3_DS1DS2DS3, 1) 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) if useCrossDerivatives: nodetemplateLinearS3.setValueNumberOfVersions(coordinates, -1, Node.VALUE_LABEL_D2_DS1DS2, 1) nodeIdentifier = nextNodeIdentifier nodeId = [ [], [] ] for n3 in range(2): for n2 in range(elementsCountRadial + 1): if (n2 == 0) and (startNodeId is not None): rowNodeId = copy.deepcopy(startNodeId[n3]) elif (n2 == elementsCountRadial) and (endNodeId is not None): rowNodeId = copy.deepcopy(endNodeId[n3]) else: rowNodeId = [] nodetemplate1 = nodetemplate if pd3[n3][n2] else nodetemplateLinearS3 for n1 in range(nodesCountAround): node = nodes.createNode(nodeIdentifier, nodetemplate1) rowNodeId.append(nodeIdentifier) cache.setNode(node) coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_VALUE, 1, px[n3][n2][n1]) coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS1, 1, pd1[n3][n2][n1]) coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS2, 1, pd2[n3][n2][n1]) if pd3[n3][n2]: coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS3, 1, pd3[n3][n2][n1]) nodeIdentifier = nodeIdentifier + 1 nodeId[n3].append(rowNodeId) ################# # Create elements ################# tricubichermite = eftfactory_tricubichermite(mesh, useCrossDerivatives) bicubichermitelinear = eftfactory_bicubichermitelinear(mesh, useCrossDerivatives) elementIdentifier = nextElementIdentifier elementtemplateStandard = mesh.createElementtemplate() elementtemplateStandard.setElementShapeType(Element.SHAPE_TYPE_CUBE) elementtemplateX = mesh.createElementtemplate() elementtemplateX.setElementShapeType(Element.SHAPE_TYPE_CUBE) for e2 in range(elementsCountRadial): nonlinearXi3 = (not rowLinearXi3[e2]) or (not rowLinearXi3[e2 + 1]) eftFactory = tricubichermite if nonlinearXi3 else bicubichermitelinear eftStandard = eftFactory.createEftBasic() elementtemplateStandard.defineField(coordinates, -1, eftStandard) mapStartDerivatives = (e2 == 0) and (startDerivativesMap is not None) mapStartLinearDerivativeXi3 = nonlinearXi3 and rowLinearXi3[e2] mapEndDerivatives = (e2 == (elementsCountRadial - 1)) and (endDerivativesMap is not None) mapEndLinearDerivativeXi3 = nonlinearXi3 and rowLinearXi3[e2 + 1] mapDerivatives = mapStartDerivatives or mapStartLinearDerivativeXi3 or mapEndDerivatives or mapEndLinearDerivativeXi3 for e1 in range(elementsCountAround): en = (e1 + 1)%elementsCountAround nids = [ nodeId[0][e2][e1], nodeId[0][e2][en], nodeId[0][e2 + 1][e1], nodeId[0][e2 + 1][en], nodeId[1][e2][e1], nodeId[1][e2][en], nodeId[1][e2 + 1][e1], nodeId[1][e2 + 1][en] ] if mapDerivatives: eft1 = eftFactory.createEftNoCrossDerivatives() setEftScaleFactorIds(eft1, [1], []) if mapStartLinearDerivativeXi3: eftFactory.setEftLinearDerivative(eft1, [ 1, 5 ], Node.VALUE_LABEL_D_DS3, 1, 5, 1) eftFactory.setEftLinearDerivative(eft1, [ 2, 6 ], Node.VALUE_LABEL_D_DS3, 2, 6, 1) if mapStartDerivatives: for i in range(2): lns = [ 1, 5 ] if (i == 0) else [ 2, 6 ] for n3 in range(2): derivativesMap = startDerivativesMap[n3][e1] if (i == 0) else startDerivativesMap[n3][en] # handle different d1 on each side of node d1Map = derivativesMap[0] if ((i == 1) or (len(derivativesMap) < 4)) else derivativesMap[3] d2Map = derivativesMap[1] d3Map = derivativesMap[2] # use temporary to safely swap DS1 and DS2: ln = [ lns[n3] ] if d1Map is not None: remapEftNodeValueLabel(eft1, ln, Node.VALUE_LABEL_D_DS1, [ ( Node.VALUE_LABEL_D2_DS1DS2, [] ) ]) if d3Map is not None: remapEftNodeValueLabel(eft1, ln, Node.VALUE_LABEL_D_DS3, [ ( Node.VALUE_LABEL_D2_DS2DS3, [] ) ]) if d2Map is not None: remapEftNodeValueLabel(eft1, ln, Node.VALUE_LABEL_D_DS2, \ derivativeSignsToExpressionTerms( ( Node.VALUE_LABEL_D_DS1, Node.VALUE_LABEL_D_DS2, Node.VALUE_LABEL_D_DS3 ), d2Map)) if d1Map is not None: remapEftNodeValueLabel(eft1, ln, Node.VALUE_LABEL_D2_DS1DS2, \ derivativeSignsToExpressionTerms( ( Node.VALUE_LABEL_D_DS1, Node.VALUE_LABEL_D_DS2, Node.VALUE_LABEL_D_DS3 ), d1Map)) if d3Map is not None: remapEftNodeValueLabel(eft1, ln, Node.VALUE_LABEL_D2_DS2DS3, \ derivativeSignsToExpressionTerms( ( Node.VALUE_LABEL_D_DS1, Node.VALUE_LABEL_D_DS2, Node.VALUE_LABEL_D_DS3 ), d3Map)) if mapEndLinearDerivativeXi3: eftFactory.setEftLinearDerivative(eft1, [ 3, 7 ], Node.VALUE_LABEL_D_DS3, 3, 7, 1) eftFactory.setEftLinearDerivative(eft1, [ 4, 8 ], Node.VALUE_LABEL_D_DS3, 4, 8, 1) if mapEndDerivatives: for i in range(2): lns = [ 3, 7 ] if (i == 0) else [ 4, 8 ] for n3 in range(2): derivativesMap = endDerivativesMap[n3][e1] if (i == 0) else endDerivativesMap[n3][en] # handle different d1 on each side of node d1Map = derivativesMap[0] if ((i == 1) or (len(derivativesMap) < 4)) else derivativesMap[3] d2Map = derivativesMap[1] d3Map = derivativesMap[2] # use temporary to safely swap DS1 and DS2: ln = [ lns[n3] ] if d1Map is not None: remapEftNodeValueLabel(eft1, ln, Node.VALUE_LABEL_D_DS1, [ ( Node.VALUE_LABEL_D2_DS1DS2, [] ) ]) if d3Map is not None: remapEftNodeValueLabel(eft1, ln, Node.VALUE_LABEL_D_DS3, [ ( Node.VALUE_LABEL_D2_DS2DS3, [] ) ]) if d2Map is not None: remapEftNodeValueLabel(eft1, ln, Node.VALUE_LABEL_D_DS2, \ derivativeSignsToExpressionTerms( ( Node.VALUE_LABEL_D_DS1, Node.VALUE_LABEL_D_DS2, Node.VALUE_LABEL_D_DS3 ), d2Map)) if d1Map is not None: remapEftNodeValueLabel(eft1, ln, Node.VALUE_LABEL_D2_DS1DS2, \ derivativeSignsToExpressionTerms( ( Node.VALUE_LABEL_D_DS1, Node.VALUE_LABEL_D_DS2, Node.VALUE_LABEL_D_DS3 ), d1Map)) if d3Map is not None: remapEftNodeValueLabel(eft1, ln, Node.VALUE_LABEL_D2_DS2DS3, \ derivativeSignsToExpressionTerms( ( Node.VALUE_LABEL_D_DS1, Node.VALUE_LABEL_D_DS2, Node.VALUE_LABEL_D_DS3 ), d3Map)) elementtemplateX.defineField(coordinates, -1, eft1) elementtemplate1 = elementtemplateX else: eft1 = eftStandard elementtemplate1 = elementtemplateStandard element = mesh.createElement(elementIdentifier, elementtemplate1) result2 = element.setNodesByIdentifier(eft1, nids) if mapDerivatives: result3 = element.setScaleFactors(eft1, [ -1.0 ]) #else: # result3 = '-' #print('create element annulus', element.isValid(), elementIdentifier, result2, result3, nids) elementIdentifier += 1 for meshGroup in meshGroups: meshGroup.addElement(element) fm.endChange() return nodeIdentifier, elementIdentifier
def replaceElementWithInlet4(self, element, startElementId, nodetemplate, startNodeId, tubeLength, innerDiameter, wallThickness): ''' Replace element with 4 element X-layout tube inlet. Inlet axis is at given length from centre of xi3=0 face, oriented with dx/dxi1. 8 new nodes are created. ''' fm = self._mesh.getFieldmodule() nodes = fm.findNodesetByFieldDomainType(Field.DOMAIN_TYPE_NODES) fm.beginChange() cache = fm.createFieldcache() diff1 = self._mesh.getChartDifferentialoperator(1, 1) diff2 = self._mesh.getChartDifferentialoperator(1, 2) coordinates = getOrCreateCoordinateField(fm) cache.setMeshLocation(element, [0.5, 0.5, 1.0]) result, fc = coordinates.evaluateReal(cache, 3) resulta, a = coordinates.evaluateDerivative(diff1, cache, 3) resultb, b = coordinates.evaluateDerivative(diff2, cache, 3) n = vector.normalise(vector.crossproduct3(a, b)) #print(resulta, 'a =', a, ',', resultb, ' b=', b, ' fc=', fc, ' n=',n) ic = [ (fc[i] + tubeLength*n[i]) for i in range(3) ] na = vector.normalise(a) nb = vector.normalise(b) a = vector.normalise([ -(na[i] + nb[i]) for i in range(3) ]) b = vector.normalise(vector.crossproduct3(a, n)) zero = [ 0.0, 0.0, 0.0 ] nodeIdentifier = startNodeId elementsCountAround = 4 radiansPerElementAround = math.pi*2.0/elementsCountAround for n3 in range(2): radius = innerDiameter*0.5 + n3*wallThickness for n1 in range(elementsCountAround): radiansAround = n1*radiansPerElementAround cosRadiansAround = math.cos(radiansAround) sinRadiansAround = math.sin(radiansAround) x = [ (ic[i] + radius*(cosRadiansAround*a[i] + sinRadiansAround*b[i])) for i in range(3) ] dx_ds1 = [ radiansPerElementAround*radius*(-sinRadiansAround*a[i] + cosRadiansAround*b[i]) for i in range(3) ] dx_ds2 = [ -tubeLength*c for c in n ] dx_ds3 = [ wallThickness*(cosRadiansAround*a[i] + sinRadiansAround*b[i]) for i in range(3) ] node = nodes.createNode(nodeIdentifier, nodetemplate) cache.setNode(node) coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_VALUE, 1, x) coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS1, 1, dx_ds1) coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS2, 1, dx_ds2) coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS3, 1, dx_ds3) if self._useCrossDerivatives: coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D2_DS1DS2, 1, zero) coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D2_DS1DS3, 1, zero) coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D2_DS2DS3, 1, zero) coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D3_DS1DS2DS3, 1, zero) nodeIdentifier = nodeIdentifier + 1 eft0 = element.getElementfieldtemplate(coordinates, -1) nids0 = getElementNodeIdentifiers(element, eft0) orig_nids = [ nids0[0], nids0[2], nids0[3], nids0[1], nids0[4], nids0[6], nids0[7], nids0[5] ] elementIdentifier = startElementId elementtemplate1 = self._mesh.createElementtemplate() elementtemplate1.setElementShapeType(Element.SHAPE_TYPE_CUBE) for e in range(4): eft1 = self.createEftNoCrossDerivatives() setEftScaleFactorIds(eft1, [1], []) if e == 0: remapEftNodeValueLabel(eft1, [ 3, 7 ], Node.VALUE_LABEL_D_DS2, [ (Node.VALUE_LABEL_D_DS1, [1]), (Node.VALUE_LABEL_D_DS2, [1]) ]) remapEftNodeValueLabel(eft1, [ 3, 7 ], Node.VALUE_LABEL_D_DS1, [ (Node.VALUE_LABEL_D_DS2, []) ]) remapEftNodeValueLabel(eft1, [ 4, 8 ], Node.VALUE_LABEL_D_DS2, [ (Node.VALUE_LABEL_D_DS1, [1]), (Node.VALUE_LABEL_D_DS2, []) ]) remapEftNodeValueLabel(eft1, [ 4, 8 ], Node.VALUE_LABEL_D_DS1, [ (Node.VALUE_LABEL_D_DS2, []) ]) elif e == 1: remapEftNodeValueLabel(eft1, [ 3, 7 ], Node.VALUE_LABEL_D_DS2, [ (Node.VALUE_LABEL_D_DS1, [1]), (Node.VALUE_LABEL_D_DS2, []) ]) remapEftNodeValueLabel(eft1, [ 4, 8 ], Node.VALUE_LABEL_D_DS2, [ (Node.VALUE_LABEL_D_DS1, []), (Node.VALUE_LABEL_D_DS2, []) ]) elif e == 2: remapEftNodeValueLabel(eft1, [ 3, 7 ], Node.VALUE_LABEL_D_DS2, [ (Node.VALUE_LABEL_D_DS1, []), (Node.VALUE_LABEL_D_DS2, []) ]) remapEftNodeValueLabel(eft1, [ 3, 7 ], Node.VALUE_LABEL_D_DS1, [ (Node.VALUE_LABEL_D_DS2, [1]) ]) remapEftNodeValueLabel(eft1, [ 4, 8 ], Node.VALUE_LABEL_D_DS2, [ (Node.VALUE_LABEL_D_DS1, []), (Node.VALUE_LABEL_D_DS2, [1]) ]) remapEftNodeValueLabel(eft1, [ 4, 8 ], Node.VALUE_LABEL_D_DS1, [ (Node.VALUE_LABEL_D_DS2, [1]) ]) elif e == 3: remapEftNodeValueLabel(eft1, [ 3, 7 ], Node.VALUE_LABEL_D_DS2, [ (Node.VALUE_LABEL_D_DS1, []), (Node.VALUE_LABEL_D_DS2, [1]) ]) remapEftNodeValueLabel(eft1, [ 3, 7 ], Node.VALUE_LABEL_D_DS1, [ (Node.VALUE_LABEL_D_DS1, [1]) ]) remapEftNodeValueLabel(eft1, [ 4, 8 ], Node.VALUE_LABEL_D_DS2, [ (Node.VALUE_LABEL_D_DS1, [1]), (Node.VALUE_LABEL_D_DS2, [1]) ]) remapEftNodeValueLabel(eft1, [ 4, 8 ], Node.VALUE_LABEL_D_DS1, [ (Node.VALUE_LABEL_D_DS1, [1]) ]) ea = e eb = (e + 1) % 4 ec = ea + 4 ed = eb + 4 nids = [ startNodeId + ea, startNodeId + eb, orig_nids[ea], orig_nids[eb], startNodeId + ec, startNodeId + ed, orig_nids[ec], orig_nids[ed] ] elementtemplate1.defineField(coordinates, -1, eft1) element = self._mesh.createElement(elementIdentifier, elementtemplate1) result2 = element.setNodesByIdentifier(eft1, nids) if eft1.getNumberOfLocalScaleFactors() == 1: result3 = element.setScaleFactors(eft1, [ -1.0 ]) else: result3 = 7 #print('create element in', element.isValid(), elementIdentifier, result2, result3, nids) elementIdentifier += 1 fm.endChange()
def generateBaseMesh(cls, region, options): ''' Generate the base bicubic Hermite mesh. :param region: Zinc region to define model in. Must be empty. :param options: Dict containing options. See getDefaultOptions(). :return: list of AnnotationGroup ''' elementsCountUpNeck = options['Number of elements up neck'] elementsCountUpBody = options['Number of elements up body'] elementsCountAround = options['Number of elements around'] height = options['Height'] majorDiameter = options['Major diameter'] minorDiameter = options['Minor diameter'] radius = 0.5 * options['Urethra diameter'] bladderWallThickness = options['Bladder wall thickness'] useCrossDerivatives = options['Use cross derivatives'] elementsCountAroundOstium = options['Number of elements around ostium'] elementsCountAnnulusRadially = options['Number of elements radially on annulus'] ostiumPositionAround = options['Ostium position around'] ostiumPositionUp = options['Ostium position up'] ostiumOptions = options['Ureter'] ostiumDefaultOptions = ostiumOptions.getScaffoldSettings() fm = region.getFieldmodule() fm.beginChange() coordinates = findOrCreateFieldCoordinates(fm) cache = fm.createFieldcache() mesh = fm.findMeshByDimension(3) nodes = fm.findNodesetByFieldDomainType(Field.DOMAIN_TYPE_NODES) nodetemplateApex = nodes.createNodetemplate() nodetemplateApex.defineField(coordinates) nodetemplateApex.setValueNumberOfVersions(coordinates, -1, Node.VALUE_LABEL_VALUE, 1) nodetemplateApex.setValueNumberOfVersions(coordinates, -1, Node.VALUE_LABEL_D_DS1, 1) nodetemplateApex.setValueNumberOfVersions(coordinates, -1, Node.VALUE_LABEL_D_DS2, 1) if useCrossDerivatives: nodetemplate = nodes.createNodetemplate() nodetemplate.defineField(coordinates) nodetemplate.setValueNumberOfVersions(coordinates, -1, Node.VALUE_LABEL_VALUE, 1) nodetemplate.setValueNumberOfVersions(coordinates, -1, Node.VALUE_LABEL_D_DS1, 1) nodetemplate.setValueNumberOfVersions(coordinates, -1, Node.VALUE_LABEL_D_DS2, 1) nodetemplate.setValueNumberOfVersions(coordinates, -1, Node.VALUE_LABEL_D2_DS1DS2, 1) else: nodetemplate = nodetemplateApex eftfactory = eftfactory_bicubichermitelinear(mesh, useCrossDerivatives) eft = eftfactory.createEftBasic() elementtemplate = mesh.createElementtemplate() elementtemplate.setElementShapeType(Element.SHAPE_TYPE_CUBE) elementtemplate.defineField(coordinates, -1, eft) neckGroup = AnnotationGroup(region, get_bladder_term("neck of urinary bladder")) bodyGroup = AnnotationGroup(region, get_bladder_term("dome of the bladder")) urinaryBladderGroup = AnnotationGroup(region, get_bladder_term("urinary bladder")) annotationGroups = [neckGroup, bodyGroup, urinaryBladderGroup] neckMeshGroup = neckGroup.getMeshGroup(mesh) bodyMeshGroup = bodyGroup.getMeshGroup(mesh) urinaryBladderMeshGroup = urinaryBladderGroup.getMeshGroup(mesh) # create nodes # create neck of the bladder nodeIdentifier = 1 radiansPerElementAround = 2.0*math.pi/elementsCountAround radiansPerElementUpNeck = (math.pi/4)/elementsCountUpNeck # create lower part of the ellipsoidal neckHeight = height - height * math.cos(math.pi / 4) ellipsoidal_x = [] ellipsoidal_d1 = [] ellipsoidal_d2 = [] for n2 in range(0, elementsCountUpNeck+1): radiansUp = n2 * radiansPerElementUpNeck cosRadiansUp = math.cos(radiansUp) sinRadiansUp = math.sin(radiansUp) majorRadius = 0.5 * majorDiameter * sinRadiansUp minorRadius = 0.5 * minorDiameter * sinRadiansUp if n2 == 0: for n1 in range(elementsCountAround): radiansAround = n1 * radiansPerElementAround cosRadiansAround = math.cos(radiansAround) sinRadiansAround = math.sin(radiansAround) x = [ -majorRadius * sinRadiansAround, minorRadius * cosRadiansAround, -height - neckHeight ] dx_ds1 = [ -majorRadius * cosRadiansAround * radiansPerElementAround, minorRadius * -sinRadiansAround * radiansPerElementAround, 0.0 ] dx_ds2 = [ -0.5 * majorDiameter * sinRadiansAround * cosRadiansUp * radiansPerElementUpNeck, 0.5 * minorDiameter * cosRadiansAround * cosRadiansUp * radiansPerElementUpNeck, height * sinRadiansUp * radiansPerElementUpNeck ] ellipsoidal_x.append(x) ellipsoidal_d1.append(dx_ds1) ellipsoidal_d2.append(dx_ds2) else: for n1 in range(elementsCountAround): neckHeight = height - height * math.cos(math.pi/4) radiansAround = n1 * radiansPerElementAround cosRadiansAround = math.cos(radiansAround) sinRadiansAround = math.sin(radiansAround) x = [ -majorRadius * sinRadiansAround, minorRadius * cosRadiansAround, -height - neckHeight + n2 * 2 * neckHeight / elementsCountUpNeck ] dx_ds1 = [ -majorRadius * cosRadiansAround * radiansPerElementAround, minorRadius * -sinRadiansAround * radiansPerElementAround, 0.0 ] dx_ds2 = [ -0.5 * majorDiameter * sinRadiansAround * cosRadiansUp * radiansPerElementUpNeck, 0.5 * minorDiameter * cosRadiansAround * cosRadiansUp * radiansPerElementUpNeck, height * sinRadiansUp * radiansPerElementUpNeck ] ellipsoidal_x.append(x) ellipsoidal_d1.append(dx_ds1) ellipsoidal_d2.append(dx_ds2) # create tube nodes radiansPerElementAround = 2.0 * math.pi / elementsCountAround tube_x = [] tube_d1 = [] tube_d2 = [] for n2 in range(0, elementsCountUpNeck + 1): radiansUp = n2 * radiansPerElementUpNeck cosRadiansUp = math.cos(radiansUp) sinRadiansUp = math.sin(radiansUp) if n2 == 0: for n1 in range(elementsCountAround): radiansAround = n1 * radiansPerElementAround cosRadiansAround = math.cos(radiansAround) sinRadiansAround = math.sin(radiansAround) x = [ -radius * sinRadiansAround, radius * cosRadiansAround, -height - neckHeight ] dx_ds1 = [ -radiansPerElementAround * radius * cosRadiansAround, radiansPerElementAround * radius * -sinRadiansAround, 0.0 ] dx_ds2 = [0, 0, height / (2 * elementsCountUpNeck)] tube_x.append(x) tube_d1.append(dx_ds1) tube_d2.append(dx_ds2) else: for n1 in range(elementsCountAround): neckHeight = height - height* math.cos(math.pi/4) radiansAround = n1 * radiansPerElementAround cosRadiansAround = math.cos(radiansAround) sinRadiansAround = math.sin(radiansAround) x = [ -radius * sinRadiansAround, radius * cosRadiansAround, -height - neckHeight + n2 * 2 * neckHeight / elementsCountUpNeck ] dx_ds1 = [ -radiansPerElementAround * radius * cosRadiansAround, radiansPerElementAround * radius * -sinRadiansAround, 0.0 ] dx_ds2 = [0, 0, height / elementsCountUpNeck] tube_x.append(x) tube_d1.append(dx_ds1) tube_d2.append(dx_ds2) # interpolation between the lower part of the ellipsoidal and the tube m1 = 0 z_bottom = ellipsoidal_x[-1][2] z_top = ellipsoidal_x[0][2] delta_z = z_top - z_bottom interpolatedNodes = [] interpolatedNodes_d1 = [] interpolatedNodes_d2 = [] for n2 in range(elementsCountUpNeck+1): xi = 1.0 - (ellipsoidal_x[m1][2] - z_bottom) / delta_z for n1 in range(elementsCountAround): phi_inner, _, phi_outer, _ = getCubicHermiteBasis(xi) x = [(phi_inner*tube_x[m1][c] + phi_outer*ellipsoidal_x[m1][c]) for c in range(3)] d1 = [(phi_inner*tube_d1[m1][c] + phi_outer*ellipsoidal_d1[m1][c]) for c in range(3)] d2 = [(phi_inner*tube_d2[m1][c] + phi_outer*ellipsoidal_d2[m1][c]) for c in range(3)] interpolatedNodes.append(x) interpolatedNodes_d1.append(d1) interpolatedNodes_d2.append(d2) m1 += 1 # smoothing the derivatives sd2Raw = [] for n1 in range(elementsCountAround): lineSmoothingNodes = [] lineSmoothingNodes_d2 = [] for n2 in range(elementsCountUpNeck+1): lineSmoothingNodes.append(interpolatedNodes[n1 + n2 * elementsCountAround]) lineSmoothingNodes_d2.append(interpolatedNodes_d2[n1 + n2 * elementsCountAround]) sd2 = smoothCubicHermiteDerivativesLine(lineSmoothingNodes, lineSmoothingNodes_d2, fixAllDirections=False, fixStartDerivative=True, fixEndDerivative=True, fixStartDirection=False, fixEndDirection=False) sd2Raw.append(sd2) # re-arrange the derivatives order d2RearrangedList = [] for n2 in range(elementsCountUpNeck+1): for n1 in range(elementsCountAround): d2 = sd2Raw[n1][n2] d2RearrangedList.append(d2) # create tracksurface at the outer layer of the neck nodesOnTrackSurface = [] nodesOnTrackSurface_d1 = [] nodesOnTrackSurface_d2 = [] for n2 in range(elementsCountUpNeck+1): for n1 in range(elementsCountAround): if (n1 <= elementsCountAround / 2): nodesOnTrackSurface.append(interpolatedNodes[n2 * elementsCountAround + n1]) nodesOnTrackSurface_d1.append(interpolatedNodes_d1[n2 * elementsCountAround + n1]) nodesOnTrackSurface_d2.append(d2RearrangedList[n2 * elementsCountAround + n1]) # nodes and derivatives of the neck of the bladder listOuterNeck_x = [] listOuterNeck_d1 = [] listOuterNeck_d2 = [] elementsCount1 = elementsCountAround // 2 elementsCount2 = elementsCountUpNeck tracksurfaceOstium1 = TrackSurface(elementsCount1, elementsCount2, nodesOnTrackSurface, nodesOnTrackSurface_d1, nodesOnTrackSurface_d2) ostium1Position = tracksurfaceOstium1.createPositionProportion(ostiumPositionAround, ostiumPositionUp) ostium1Position.xi1 = 1.0 ostium1Position.xi2 = 1.0 ostiumElementPositionAround = ostium1Position.e1 ostiumElementPositionUp = ostium1Position.e2 for n2 in range(len(interpolatedNodes)): listOuterNeck_x.append(interpolatedNodes[n2]) listOuterNeck_d1.append(interpolatedNodes_d1[n2]) listOuterNeck_d2.append(d2RearrangedList[n2]) # create body of the bladder radiansPerElementAround = 2.0 * math.pi / elementsCountAround radiansPerElementUpBody = (3 * math.pi / 4) / elementsCountUpBody # create regular rows listOuterBody_x = [] listOuterBody_d1 = [] listOuterBody_d2 = [] for n2 in range(1, elementsCountUpBody): radiansUp = (math.pi / 4) + n2 * radiansPerElementUpBody cosRadiansUp = math.cos(radiansUp) sinRadiansUp = math.sin(radiansUp) majorRadius = 0.5 * majorDiameter * sinRadiansUp minorRadius = 0.5 * minorDiameter * sinRadiansUp for n1 in range(elementsCountAround): radiansAround = n1 * radiansPerElementAround cosRadiansAround = math.cos(radiansAround) sinRadiansAround = math.sin(radiansAround) x = [ -majorRadius * sinRadiansAround, minorRadius * cosRadiansAround, -height * cosRadiansUp ] dx_ds1 = [ -majorRadius * cosRadiansAround * radiansPerElementAround, minorRadius * -sinRadiansAround * radiansPerElementAround, 0.0 ] dx_ds2 = [ -0.5 * majorDiameter * sinRadiansAround * cosRadiansUp * radiansPerElementUpBody, 0.5 * minorDiameter * cosRadiansAround * cosRadiansUp * radiansPerElementUpBody, height*sinRadiansUp * radiansPerElementUpBody ] listOuterBody_x.append(x) listOuterBody_d1.append(dx_ds1) listOuterBody_d2.append(dx_ds2) # create outer apex node outerApexNode_x = [] outerApexNode_d1 = [] outerApexNode_d2 = [] x = [0.0, 0.0, height] dx_ds1 = [height*radiansPerElementUpBody/2, 0.0, 0.0] dx_ds2 = [0.0, height*radiansPerElementUpBody/2, 0.0] outerApexNode_x.append(x) outerApexNode_d1.append(dx_ds1) outerApexNode_d2.append(dx_ds2) # set nodes of outer layer of the bladder listTotalOuter_x = listOuterNeck_x + listOuterBody_x + outerApexNode_x listTotalOuter_d1 = listOuterNeck_d1 + listOuterBody_d1 + outerApexNode_d1 listTotalOuter_d2 = listOuterNeck_d2 + listOuterBody_d2 + outerApexNode_d2 outerLayer_x = [] outerLayer_d1 = [] outerLayer_d2 = [] for n2 in range(len(listTotalOuter_x)): if (n2 != (ostiumElementPositionUp + 1) * elementsCountAround + ostiumElementPositionAround + 1) and\ (n2 != (ostiumElementPositionUp + 1) * elementsCountAround + elementsCountAround - ostiumElementPositionAround - 1): node = nodes.createNode(nodeIdentifier, nodetemplate) cache.setNode(node) coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_VALUE, 1, listTotalOuter_x[n2]) coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS1, 1, listTotalOuter_d1[n2]) coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS2, 1, listTotalOuter_d2[n2]) nodeIdentifier += 1 outerLayer_x.append(listTotalOuter_x[n2]) outerLayer_d1.append(listTotalOuter_d1[n2]) outerLayer_d2.append(listTotalOuter_d2[n2]) # create and set nodes of inner layer of the bladder listTotalInner_x = [] listTotalInner_d1 = [] listTotalInner_d2 = [] for n2 in range(elementsCountUpNeck + elementsCountUpBody): loop_x = [listTotalOuter_x[n2 * elementsCountAround + n1] for n1 in range(elementsCountAround)] loop_d1 = [listTotalOuter_d1[n2 * elementsCountAround + n1] for n1 in range(elementsCountAround)] loop_d2 = [listTotalOuter_d2[n2 * elementsCountAround + n1] for n1 in range(elementsCountAround)] for n1 in range(elementsCountAround): x, d1, _, _ = interp.projectHermiteCurvesThroughWall(loop_x, loop_d1, loop_d2, n1, -bladderWallThickness, loop=True) listTotalInner_x.append(x) listTotalInner_d1.append(d1) listInner_d2 = [] for n2 in range(elementsCountAround): nx = [listTotalOuter_x[n1 * elementsCountAround + n2] for n1 in range(elementsCountUpNeck + elementsCountUpBody)] nd1 = [listTotalOuter_d1[n1 * elementsCountAround + n2] for n1 in range(elementsCountUpNeck + elementsCountUpBody)] nd2 = [listTotalOuter_d2[n1 * elementsCountAround + n2] for n1 in range(elementsCountUpNeck + elementsCountUpBody)] for n1 in range(elementsCountUpNeck + elementsCountUpBody): _, d2, _, _ = interp.projectHermiteCurvesThroughWall(nx, nd2, nd1, n1, bladderWallThickness, loop=False) listInner_d2.append(d2) # re-arrange the derivatives order for n2 in range(elementsCountUpNeck + elementsCountUpBody): for n1 in range(elementsCountAround): rearranged_d2 = listInner_d2[n1 * (elementsCountUpNeck + elementsCountUpBody) + n2] listTotalInner_d2.append(rearranged_d2) innerLayer_x = [] innerLayer_d1 = [] innerLayer_d2 = [] for n2 in range(len(listTotalInner_x)): if (n2 != (ostiumElementPositionUp + 1) * elementsCountAround + ostiumElementPositionAround + 1) and \ (n2 != (ostiumElementPositionUp + 1) * elementsCountAround + elementsCountAround - ostiumElementPositionAround - 1): node = nodes.createNode(nodeIdentifier, nodetemplate) cache.setNode(node) coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_VALUE, 1, listTotalInner_x[n2]) coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS1, 1, listTotalInner_d1[n2]) coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS2, 1, listTotalInner_d2[n2]) nodeIdentifier += 1 innerLayer_x.append(listTotalInner_x[n2]) innerLayer_d1.append(listTotalInner_d1[n2]) innerLayer_d2.append(listTotalInner_d2[n2]) # create inner apex node x = [0.0, 0.0, height - bladderWallThickness] dx_ds1 = [height*radiansPerElementUpBody/2, 0.0, 0.0] dx_ds2 = [0.0, height*radiansPerElementUpBody/2, 0.0] node = nodes.createNode(nodeIdentifier, nodetemplate) cache.setNode(node) coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_VALUE, 1, x) coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS1, 1, dx_ds1) coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS2, 1, dx_ds2) listTotalInner_x.append(x) listTotalInner_d1.append(dx_ds1) listTotalInner_d2.append(dx_ds2) innerLayer_x.append(x) innerLayer_d1.append(dx_ds1) innerLayer_d2.append(dx_ds2) nodeIdentifier += 1 # create ureters on the surface elementIdentifier = 1 # ureter 1 centerUreter1_x, centerUreter1_d1, centerUreter1_d2 = tracksurfaceOstium1.evaluateCoordinates(ostium1Position, derivatives=True) td1, td2, td3 = calculate_surface_axes(centerUreter1_d1, centerUreter1_d2, [1.0, 0.0, 0.0]) m1 = ostiumElementPositionUp * elementsCountAround + ostiumElementPositionAround ureter1StartCornerx = listOuterNeck_x[m1] v1 = [(ureter1StartCornerx[c] - centerUreter1_x[c]) for c in range(3)] ostium1Direction = vector.crossproduct3(td3, v1) nodeIdentifier, elementIdentifier, (o1_x, o1_d1, o1_d2, _, o1_NodeId, o1_Positions) = \ generateOstiumMesh(region, ostiumDefaultOptions, tracksurfaceOstium1, ostium1Position, ostium1Direction, startNodeIdentifier=nodeIdentifier, startElementIdentifier=elementIdentifier) # ureter 2 tracksurfaceOstium2 = tracksurfaceOstium1.createMirrorX() ostium2Position = TrackSurfacePosition(elementsCountAround - ostiumElementPositionAround, ostiumElementPositionUp - 1, 0.0, 1.0) centerUreter2_x, centerUreter2_d1, centerUreter2_d2 = tracksurfaceOstium2.evaluateCoordinates(ostium2Position, derivatives =True) ad1, ad2, ad3 = calculate_surface_axes(centerUreter2_d1, centerUreter2_d2, [1.0, 0.0, 0.0]) if elementsCountAroundOstium == 4: m2 = ostiumElementPositionUp * elementsCountAround + elementsCountAround - ostiumElementPositionAround - 1 else: m2 = ostiumElementPositionUp * elementsCountAround + elementsCountAround - ostiumElementPositionAround - 2 ureter2StartCornerx = listOuterNeck_x[m2] v2 = [(ureter2StartCornerx[c] - centerUreter2_x[c]) for c in range(3)] ostium2Direction = vector.crossproduct3(ad3, v2) nodeIdentifier, elementIdentifier, (o2_x, o2_d1, o2_d2, _, o2_NodeId, o2_Positions) = \ generateOstiumMesh(region, ostiumDefaultOptions, tracksurfaceOstium2, ostium2Position, ostium2Direction, startNodeIdentifier=nodeIdentifier, startElementIdentifier=elementIdentifier) # create annulus mesh around ostium endPoints1_x = [[None] * elementsCountAroundOstium, [None] * elementsCountAroundOstium] endPoints1_d1 = [[None] * elementsCountAroundOstium, [None] * elementsCountAroundOstium] endPoints1_d2 = [[None] * elementsCountAroundOstium, [None] * elementsCountAroundOstium] endNode1_Id = [[None] * elementsCountAroundOstium, [None] * elementsCountAroundOstium] endDerivativesMap = [[None] * elementsCountAroundOstium, [None] * elementsCountAroundOstium] endPoints2_x = [[None] * elementsCountAroundOstium, [None] * elementsCountAroundOstium] endPoints2_d1 = [[None] * elementsCountAroundOstium, [None] * elementsCountAroundOstium] endPoints2_d2 = [[None] * elementsCountAroundOstium, [None] * elementsCountAroundOstium] endNode2_Id = [[None] * elementsCountAroundOstium, [None] * elementsCountAroundOstium] nodeCountsEachWallLayer = (elementsCountUpNeck + elementsCountUpBody) * elementsCountAround - 1 for n3 in range(2): n1 = 0 endNode1_Id[n3][n1] = ((1 - n3) * nodeCountsEachWallLayer) + (ostiumElementPositionUp * elementsCountAround) + ostiumElementPositionAround + 1 endNode1_Id[n3][n1 + 1] = endNode1_Id[n3][n1] + elementsCountAround endNode1_Id[n3][n1 + 2] = endNode1_Id[n3][n1 + 1] + elementsCountAround - 2 endNode1_Id[n3][n1 + 3] = endNode1_Id[n3][n1 + 2] + 1 endNode1_Id[n3][n1 + 4] = endNode1_Id[n3][n1 + 3] + 1 endNode1_Id[n3][n1 + 5] = endNode1_Id[n3][n1 + 1] + 1 endNode1_Id[n3][n1 + 6] = endNode1_Id[n3][n1] + 2 endNode1_Id[n3][n1 + 7] = endNode1_Id[n3][n1] + 1 if ostiumElementPositionAround == 0: endNode2_Id[n3][n1] = ((1 - n3) * nodeCountsEachWallLayer) + (ostiumElementPositionUp * elementsCountAround)\ + elementsCountAround - ostiumElementPositionAround - 1 endNode2_Id[n3][n1 + 1] = endNode2_Id[n3][n1] + elementsCountAround - 1 endNode2_Id[n3][n1 + 2] = endNode2_Id[n3][n1 + 1] + elementsCountAround - 1 endNode2_Id[n3][n1 + 3] = endNode2_Id[n3][n1 + 2] + 1 endNode2_Id[n3][n1 + 4] = endNode2_Id[n3][n1 + 3] - elementsCountAround + 1 endNode2_Id[n3][n1 + 5] = endNode2_Id[n3][n1 + 4] - elementsCountAround + 2 endNode2_Id[n3][n1 + 6] = endNode2_Id[n3][n1 + 5] - elementsCountAround endNode2_Id[n3][n1 + 7] = endNode2_Id[n3][n1] + 1 else: endNode2_Id[n3][n1] = ((1 - n3) * nodeCountsEachWallLayer) + (ostiumElementPositionUp * elementsCountAround)\ + elementsCountAround - ostiumElementPositionAround - 1 endNode2_Id[n3][n1 + 1] = endNode2_Id[n3][n1] + elementsCountAround - 1 endNode2_Id[n3][n1 + 2] = endNode2_Id[n3][n1 + 1] + elementsCountAround - 1 endNode2_Id[n3][n1 + 3] = endNode2_Id[n3][n1 + 2] + 1 endNode2_Id[n3][n1 + 4] = endNode2_Id[n3][n1 + 3] + 1 endNode2_Id[n3][n1 + 5] = endNode2_Id[n3][n1 + 1] + 1 endNode2_Id[n3][n1 + 6] = endNode2_Id[n3][n1] + 2 endNode2_Id[n3][n1 + 7] = endNode2_Id[n3][n1] + 1 for n3 in range(2): for n1 in range(elementsCountAroundOstium): nc1 = endNode1_Id[n3][n1] - (1 - n3) * nodeCountsEachWallLayer - 1 endPoints1_x[n3][n1] = innerLayer_x[nc1] endPoints1_d1[n3][n1] = innerLayer_d1[nc1] endPoints1_d2[n3][n1] = [innerLayer_d2[nc1][c] for c in range(3)] nc2 = endNode2_Id[n3][n1] - (1 - n3) * nodeCountsEachWallLayer - 1 endPoints2_x[n3][n1] = innerLayer_x[nc2] endPoints2_d1[n3][n1] = innerLayer_d1[nc2] endPoints2_d2[n3][n1] = innerLayer_d2[nc2] for n1 in range(elementsCountAroundOstium): if n1 == 0: endDerivativesMap[0][n1] = ((-1, 0, 0), (-1, -1, 0), None, (0, 1, 0)) endDerivativesMap[1][n1] = ((-1, 0, 0), (-1, -1, 0), None, (0, 1, 0)) elif n1 == 1: endDerivativesMap[0][n1] = ((0, 1, 0), (-1, 0, 0), None) endDerivativesMap[1][n1] = ((0, 1, 0), (-1, 0, 0), None) elif n1 == 2: endDerivativesMap[0][n1] = ((0, 1, 0), (-1, 1, 0), None, (1, 0, 0)) endDerivativesMap[1][n1] = ((0, 1, 0), (-1, 1, 0), None, (1, 0, 0)) elif n1 == 3: endDerivativesMap[0][n1] = ((1, 0, 0), (0, 1, 0), None) endDerivativesMap[1][n1] = ((1, 0, 0), (0, 1, 0), None) elif n1 == 4: endDerivativesMap[0][n1] = ((1, 0, 0), (1, 1, 0), None, (0, -1, 0)) endDerivativesMap[1][n1] = ((1, 0, 0), (1, 1, 0), None, (0, -1, 0)) elif n1 == 5: endDerivativesMap[0][n1] = ((0, -1, 0), (1, 0, 0), None) endDerivativesMap[1][n1] = ((0, -1, 0), (1, 0, 0), None) elif n1 == 6: endDerivativesMap[0][n1] = ((0, -1, 0), (1, -1, 0), None, (-1, 0, 0)) endDerivativesMap[1][n1] = ((0, -1, 0), (1, -1, 0), None, (-1, 0, 0)) else: endDerivativesMap[0][n1] = ((-1, 0, 0), (0, -1, 0), None) endDerivativesMap[1][n1] = ((-1, 0, 0), (0, -1, 0), None) nodeIdentifier, elementIdentifier = createAnnulusMesh3d( nodes, mesh, nodeIdentifier, elementIdentifier, o1_x, o1_d1, o1_d2, None, o1_NodeId, None, endPoints1_x, endPoints1_d1, endPoints1_d2, None, endNode1_Id, endDerivativesMap, elementsCountRadial=elementsCountAnnulusRadially, meshGroups=[neckMeshGroup, urinaryBladderMeshGroup]) nodeIdentifier, elementIdentifier = createAnnulusMesh3d( nodes, mesh, nodeIdentifier, elementIdentifier, o2_x, o2_d1, o2_d2, None, o2_NodeId, None, endPoints2_x, endPoints2_d1, endPoints2_d2, None, endNode2_Id, endDerivativesMap, elementsCountRadial=elementsCountAnnulusRadially, meshGroups=[neckMeshGroup, urinaryBladderMeshGroup]) # create elements for e3 in range(1): newl = (e3 + 1) * ((elementsCountUpNeck + elementsCountUpBody) * elementsCountAround - 1) # create bladder neck elements for e2 in range(elementsCountUpNeck): for e1 in range(elementsCountAround): if e2 == ostiumElementPositionUp: if (e1 == ostiumElementPositionAround or e1 == ostiumElementPositionAround + 1): pass elif (e1 == elementsCountAround - ostiumElementPositionAround - 2 or e1 == elementsCountAround - 1 - ostiumElementPositionAround): pass else: bni1 = e2 * elementsCountAround + e1 + 1 bni2 = e2 * elementsCountAround + (e1 + 1) % elementsCountAround + 1 if e1 < ostiumElementPositionAround: bni3 = bni1 + elementsCountAround bni4 = bni2 + elementsCountAround elif (ostiumElementPositionAround + 1 < e1 < elementsCountAround - ostiumElementPositionAround - 2): bni3 = bni1 + elementsCountAround - 1 bni4 = bni2 + elementsCountAround - 1 elif e1 > elementsCountAround - ostiumElementPositionAround - 1: bni3 = bni1 + elementsCountAround - 2 if e1 == elementsCountAround - 1: bni4 = bni2 + elementsCountAround else: bni4 = bni2 + elementsCountAround - 2 element = mesh.createElement(elementIdentifier, elementtemplate) nodeIdentifiers = [bni1 + newl, bni2 + newl, bni3 + newl, bni4 + newl, bni1, bni2, bni3, bni4] result = element.setNodesByIdentifier(eft, nodeIdentifiers) neckMeshGroup.addElement(element) urinaryBladderMeshGroup.addElement(element) elementIdentifier += 1 elif e2 == ostiumElementPositionUp + 1: if (e1 == ostiumElementPositionAround or e1 == ostiumElementPositionAround + 1): pass elif (e1 == elementsCountAround - ostiumElementPositionAround - 2 or e1 == elementsCountAround - 1 - ostiumElementPositionAround): pass else: if e1 < ostiumElementPositionAround: bni1 = e2 * elementsCountAround + e1 + 1 bni2 = e2 * elementsCountAround + (e1 + 1) % elementsCountAround + 1 bni3 = bni1 + elementsCountAround - 2 bni4 = bni2 + elementsCountAround - 2 elif (ostiumElementPositionAround + 1 < e1 < elementsCountAround - ostiumElementPositionAround - 2): bni1 = e2 * elementsCountAround + e1 bni2 = e2 * elementsCountAround + (e1 + 1) % elementsCountAround bni3 = bni1 + elementsCountAround - 1 bni4 = bni2 + elementsCountAround - 1 elif e1 > elementsCountAround - ostiumElementPositionAround - 1: bni1 = e2 * elementsCountAround + e1 - 1 bni3 = bni1 + elementsCountAround if e1 == elementsCountAround - 1: bni2 = e2 * elementsCountAround + (e1 + 1) % elementsCountAround + 1 bni4 = bni2 + elementsCountAround - 2 else: bni2 = e2 * elementsCountAround + (e1 + 1) % elementsCountAround - 1 bni4 = bni2 + elementsCountAround element = mesh.createElement(elementIdentifier, elementtemplate) nodeIdentifiers = [bni1 + newl, bni2 + newl, bni3 + newl, bni4 + newl, bni1, bni2, bni3, bni4] result = element.setNodesByIdentifier(eft, nodeIdentifiers) neckMeshGroup.addElement(element) urinaryBladderMeshGroup.addElement(element) elementIdentifier += 1 elif e2 > ostiumElementPositionUp + 1: element = mesh.createElement(elementIdentifier, elementtemplate) bni1 = e2 * elementsCountAround + e1 - 1 bni2 = e2 * elementsCountAround + (e1 + 1) % elementsCountAround - 1 bni3 = bni1 + elementsCountAround bni4 = bni2 + elementsCountAround nodeIdentifiers = [bni1 + newl, bni2 + newl, bni3 + newl, bni4 + newl, bni1, bni2, bni3, bni4] result = element.setNodesByIdentifier(eft, nodeIdentifiers) neckMeshGroup.addElement(element) urinaryBladderMeshGroup.addElement(element) elementIdentifier += 1 else: element = mesh.createElement(elementIdentifier, elementtemplate) bni1 = e2 * elementsCountAround + e1 + 1 bni2 = e2 * elementsCountAround + (e1 + 1) % elementsCountAround + 1 bni3 = bni1 + elementsCountAround bni4 = bni2 + elementsCountAround nodeIdentifiers = [bni1 + newl, bni2 + newl, bni3 + newl, bni4 + newl, bni1, bni2, bni3, bni4] result = element.setNodesByIdentifier(eft, nodeIdentifiers) neckMeshGroup.addElement(element) urinaryBladderMeshGroup.addElement(element) elementIdentifier += 1 # create bladder body elements for e2 in range(elementsCountUpNeck, (elementsCountUpNeck + elementsCountUpBody - 1)): for e1 in range(elementsCountAround): element = mesh.createElement(elementIdentifier, elementtemplate) bni1 = e2 * elementsCountAround + e1 - 1 bni2 = e2 * elementsCountAround + (e1 + 1) % elementsCountAround - 1 bni3 = bni1 + elementsCountAround bni4 = bni2 + elementsCountAround nodeIdentifiers = [bni1 + newl, bni2 + newl, bni3 + newl, bni4 + newl, bni1, bni2, bni3, bni4] result = element.setNodesByIdentifier(eft, nodeIdentifiers) bodyMeshGroup.addElement(element) urinaryBladderMeshGroup.addElement(element) elementIdentifier += 1 # create apex elements bni3 = (elementsCountUpNeck + elementsCountUpBody) * elementsCountAround - 1 elementtemplateApex = mesh.createElementtemplate() elementtemplateApex.setElementShapeType(Element.SHAPE_TYPE_CUBE) for e1 in range(elementsCountAround): va = e1 vb = (e1 + 1) % elementsCountAround eftApex = eftfactory.createEftShellPoleTop(va, vb) elementtemplateApex.defineField(coordinates, -1, eftApex) # redefine field in template for changes to eftApex: element = mesh.createElement(elementIdentifier, elementtemplateApex) bni1 = bni3 - elementsCountAround + e1 bni2 = bni3 - elementsCountAround + (e1 + 1) % elementsCountAround nodeIdentifiers = [bni1 + newl, bni2 + newl, bni3 + newl, bni1, bni2, bni3] result = element.setNodesByIdentifier(eftApex, nodeIdentifiers) bodyMeshGroup.addElement(element) urinaryBladderMeshGroup.addElement(element) elementIdentifier += 1 fm.endChange() return annotationGroups
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 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 createAnnulusMesh3d(nodes, mesh, nextNodeIdentifier, nextElementIdentifier, startPointsx, startPointsd1, startPointsd2, startPointsd3, startNodeId, startDerivativesMap, endPointsx, endPointsd1, endPointsd2, endPointsd3, endNodeId, endDerivativesMap, forceStartLinearXi3=False, forceMidLinearXi3=False, forceEndLinearXi3=False, maxStartThickness=None, maxEndThickness=None, useCrossDerivatives=False, elementsCountRadial=1, meshGroups=None, wallAnnotationGroups=None, tracksurface=None, startProportions=None, endProportions=None, rescaleStartDerivatives=False, rescaleEndDerivatives=False, sampleBlend=0.0): """ Create an annulus mesh from a loop of start points/nodes with specified derivative mappings to a loop of end points/nodes with specified derivative mappings. Derivative d3 is through the wall. Currently limited to single element layer through wall. Points/nodes order cycles fastest around the annulus, then through the wall. Note doesn't support cross derivatives. Arrays are indexed by n3 (node through wall, size 2), n2 (node along/radial), n1 (node around, variable size) and coordinate component c. :param nodes: The nodeset to create nodes in. :param mesh: The mesh to create elements in. :param nextNodeIdentifier, nextElementIdentifier: Next identifiers to use and increment. :param startPointsx, startPointsd1, startPointsd2, startPointsd3, endPointsx, endPointsd1, endPointsd2, endPointsd3: List array[n3][n1][c] or start/point coordinates and derivatives. To linearise through the wall, pass None to d3. If both ends are linear through the wall, interior points are linear through the wall. :param startNodeId, endNodeId: List array [n3][n1] of existing node identifiers to use at start/end. Pass None for argument if no nodes are specified at end. These arguments are 'all or nothing'. :param startDerivativesMap, endDerivativesMap: List array[n3][n1] of mappings for d/dxi1, d/dxi2, d/dxi3 at start/end of form: ( (1, -1, 0), (1, 0, 0), None ) where the first tuple means d/dxi1 = d/ds1 - d/ds2. Only 0, 1 and -1 may be used. None means use default e.g. d/dxi2 = d/ds2. Pass None for the entire argument to use the defaults d/dxi1 = d/ds1, d/dxi2 = d/ds2, d/dxi3 = d/ds3. Pass a 4th mapping to apply to d/dxi1 on other side of node; if not supplied first mapping applies both sides. :param forceStartLinearXi3, forceMidLinearXi3, forceEndLinearXi3: Force start, middle or end elements to be linear through the wall, even if d3 is supplied at either end. Can only use forceMidLinearXi3 only if at least one end is linear in d3. :param maxStartThickness, maxEndThickness: Optional maximum override on start/end thicknesses. :param useCrossDerivatives: May only be True if no derivatives maps are in use. :param elementsCountRadial: Optional number of elements in radial direction between start and end. :param meshGroups: Optional sequence of Zinc MeshGroup for adding all new elements to, or a sequence of length elementsCountRadial containing sequences of mesh groups to add rows of radial elements to from start to end. :param wallAnnotationGroups: Annotation groups for adding all new elements to a sequence of groups to add to elements through wall. :param tracksurface: Description for outer surface representation used for creating annulus mesh. Provides information for creating radial nodes on annulus that sit on tracksurface. Need startProportions and endProportions to work. :param startProportions: Proportion around and along of startPoints on tracksurface. These vary with nodes around as for startPoints. Values only given for tracksurface for outer layer (xi3 == 1). :param endProportions: Proportion around and along of endPoints on track surface. These vary with nodes around as for endPoints. Values only given for tracksurface for outer layer (xi3 == 1). :param rescaleStartDerivatives, rescaleEndDerivatives: Optional flags to compute and multiply additional scale factors on start, end or both radial derivatives to fit arc length, needed if derivatives are of the wrong scale for the radial distances and the chosen elementsCountRadial. If either is True, derivatives and sampled radial nodes are spaced for a gradual change of derivative from that at the other end. If both are True, scaling is set to give even sampling and arclength derivatives. :param sampleBlend: Real value varying from 0.0 to 1.0 controlling weighting of start and end derivatives when interpolating extra points in-between, where 0.0 = sample with equal end derivatives, and 1.0 = proportional to current magnitudes, interpolated in between. :return: Final values of nextNodeIdentifier, nextElementIdentifier """ assert (elementsCountRadial >= 1), 'createAnnulusMesh3d: Invalid number of radial elements' startLinearXi3 = (not startPointsd3) or forceStartLinearXi3 endLinearXi3 = (not endPointsd3) or forceEndLinearXi3 midLinearXi3 = (startLinearXi3 and endLinearXi3) or ( (startLinearXi3 or endLinearXi3) and forceMidLinearXi3) # get list whether each row of nodes in elements is linear in Xi3 # this is for element use; start/end nodes may have d3 even if element is linear rowLinearXi3 = [ startLinearXi3 ] + [midLinearXi3] * (elementsCountRadial - 1) + [endLinearXi3] assert (not useCrossDerivatives) or ((not startDerivativesMap) and (not endDerivativesMap)), \ 'createAnnulusMesh3d: Cannot use cross derivatives with derivatives map' nodesCountWall = len(startPointsx) assert (len(startPointsd1) == nodesCountWall) and (len(startPointsd2) == nodesCountWall) and \ (startLinearXi3 or (len(startPointsd3) == nodesCountWall)) and \ (len(endPointsx) == nodesCountWall) and (len(endPointsd1) == nodesCountWall) and \ (len(endPointsd2) == nodesCountWall) and (endLinearXi3 or (len(endPointsd3) == nodesCountWall)) and \ ((startNodeId is None) or (len(startNodeId) == nodesCountWall)) and \ ((endNodeId is None) or (len(endNodeId) == nodesCountWall)) and \ ((startDerivativesMap is None) or (len(startDerivativesMap) == nodesCountWall)) and \ ((endDerivativesMap is None) or (len(endDerivativesMap) == nodesCountWall)),\ 'createAnnulusMesh3d: Mismatch in number of layers through wall' elementsCountAround = nodesCountAround = len(startPointsx[0]) assert ( nodesCountAround > 1 ), 'createAnnulusMesh3d: Invalid number of points/nodes around annulus' for n3 in range(nodesCountWall): assert (len(startPointsx[n3]) == nodesCountAround) and (len(startPointsd1[n3]) == nodesCountAround) and \ (len(startPointsd2[n3]) == nodesCountAround) and \ (startLinearXi3 or (len(startPointsd3[n3]) == nodesCountAround)) and\ (len(endPointsx[n3]) == nodesCountAround) and (len(endPointsd1[n3]) == nodesCountAround) and \ (len(endPointsd2[n3]) == nodesCountAround) and \ (endLinearXi3 or (len(endPointsd3[n3]) == nodesCountAround)) and \ ((startNodeId is None) or (len(startNodeId[n3]) == nodesCountAround)) and\ ((endNodeId is None) or (len(endNodeId[n3]) == nodesCountAround)) and \ ((startDerivativesMap is None) or (len(startDerivativesMap[n3]) == nodesCountAround)) and \ ((endDerivativesMap is None) or (len(endDerivativesMap[n3]) == nodesCountAround)), \ 'createAnnulusMesh3d: Mismatch in number of points/nodes in layers through wall' rowMeshGroups = meshGroups if meshGroups: assert isinstance( meshGroups, Sequence), 'createAnnulusMesh3d: Mesh groups is not a sequence' if (len(meshGroups) == 0) or (not isinstance(meshGroups[0], Sequence)): rowMeshGroups = [meshGroups] * elementsCountRadial else: assert len(meshGroups) == elementsCountRadial, \ 'createAnnulusMesh3d: Length of meshGroups sequence does not equal elementsCountRadial' if wallAnnotationGroups: assert len(wallAnnotationGroups) == nodesCountWall - 1, \ 'createAnnulusMesh3d: Length of wallAnnotationGroups sequence does not equal elementsCountThroughWall' if tracksurface: assert startProportions and endProportions, \ 'createAnnulusMesh3d: Missing start and/or end proportions for use with tracksurface' assert len(startProportions) == nodesCountAround, \ 'createAnnulusMesh3d: Length of startProportions does not equal nodesCountAround' assert len(endProportions) == nodesCountAround, \ 'createAnnulusMesh3d: Length of endProportions does not equal nodesCountAround' fm = mesh.getFieldmodule() fm.beginChange() cache = fm.createFieldcache() coordinates = findOrCreateFieldCoordinates(fm) # Build arrays of points from start to end px = [[] for n3 in range(nodesCountWall)] pd1 = [[] for n3 in range(nodesCountWall)] pd2 = [[] for n3 in range(nodesCountWall)] pd3 = [[] for n3 in range(nodesCountWall)] # Find total wall thickness thicknessProportions = [] thicknesses = [] thicknesses.append([ vector.magnitude([ (startPointsx[nodesCountWall - 1][n1][c] - startPointsx[0][n1][c]) for c in range(3) ]) for n1 in range(nodesCountAround) ]) for n2 in range(1, elementsCountRadial): thicknesses.append([None] * nodesCountAround) thicknesses.append([ vector.magnitude([ (endPointsx[nodesCountWall - 1][n1][c] - endPointsx[0][n1][c]) for c in range(3) ]) for n1 in range(nodesCountAround) ]) for n3 in range(nodesCountWall): px[n3] = [startPointsx[n3], endPointsx[n3]] pd1[n3] = [startPointsd1[n3], endPointsd1[n3]] pd2[n3] = [startPointsd2[n3], endPointsd2[n3]] pd3[n3] = [ startPointsd3[n3] if (startPointsd3 is not None) else None, endPointsd3[n3] if (endPointsd3 is not None) else None ] startThicknessList = \ [vector.magnitude([(startPointsx[n3][n1][c] - startPointsx[n3 - (1 if n3 > 0 else 0)][n1][c]) for c in range(3)]) for n1 in range(len(startPointsx[n3]))] endThicknessList = \ [vector.magnitude([(endPointsx[n3][n1][c] - endPointsx[n3 - (1 if n3 > 0 else 0)][n1][c]) for c in range(3)]) for n1 in range(len(endPointsx[n3]))] thicknessList = [startThicknessList, endThicknessList] # thickness of each layer startThicknessProportions = [ thicknessList[0][c] / thicknesses[0][c] for c in range(nodesCountAround) ] endThicknessProportions = [ thicknessList[1][c] / thicknesses[-1][c] for c in range(nodesCountAround) ] thicknessProportions.append( [startThicknessProportions, endThicknessProportions]) if rescaleStartDerivatives: scaleFactorMapStart = [[] for n3 in range(nodesCountWall)] if rescaleEndDerivatives: scaleFactorMapEnd = [[] for n3 in range(nodesCountWall)] # following code adds in-between points, but also handles rescaling for 1 radial element for n3 in range(nodesCountWall): for n2 in range(1, elementsCountRadial): px[n3].insert(n2, [None] * nodesCountAround) pd1[n3].insert(n2, [None] * nodesCountAround) pd2[n3].insert(n2, [None] * nodesCountAround) pd3[n3].insert(n2, None if midLinearXi3 else [None] * nodesCountAround) thicknessProportions[n3].insert(n2, [None] * nodesCountAround) if maxStartThickness: for n1 in range(nodesCountAround): thicknesses[0][n1] = min(thicknesses[0][n1], maxStartThickness) if maxEndThickness: for n1 in range(nodesCountAround): thicknesses[-1][n1] = min(thicknesses[-1][n1], maxEndThickness) n3 = nodesCountWall - 1 for n1 in range(nodesCountAround): ax = startPointsx[n3][n1] ad1, ad2 = getMappedD1D2( [startPointsd1[n3][n1], startPointsd2[n3][n1]] + ([startPointsd3[n3][n1]] if startPointsd3 else []), startDerivativesMap[n3][n1] if startDerivativesMap else None) bx = endPointsx[n3][n1] bd1, bd2 = getMappedD1D2( [endPointsd1[n3][n1], endPointsd2[n3][n1]] + ([endPointsd3[n3][n1]] if endPointsd3 else []), endDerivativesMap[n3][n1] if endDerivativesMap else None) # sample between start and end points and derivatives # scaling end derivatives to arc length gives even curvature along the curve aMag = vector.magnitude(ad2) bMag = vector.magnitude(bd2) ad2Scaled = vector.setMagnitude( ad2, 0.5 * ((1.0 + sampleBlend) * aMag + (1.0 - sampleBlend) * bMag)) bd2Scaled = vector.setMagnitude( bd2, 0.5 * ((1.0 + sampleBlend) * bMag + (1.0 - sampleBlend) * aMag)) scaling = interp.computeCubicHermiteDerivativeScaling( ax, ad2Scaled, bx, bd2Scaled) ad2Scaled = [d * scaling for d in ad2Scaled] bd2Scaled = [d * scaling for d in bd2Scaled] derivativeMagnitudeStart = None if rescaleStartDerivatives else vector.magnitude( ad2) derivativeMagnitudeEnd = None if rescaleEndDerivatives else vector.magnitude( bd2) if tracksurface: mx, md2, md1, md3, mProportions = \ tracksurface.createHermiteCurvePoints(startProportions[n1][0], startProportions[n1][1], endProportions[n1][0], endProportions[n1][1], elementsCountRadial, derivativeStart=[d / elementsCountRadial for d in ad2Scaled], derivativeEnd=[d / elementsCountRadial for d in bd2Scaled]) mx, md2, md1 = \ tracksurface.resampleHermiteCurvePointsSmooth(mx, md2, md1, md3, mProportions, derivativeMagnitudeStart, derivativeMagnitudeEnd)[0:3] # interpolate thicknesses using xi calculated from radial arclength distances to points arcLengthInsideToRadialPoint = \ [0.0] + [interp.getCubicHermiteArcLength(mx[n2], md2[n2], mx[n2 + 1], md2[n2 + 1]) for n2 in range(elementsCountRadial)] arclengthInsideToOutside = sum(arcLengthInsideToRadialPoint) thi = [] for n2 in range(elementsCountRadial + 1): xi2 = arcLengthInsideToRadialPoint[ n2 - 1] / arclengthInsideToOutside thi.append(thicknesses[-1][n1] * xi2 + thicknesses[0][n1] * (1.0 - xi2)) thiProportion = [] for m3 in range(nodesCountWall): thiProportionRadial = [] for n2 in range(elementsCountRadial + 1): xi2 = arcLengthInsideToRadialPoint[ n2 - 1] / arclengthInsideToOutside thiProportionRadial.append( thicknessProportions[m3][-1][n1] * xi2 + thicknessProportions[m3][0][n1] * (1.0 - xi2)) thiProportion.append(thiProportionRadial) else: mx, md2, me, mxi = interp.sampleCubicHermiteCurvesSmooth( [ax, bx], [ad2Scaled, bd2Scaled], elementsCountRadial, derivativeMagnitudeStart, derivativeMagnitudeEnd)[0:4] md1 = interp.interpolateSampleLinear([ad1, bd1], me, mxi) thi = interp.interpolateSampleLinear( [thicknesses[0][n1], thicknesses[-1][n1]], me, mxi) thiProportion = [] for m3 in range(nodesCountWall): thiProportion.append( interp.interpolateSampleLinear([ thicknessProportions[m3][0][n1], thicknessProportions[m3][-1][n1] ], me, mxi)) # set scalefactors if rescaling, make same on inside for now if rescaleStartDerivatives: scaleFactor = vector.magnitude(md2[0]) / vector.magnitude(ad2) scaleFactorMapStart[n3].append(scaleFactor) if rescaleEndDerivatives: scaleFactor = vector.magnitude(md2[-1]) / vector.magnitude(bd2) scaleFactorMapEnd[n3].append(scaleFactor) for n2 in range(1, elementsCountRadial): px[n3][n2][n1] = mx[n2] pd1[n3][n2][n1] = md1[n2] pd2[n3][n2][n1] = md2[n2] thicknesses[n2][n1] = thi[n2] for m3 in range(nodesCountWall): thicknessProportions[m3][n2][n1] = thiProportion[m3][n2] xi3List = [[[[] for n1 in range(nodesCountAround)] for n2 in range(elementsCountRadial + 1)] for n3 in range(nodesCountWall)] for n1 in range(nodesCountAround): for n2 in range(elementsCountRadial + 1): xi3 = 0.0 for n3 in range(nodesCountWall): xi3 += thicknessProportions[n3][n2][n1] xi3List[n3][n2][n1] = xi3 # now get inner positions from normal and thickness, derivatives from curvature for n2 in range(1, elementsCountRadial): # first smooth derivative 1 around outer loop pd1[-1][n2] = \ interp.smoothCubicHermiteDerivativesLoop(px[-1][n2], pd1[-1][n2], magnitudeScalingMode=interp.DerivativeScalingMode.HARMONIC_MEAN) for n3 in range(0, nodesCountWall - 1): for n1 in range(nodesCountAround): xi3 = 1 - xi3List[n3][n2][n1] normal = vector.normalise( vector.crossproduct3(pd1[-1][n2][n1], pd2[-1][n2][n1])) thickness = thicknesses[n2][n1] * xi3 d3 = [d * thickness for d in normal] px[n3][n2][n1] = [(px[-1][n2][n1][c] - d3[c]) for c in range(3)] # calculate inner d1 from curvature around n1m = n1 - 1 n1p = (n1 + 1) % nodesCountAround curvature = 0.5 * (interp.getCubicHermiteCurvature( px[-1][n2][n1m], pd1[-1][n2][n1m], px[-1][n2][n1], pd1[-1] [n2][n1], normal, 1.0) + interp.getCubicHermiteCurvature( px[-1][n2][n1], pd1[-1][n2][n1], px[-1][n2][n1p], pd1[-1][n2][n1p], normal, 0.0)) factor = 1.0 + curvature * thickness pd1[n3][n2][n1] = [factor * d for d in pd1[-1][n2][n1]] # calculate inner d2 from curvature radially n2m = n2 - 1 n2p = n2 + 1 curvature = 0.5 * (interp.getCubicHermiteCurvature( px[-1][n2m][n1], pd2[-1][n2m][n1], px[-1][n2][n1], pd2[-1] [n2][n1], normal, 1.0) + interp.getCubicHermiteCurvature( px[-1][n2][n1], pd2[-1][n2][n1], px[-1][n2p][n1], pd2[-1][n2p][n1], normal, 0.0)) factor = 1.0 + curvature * thickness pd2[n3][n2][n1] = [factor * d for d in pd2[-1][n2][n1]] d2Scaled = [factor * d for d in pd2[-1][n2][n1]] if vector.dotproduct(vector.normalise(pd2[-1][n2][n1]), vector.normalise(d2Scaled)) == -1: pd2[n3][n2][n1] = [-factor * d for d in pd2[-1][n2][n1]] if not midLinearXi3: pd3[n3][n2][n1] = pd3[-1][n2][n1] = \ [d * thicknesses[n2][n1] * thicknessProportions[n3 + 1][n2][n1] for d in normal] # smooth derivative 1 around inner loop pd1[n3][n2] = interp.smoothCubicHermiteDerivativesLoop( px[n3][n2], pd1[n3][n2], magnitudeScalingMode=interp.DerivativeScalingMode.HARMONIC_MEAN ) for n3 in range(0, nodesCountWall): # smooth derivative 2 radially/along annulus for n1 in range(nodesCountAround): mx = [px[n3][n2][n1] for n2 in range(elementsCountRadial + 1)] md2 = [pd2[n3][n2][n1] for n2 in range(elementsCountRadial + 1)] # replace mapped start/end d2 md2[0] = getMappedD1D2( [startPointsd1[n3][n1], startPointsd2[n3][n1]] + ([startPointsd3[n3][n1]] if startPointsd3 else []), startDerivativesMap[n3][n1] if startDerivativesMap else None)[1] md2[-1] = getMappedD1D2( [endPointsd1[n3][n1], endPointsd2[n3][n1]] + ([endPointsd3[n3][n1]] if endPointsd3 else []), endDerivativesMap[n3][n1] if endDerivativesMap else None)[1] sd2 = interp.smoothCubicHermiteDerivativesLine( mx, md2, fixAllDirections=True, fixStartDerivative=not rescaleStartDerivatives, fixStartDirection=rescaleStartDerivatives, fixEndDerivative=not rescaleEndDerivatives, fixEndDirection=rescaleEndDerivatives, magnitudeScalingMode=interp.DerivativeScalingMode.HARMONIC_MEAN ) if rescaleStartDerivatives: scaleFactor = vector.magnitude(sd2[0]) / vector.magnitude( md2[0]) scaleFactorMapStart[n3].append(scaleFactor) if rescaleEndDerivatives: scaleFactor = vector.magnitude(sd2[-1]) / vector.magnitude( md2[-1]) scaleFactorMapEnd[n3].append(scaleFactor) for n2 in range(1, elementsCountRadial): pd2[n3][n2][n1] = sd2[n2] ############## # Create nodes ############## nodetemplate = nodes.createNodetemplate() nodetemplate.defineField(coordinates) nodetemplate.setValueNumberOfVersions(coordinates, -1, Node.VALUE_LABEL_VALUE, 1) nodetemplate.setValueNumberOfVersions(coordinates, -1, Node.VALUE_LABEL_D_DS1, 1) nodetemplate.setValueNumberOfVersions(coordinates, -1, Node.VALUE_LABEL_D_DS2, 1) if useCrossDerivatives: nodetemplate.setValueNumberOfVersions(coordinates, -1, Node.VALUE_LABEL_D2_DS1DS2, 1) nodetemplate.setValueNumberOfVersions(coordinates, -1, Node.VALUE_LABEL_D_DS3, 1) if useCrossDerivatives: nodetemplate.setValueNumberOfVersions(coordinates, -1, Node.VALUE_LABEL_D2_DS1DS3, 1) nodetemplate.setValueNumberOfVersions(coordinates, -1, Node.VALUE_LABEL_D2_DS2DS3, 1) nodetemplate.setValueNumberOfVersions(coordinates, -1, Node.VALUE_LABEL_D3_DS1DS2DS3, 1) 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) if useCrossDerivatives: nodetemplateLinearS3.setValueNumberOfVersions( coordinates, -1, Node.VALUE_LABEL_D2_DS1DS2, 1) nodeIdentifier = nextNodeIdentifier nodeId = [[] for n3 in range(nodesCountWall)] for n2 in range(elementsCountRadial + 1): for n3 in range(nodesCountWall): if (n2 == 0) and (startNodeId is not None): rowNodeId = copy.deepcopy(startNodeId[n3]) elif (n2 == elementsCountRadial) and (endNodeId is not None): rowNodeId = copy.deepcopy(endNodeId[n3]) else: rowNodeId = [] nodetemplate1 = nodetemplate if pd3[n3][ n2] else nodetemplateLinearS3 for n1 in range(nodesCountAround): node = nodes.createNode(nodeIdentifier, nodetemplate1) rowNodeId.append(nodeIdentifier) cache.setNode(node) coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_VALUE, 1, px[n3][n2][n1]) coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS1, 1, pd1[n3][n2][n1]) coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS2, 1, pd2[n3][n2][n1]) if pd3[n3][n2]: coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS3, 1, pd3[n3][n2][n1]) nodeIdentifier = nodeIdentifier + 1 nodeId[n3].append(rowNodeId) ################# # Create elements ################# tricubichermite = eftfactory_tricubichermite(mesh, useCrossDerivatives) bicubichermitelinear = eftfactory_bicubichermitelinear( mesh, useCrossDerivatives) elementIdentifier = nextElementIdentifier elementtemplateStandard = mesh.createElementtemplate() elementtemplateStandard.setElementShapeType(Element.SHAPE_TYPE_CUBE) elementtemplateX = mesh.createElementtemplate() elementtemplateX.setElementShapeType(Element.SHAPE_TYPE_CUBE) elementsCountWall = nodesCountWall - 1 for e2 in range(elementsCountRadial): nonlinearXi3 = (not rowLinearXi3[e2]) or (not rowLinearXi3[e2 + 1]) eftFactory = tricubichermite if nonlinearXi3 else bicubichermitelinear eftStandard = eftFactory.createEftBasic() elementtemplateStandard.defineField(coordinates, -1, eftStandard) mapStartDerivatives = (e2 == 0) and (startDerivativesMap or rescaleStartDerivatives) mapStartLinearDerivativeXi3 = nonlinearXi3 and rowLinearXi3[e2] mapEndDerivatives = (e2 == (elementsCountRadial - 1)) and ( endDerivativesMap or rescaleEndDerivatives) mapEndLinearDerivativeXi3 = nonlinearXi3 and rowLinearXi3[e2 + 1] mapDerivatives = mapStartDerivatives or mapStartLinearDerivativeXi3 or \ mapEndDerivatives or mapEndLinearDerivativeXi3 for e3 in range(elementsCountWall): for e1 in range(elementsCountAround): en = (e1 + 1) % elementsCountAround nids = [ nodeId[e3][e2][e1], nodeId[e3][e2][en], nodeId[e3][e2 + 1][e1], nodeId[e3][e2 + 1][en], nodeId[e3 + 1][e2][e1], nodeId[e3 + 1][e2][en], nodeId[e3 + 1][e2 + 1][e1], nodeId[e3 + 1][e2 + 1][en] ] scaleFactors = [] if mapDerivatives: eft1 = eftFactory.createEftNoCrossDerivatives() # work out if scaling by global -1 scaleMinus1 = mapStartLinearDerivativeXi3 or mapEndLinearDerivativeXi3 if (not scaleMinus1 ) and mapStartDerivatives and startDerivativesMap: for n3 in range(2): n3Idx = n3 + e3 # need to handle 3 or 4 maps (e1 uses last 3, en uses first 3) for map in startDerivativesMap[n3Idx][e1][-3:]: if map and (-1 in map): scaleMinus1 = True break for map in startDerivativesMap[n3Idx][en][:3]: if map and (-1 in map): scaleMinus1 = True break if (not scaleMinus1 ) and mapEndDerivatives and endDerivativesMap: for n3 in range(2): n3Idx = n3 + e3 # need to handle 3 or 4 maps (e1 uses last 3, en uses first 3) for map in endDerivativesMap[n3Idx][e1][-3:]: if map and (-1 in map): scaleMinus1 = True break for map in endDerivativesMap[n3Idx][en][:3]: if map and (-1 in map): scaleMinus1 = True break # make node scale factors vary fastest by local node varying across lower xi nodeScaleFactorIds = [] for n3 in range(2): n3Idx = n3 + e3 if mapStartDerivatives and rescaleStartDerivatives: for i in range(2): derivativesMap = ( startDerivativesMap[n3Idx][e1][1] if (i == 0) else startDerivativesMap[n3Idx] [en][1]) if startDerivativesMap else None nodeScaleFactorIds.append( getQuadrantID(derivativesMap if derivativesMap else (0, 1, 0))) if mapEndDerivatives and rescaleEndDerivatives: for i in range(2): derivativesMap = ( endDerivativesMap[n3Idx][e1][1] if (i == 0) else endDerivativesMap[n3Idx][en] [1]) if endDerivativesMap else None nodeScaleFactorIds.append( getQuadrantID(derivativesMap if derivativesMap else (0, 1, 0))) setEftScaleFactorIds(eft1, [1] if scaleMinus1 else [], nodeScaleFactorIds) firstNodeScaleFactorIndex = 2 if scaleMinus1 else 1 firstStartNodeScaleFactorIndex = \ firstNodeScaleFactorIndex if (mapStartDerivatives and rescaleStartDerivatives) else None firstEndNodeScaleFactorIndex = \ (firstNodeScaleFactorIndex + (2 if firstStartNodeScaleFactorIndex else 0)) \ if (mapEndDerivatives and rescaleEndDerivatives) else None layerNodeScaleFactorIndexOffset = \ 4 if (firstStartNodeScaleFactorIndex and firstEndNodeScaleFactorIndex) else 2 if scaleMinus1: scaleFactors.append(-1.0) for n3 in range(2): n3Idx = n3 + e3 if firstStartNodeScaleFactorIndex: scaleFactors.append(scaleFactorMapStart[n3Idx][e1]) scaleFactors.append(scaleFactorMapStart[n3Idx][en]) if firstEndNodeScaleFactorIndex: scaleFactors.append(scaleFactorMapEnd[n3Idx][e1]) scaleFactors.append(scaleFactorMapEnd[n3Idx][en]) if mapStartLinearDerivativeXi3: eftFactory.setEftLinearDerivative2( eft1, [1, 5, 2, 6], Node.VALUE_LABEL_D_DS3, [Node.VALUE_LABEL_D2_DS1DS3]) if mapStartDerivatives: for i in range(2): lns = [1, 5] if (i == 0) else [2, 6] for n3 in range(2): n3Idx = n3 + e3 derivativesMap = \ (startDerivativesMap[n3Idx][e1] if (i == 0) else startDerivativesMap[n3Idx][en]) \ if startDerivativesMap else (None, None, None) # handle different d1 on each side of node d1Map = \ derivativesMap[0] if ((i == 1) or (len(derivativesMap) < 4)) else derivativesMap[3] d2Map = derivativesMap[1] if derivativesMap[ 1] else (0, 1, 0) d3Map = derivativesMap[2] # use temporary to safely swap DS1 and DS2: ln = [lns[n3]] if d1Map: remapEftNodeValueLabel( eft1, ln, Node.VALUE_LABEL_D_DS1, [(Node.VALUE_LABEL_D2_DS1DS2, [])]) if d3Map: remapEftNodeValueLabel( eft1, ln, Node.VALUE_LABEL_D_DS3, [(Node.VALUE_LABEL_D2_DS2DS3, [])]) if d2Map: remapEftNodeValueLabel( eft1, ln, Node.VALUE_LABEL_D_DS2, derivativeSignsToExpressionTerms( (Node.VALUE_LABEL_D_DS1, Node.VALUE_LABEL_D_DS2, Node.VALUE_LABEL_D_DS3), d2Map, (firstStartNodeScaleFactorIndex + i + n3 * layerNodeScaleFactorIndexOffset) if rescaleStartDerivatives else None)) if d1Map: remapEftNodeValueLabel( eft1, ln, Node.VALUE_LABEL_D2_DS1DS2, derivativeSignsToExpressionTerms( (Node.VALUE_LABEL_D_DS1, Node.VALUE_LABEL_D_DS2, Node.VALUE_LABEL_D_DS3), d1Map)) if d3Map: remapEftNodeValueLabel( eft1, ln, Node.VALUE_LABEL_D2_DS2DS3, derivativeSignsToExpressionTerms( (Node.VALUE_LABEL_D_DS1, Node.VALUE_LABEL_D_DS2, Node.VALUE_LABEL_D_DS3), d3Map)) if mapEndLinearDerivativeXi3: eftFactory.setEftLinearDerivative2( eft1, [3, 7, 4, 8], Node.VALUE_LABEL_D_DS3, [Node.VALUE_LABEL_D2_DS1DS3]) if mapEndDerivatives: for i in range(2): lns = [3, 7] if (i == 0) else [4, 8] for n3 in range(2): n3Idx = n3 + e3 derivativesMap = \ (endDerivativesMap[n3Idx][e1] if (i == 0) else endDerivativesMap[n3Idx][en]) \ if endDerivativesMap else (None, None, None) # handle different d1 on each side of node d1Map = derivativesMap[0] if ((i == 1) or (len(derivativesMap) < 4)) else \ derivativesMap[3] d2Map = derivativesMap[1] if derivativesMap[ 1] else (0, 1, 0) d3Map = derivativesMap[2] # use temporary to safely swap DS1 and DS2: ln = [lns[n3]] if d1Map: remapEftNodeValueLabel( eft1, ln, Node.VALUE_LABEL_D_DS1, [(Node.VALUE_LABEL_D2_DS1DS2, [])]) if d3Map: remapEftNodeValueLabel( eft1, ln, Node.VALUE_LABEL_D_DS3, [(Node.VALUE_LABEL_D2_DS2DS3, [])]) if d2Map: remapEftNodeValueLabel( eft1, ln, Node.VALUE_LABEL_D_DS2, derivativeSignsToExpressionTerms( (Node.VALUE_LABEL_D_DS1, Node.VALUE_LABEL_D_DS2, Node.VALUE_LABEL_D_DS3), d2Map, (firstEndNodeScaleFactorIndex + i + n3 * layerNodeScaleFactorIndexOffset) if rescaleEndDerivatives else None)) if d1Map: remapEftNodeValueLabel( eft1, ln, Node.VALUE_LABEL_D2_DS1DS2, derivativeSignsToExpressionTerms( (Node.VALUE_LABEL_D_DS1, Node.VALUE_LABEL_D_DS2, Node.VALUE_LABEL_D_DS3), d1Map)) if d3Map: remapEftNodeValueLabel( eft1, ln, Node.VALUE_LABEL_D2_DS2DS3, derivativeSignsToExpressionTerms( (Node.VALUE_LABEL_D_DS1, Node.VALUE_LABEL_D_DS2, Node.VALUE_LABEL_D_DS3), d3Map)) elementtemplateX.defineField(coordinates, -1, eft1) elementtemplate1 = elementtemplateX else: eft1 = eftStandard elementtemplate1 = elementtemplateStandard element = mesh.createElement(elementIdentifier, elementtemplate1) result2 = element.setNodesByIdentifier(eft1, nids) if scaleFactors: result3 = element.setScaleFactors(eft1, scaleFactors) # print('create element annulus', element.isValid(), elementIdentifier, eft1.validate(), # result2, result3 if scaleFactors else None, nids) elementIdentifier += 1 if rowMeshGroups: for meshGroup in rowMeshGroups[e2]: meshGroup.addElement(element) if wallAnnotationGroups: for annotationGroup in wallAnnotationGroups[e3]: meshGroup = annotationGroup.getMeshGroup(mesh) meshGroup.addElement(element) fm.endChange() return nodeIdentifier, elementIdentifier
def warpSegmentPoints(xList, d1List, d2List, segmentAxis, segmentLength, sx, sd1, sd2, elementsCountAround, elementsCountAlongSegment, nSegment, faceMidPointZ): """ Warps points in segment to account for bending and twisting along central path defined by nodes sx and derivatives sd1 and sd2. :param xList: coordinates of segment points. :param d1List: derivatives around axis of segment. :param d2List: derivatives along axis of segment. :param segmentAxis: axis perpendicular to segment plane. :param sx: coordinates of points on central path. :param sd1: derivatives of points along central path. :param sd2: derivatives representing cross axes. :param elementsCountAround: Number of elements around segment. :param elementsCountAlongSegment: Number of elements along segment. :param nSegment: Segment index along central path. :param faceMidPointZ: z-coordinate of midpoint for each element groups along the segment. :return coordinates and derivatives of warped points. """ xWarpedList = [] d1WarpedList = [] d2WarpedList = [] smoothd2WarpedList = [] d3WarpedUnitList = [] for nAlongSegment in range(elementsCountAlongSegment + 1): n2 = elementsCountAlongSegment * nSegment + nAlongSegment xElementAlongSegment = xList[elementsCountAround * nAlongSegment:elementsCountAround * (nAlongSegment + 1)] d1ElementAlongSegment = d1List[elementsCountAround * nAlongSegment:elementsCountAround * (nAlongSegment + 1)] d2ElementAlongSegment = d2List[elementsCountAround * nAlongSegment:elementsCountAround * (nAlongSegment + 1)] xMid = [0.0, 0.0, faceMidPointZ[nAlongSegment]] # Rotate to align segment axis with tangent of central line unitTangent = vector.normalise(sd1[n2]) cp = vector.crossproduct3(segmentAxis, unitTangent) dp = vector.dotproduct(segmentAxis, unitTangent) if vector.magnitude( cp) > 0.0: # path tangent not parallel to segment axis axisRot = vector.normalise(cp) thetaRot = math.acos(vector.dotproduct(segmentAxis, unitTangent)) rotFrame = matrix.getRotationMatrixFromAxisAngle(axisRot, thetaRot) midRot = [ rotFrame[j][0] * xMid[0] + rotFrame[j][1] * xMid[1] + rotFrame[j][2] * xMid[2] for j in range(3) ] else: # path tangent parallel to segment axis (z-axis) if dp == -1.0: # path tangent opposite direction to segment axis thetaRot = math.pi axisRot = [1.0, 0, 0] rotFrame = matrix.getRotationMatrixFromAxisAngle( axisRot, thetaRot) midRot = [ rotFrame[j][0] * xMid[0] + rotFrame[j][1] * xMid[1] + rotFrame[j][2] * xMid[2] for j in range(3) ] else: # segment axis in same direction as unit tangent midRot = xMid translateMatrix = [sx[n2][j] - midRot[j] for j in range(3)] for n1 in range(elementsCountAround): x = xElementAlongSegment[n1] d1 = d1ElementAlongSegment[n1] d2 = d2ElementAlongSegment[n1] if vector.magnitude( cp) > 0.0: # path tangent not parallel to segment axis xRot1 = [ rotFrame[j][0] * x[0] + rotFrame[j][1] * x[1] + rotFrame[j][2] * x[2] for j in range(3) ] d1Rot1 = [ rotFrame[j][0] * d1[0] + rotFrame[j][1] * d1[1] + rotFrame[j][2] * d1[2] for j in range(3) ] d2Rot1 = [ rotFrame[j][0] * d2[0] + rotFrame[j][1] * d2[1] + rotFrame[j][2] * d2[2] for j in range(3) ] if n1 == 0: # Project sd2 onto plane normal to sd1 v = sd2[n2] pt = [midRot[j] + sd2[n2][j] for j in range(3)] dist = vector.dotproduct(v, unitTangent) ptOnPlane = [ pt[j] - dist * unitTangent[j] for j in range(3) ] newVector = [ptOnPlane[j] - midRot[j] for j in range(3)] # Rotate first point to align with planar projection of sd2 firstVector = vector.normalise( [xRot1[j] - midRot[j] for j in range(3)]) thetaRot2 = math.acos( vector.dotproduct(vector.normalise(newVector), firstVector)) cp2 = vector.crossproduct3(vector.normalise(newVector), firstVector) if vector.magnitude(cp2) > 0.0: cp2 = vector.normalise(cp2) signThetaRot2 = vector.dotproduct(unitTangent, cp2) axisRot2 = unitTangent rotFrame2 = matrix.getRotationMatrixFromAxisAngle( axisRot2, -signThetaRot2 * thetaRot2) else: rotFrame2 = [[1, 0, 0], [0, 1, 0], [0, 0, 1]] else: # path tangent parallel to segment axis xRot1 = [ rotFrame[j][0] * x[0] + rotFrame[j][1] * x[1] + rotFrame[j][2] * x[2] for j in range(3) ] if dp == -1.0 else x d1Rot1 = [ rotFrame[j][0] * d1[0] + rotFrame[j][1] * d1[1] + rotFrame[j][2] * d1[2] for j in range(3) ] if dp == -1.0 else d1 d2Rot1 = [ rotFrame[j][0] * d2[0] + rotFrame[j][1] * d2[1] + rotFrame[j][2] * d2[2] for j in range(3) ] if dp == -1.0 else d2 # Rotate to align start of elementsAround with sd2 if n1 == 0: v = vector.normalise(sd2[n2]) startVector = vector.normalise( [xRot1[j] - midRot[j] for j in range(3)]) axisRot2 = unitTangent thetaRot2 = dp * -math.acos( vector.dotproduct(v, startVector)) rotFrame2 = matrix.getRotationMatrixFromAxisAngle( axisRot2, thetaRot2) xRot2 = [ rotFrame2[j][0] * xRot1[0] + rotFrame2[j][1] * xRot1[1] + rotFrame2[j][2] * xRot1[2] for j in range(3) ] d1Rot2 = [ rotFrame2[j][0] * d1Rot1[0] + rotFrame2[j][1] * d1Rot1[1] + rotFrame2[j][2] * d1Rot1[2] for j in range(3) ] d2Rot2 = [ rotFrame2[j][0] * d2Rot1[0] + rotFrame2[j][1] * d2Rot1[1] + rotFrame2[j][2] * d2Rot1[2] for j in range(3) ] xTranslate = [xRot2[j] + translateMatrix[j] for j in range(3)] xWarpedList.append(xTranslate) d1WarpedList.append(d1Rot2) d2WarpedList.append(d2Rot2) # Smooth d2 for segment smoothd2Raw = [] for n1 in range(elementsCountAround): nx = [] nd2 = [] for n2 in range(elementsCountAlongSegment + 1): n = n2 * elementsCountAround + n1 nx.append(xWarpedList[n]) nd2.append(d2WarpedList[n]) smoothd2 = interp.smoothCubicHermiteDerivativesLine( nx, nd2, fixStartDerivative=True, fixEndDerivative=True) smoothd2Raw.append(smoothd2) # Re-arrange smoothd2 for n2 in range(elementsCountAlongSegment + 1): for n1 in range(elementsCountAround): smoothd2WarpedList.append(smoothd2Raw[n1][n2]) # Calculate unit d3 for n in range(len(xWarpedList)): d3Unit = vector.normalise( vector.crossproduct3(vector.normalise(d1WarpedList[n]), vector.normalise(smoothd2WarpedList[n]))) d3WarpedUnitList.append(d3Unit) return xWarpedList, d1WarpedList, smoothd2WarpedList, d3WarpedUnitList