def resampleHermiteCurvePointsSmooth(self, nx, nd1, nd2, nd3, nProportions, derivativeMagnitudeStart=None, derivativeMagnitudeEnd=None): ''' Call interp.sampleCubicHermiteCurvesSmooth on nx, nd1 and recalculate positions, nd2, nd3 for points. :return: nx[], nd1[], nd2[], nd3[], nProportions[] ''' elementsCount = len(nx) - 1 #print(nx, nd1, elementsCount, derivativeMagnitudeStart, derivativeMagnitudeEnd) nx, nd1 = interp.sampleCubicHermiteCurvesSmooth( nx, nd1, elementsCount, derivativeMagnitudeStart, derivativeMagnitudeEnd)[0:2] mag2 = vector.magnitude(nd2[0]) if mag2 > 0.0: nd2[0] = vector.setMagnitude(nd2[0], vector.magnitude(nd1[0])) for n in range(1, elementsCount): p = self.findNearestPosition( nx[n], self.createPositionProportion(*nProportions[n])) nProportions[n] = self.getProportion(p) _, sd1, sd2 = self.evaluateCoordinates(p, derivatives=True) _, d2, d3 = calculate_surface_axes(sd1, sd2, vector.normalise(nd1[n])) nd2[n] = vector.setMagnitude(d2, vector.magnitude(nd1[n])) nd3[n] = d3 mag2 = vector.magnitude(nd2[-1]) if mag2 > 0.0: nd2[-1] = vector.setMagnitude(nd2[-1], vector.magnitude(nd1[-1])) return nx, nd1, nd2, nd3, nProportions
def __init__(self, region, centralPath, elementsCount): """ :param region: Zinc region to define model in. :param centralPath: Central path subscaffold comes from meshtype_1d_path1 and used to calculate ellipse radii. :param elementsCount: Number of elements needs to be sampled along the central path. """ tmpRegion = region.createRegion() centralPath.generate(tmpRegion) cx, cd1, cd2, cd3, cd12, cd13 = extractPathParametersFromRegion( tmpRegion, [ Node.VALUE_LABEL_VALUE, Node.VALUE_LABEL_D_DS1, Node.VALUE_LABEL_D_DS2, Node.VALUE_LABEL_D_DS3, Node.VALUE_LABEL_D2_DS1DS2, Node.VALUE_LABEL_D2_DS1DS3 ]) del tmpRegion # for i in range(len(cx)): # print(i, '[', cx[i], ',', cd1[i], ',', cd2[i], ',', cd12[i], ',', cd3[i], ',', cd13[i], '],') sx, sd1, se, sxi, ssf = sampleCubicHermiteCurves( cx, cd1, elementsCount) sd2, sd12 = interpolateSampleCubicHermite(cd2, cd12, se, sxi, ssf) sd3, sd13 = interpolateSampleCubicHermite(cd3, cd13, se, sxi, ssf) self.centres = sx self.majorRadii = [vector.magnitude(a) for a in sd2] self.majorAxis = sd2 self.minorRadii = [vector.magnitude(a) for a in sd3] self.minorAxis = sd3 self.alongAxis = sd1
def 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 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 __init__(self, centre, majorAxis, minorAxis, elementsCountAcrossMajor, elementsCountAcrossMinor, elementsCountAcrossShell, elementsCountAcrossTransition, shellProportion, coreMajorRadius, coreMinorRadius, ellipseShape=EllipseShape.Ellipse_SHAPE_FULL): """ :param centre: Ellipse centre. :param majorAxis: A vector for ellipse major axis. :param minorAxis: Ellipse minor axis. :param elementsCountAcrossMajor: :param elementsCountAcrossMinor: :param ellipseShape: The shape of the ellipse which can be full or lower half. """ self.centre = centre self.majorAxis = majorAxis self.minorAxis = minorAxis self.majorRadius = vector.magnitude(majorAxis) self.minorRadius = vector.magnitude(minorAxis) self.coreMajorRadius = coreMajorRadius self.coreMinorRadius = coreMinorRadius elementsCountRim = elementsCountAcrossShell + elementsCountAcrossTransition - 1 shieldMode = ShieldShape2D.SHIELD_SHAPE_FULL if ellipseShape is EllipseShape.Ellipse_SHAPE_FULL\ else ShieldShape2D.SHIELD_SHAPE_LOWER_HALF shield = ShieldMesh2D(elementsCountAcrossMinor, elementsCountAcrossMajor, elementsCountRim, None, 1, shieldMode, shieldType=ShieldRimDerivativeMode. SHIELD_RIM_DERIVATIVE_MODE_AROUND) self.elementsCountAcrossMajor = elementsCountAcrossMajor self.elementsCountAround = shield.elementsCountAroundFull self.elementsCountUp = shield.elementsCountUp self.elementsCountAcrossMinor = elementsCountAcrossMinor self.elementsCountAcrossShell = elementsCountAcrossShell self.elementsCountAcrossTransition = elementsCountAcrossTransition self.elementsCountAcrossRim = elementsCountRim self.shellProportion = shellProportion self.nodeId = shield.nodeId self.px = shield.px[0] self.pd1 = shield.pd1[0] self.pd2 = shield.pd2[0] self.pd3 = shield.pd3[0] self.__shield = shield self.ellipseShape = ellipseShape # generate the ellipse self.generate2DEllipseMesh()
def createEllipsePerimeter(centre, majorAxis, minorAxis, elementsCountAround, height): """ Generate a set of points and derivatives for an ellipse starting at pole majorAxis from centre. :param elementsCountAround: Number of elements around. :param centre: Centre of full ellipse. :param majorAxis: Vector in direction of starting major radius, magnitude is ellipse major radius. :param minorAxis: Vector normal to major axis, magnitude is ellipse minor axis length. :param height: Height of arc of ellipsoid from starting point along majorAxis. :return: Lists nx, nd1. Ordered fastest around, starting at major radius. """ nx = [] nd1 = [] magMajorAxis = vector.magnitude(majorAxis) magMinorAxis = vector.magnitude(minorAxis) unitMajorAxis = vector.normalise(majorAxis) unitMinorAxis = vector.normalise(minorAxis) useHeight = min(max(0.0, height), 2.0 * magMajorAxis) totalRadians = math.acos((magMajorAxis - useHeight) / magMajorAxis) radians = 0.0 arcLengthUp = geometry.getEllipseArcLength(magMajorAxis, magMinorAxis, radians, totalRadians, 'integrate') elementsCountUp = elementsCountAround // 2 elementArcLength = arcLengthUp / elementsCountUp radians = geometry.updateEllipseAngleByArcLength(magMajorAxis, magMinorAxis, radians, -arcLengthUp, method='Newton') for n1 in range(2 * elementsCountUp + 1): cosRadians = math.cos(radians) sinRadians = math.sin(radians) nx.append([ (centre[c] + cosRadians * majorAxis[c] + sinRadians * minorAxis[c]) for c in range(3) ]) ndab = vector.setMagnitude( [-sinRadians * magMajorAxis, cosRadians * magMinorAxis], elementArcLength) nd1.append([(ndab[0] * unitMajorAxis[c] + ndab[1] * unitMinorAxis[c]) for c in range(3)]) radians = geometry.updateEllipseAngleByArcLength(magMajorAxis, magMinorAxis, radians, elementArcLength, method='Newton') return nx, nd1
def getDoubleCubicHermiteCurvesMidDerivative(ax, ad1, mx, bx, bd1): """ Get derivative at centre of two cubic curves. :return: Derivative at mx to balance ax, ad1 with bx, bd1. """ md1 = [ (bx[c] - ax[c]) for c in range(3) ] arcLengtha = computeCubicHermiteArcLength(ax, ad1, mx, md1, rescaleDerivatives = True) arcLengthb = computeCubicHermiteArcLength(mx, md1, bx, bd1, rescaleDerivatives = True) maga = vector.magnitude(ad1) magb = vector.magnitude(bd1) magm = arcLengtha + arcLengthb - 0.5*(maga + magb) return vector.setMagnitude(md1, magm)
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 createEllipsePoints(cx, radian, axis1, axis2, elementsCountAround, startRadians=0.0): ''' Create ellipse points centred at cx, from axis1 around through axis2. Assumes axis1 and axis2 are orthogonal. :param cx: centre :param radian: Part of ellipse to be created based on radian (will be 2*math.pi for a complete ellipse). :param axis1: Vector from cx to inside at zero angle :param axis2: Vector from cx to inside at 90 degree angle. :param elementsCountAround: Number of elements around. :param startRadians: Angle from axis1 to start creating the ellipse. :return: lists px, pd1 ''' px = [] pd1 = [] magAxis1 = vector.magnitude(axis1) magAxis2 = vector.magnitude(axis2) totalEllipsePerimeter = getApproximateEllipsePerimeter(magAxis1, magAxis2) partOfEllipsePerimeter = radian * totalEllipsePerimeter / (2 * math.pi) elementLength = partOfEllipsePerimeter / elementsCountAround if radian != 2 * math.pi: elementsCountAround = elementsCountAround + 1 unitSideAxis1 = vector.normalise(axis1) unitSideAxis2 = vector.normalise(axis2) for n in range(elementsCountAround): angle = startRadians arcLength = n * elementLength newAngle = updateEllipseAngleByArcLength(magAxis1, magAxis2, angle, arcLength) cosRadiansAround = math.cos(newAngle) sinRadiansAround = math.sin(newAngle) x = [ cx[0] + cosRadiansAround * axis1[0] - sinRadiansAround * axis2[0], cx[1] + cosRadiansAround * axis1[1] + sinRadiansAround * axis2[1], cx[2] + cosRadiansAround * axis1[2] + sinRadiansAround * axis2[2] ] px.append(x) rd1 = [ magAxis1 * (-sinRadiansAround * unitSideAxis1[c]) + magAxis2 * (cosRadiansAround * unitSideAxis2[c]) for c in range(3) ] rd1Norm = vector.normalise(rd1) pd1.append([elementLength * (rd1Norm[c]) for c in range(3)]) return px, pd1
def getSemilunarValveSinusPoints(centre, axisSide1, axisSide2, radius, sinusRadialDisplacement, startMidCusp, elementsCountAround = 6): ''' Get points around a circle of radius with every second point displaced radially. :return: x[], d1[] for 6 points around ''' assert elementsCountAround == 6, 'getArterialRootLowerPoints. Only supports 6 elements around' px = [] pd1 = [] # every second outer points is displaced by sinusRadialDisplacement to make sinus dilatation nMidCusp = 0 if startMidCusp else 1 radiusPlus = radius + sinusRadialDisplacement radiansPerElementAround = 2.0*math.pi/elementsCountAround for n in range(elementsCountAround): radiansAround = n*radiansPerElementAround midCusp = (n % 2) == nMidCusp r = radiusPlus if midCusp else radius rcosRadiansAround = r*math.cos(radiansAround) rsinRadiansAround = r*math.sin(radiansAround) px.append([ (centre[c] + rcosRadiansAround*axisSide1[c] + rsinRadiansAround*axisSide2[c]) for c in range(3) ]) pd1.append([ radiansPerElementAround*(-rsinRadiansAround*axisSide1[c] + rcosRadiansAround*axisSide2[c]) for c in range(3) ]) # smooth to get derivative in sinus sd1 = interp.smoothCubicHermiteDerivativesLine(px[1 - nMidCusp:3 - nMidCusp], pd1[1 - nMidCusp:3 - nMidCusp], fixStartDerivative=True, fixEndDirection=True) magSinus = vector.magnitude(sd1[1]) for n in range(nMidCusp, elementsCountAround, 2): pd1[n] = vector.setMagnitude(pd1[n], magSinus) return px, pd1
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 __init__(self, fieldModule, coordinates, elementsCountAlong, base=None, end=None, cylinderShape=CylinderShape.CYLINDER_SHAPE_FULL, tapered=None, cylinderCentralPath=None, useCrossDerivatives=False): """ :param fieldModule: Zinc fieldModule to create elements in. :param coordinates: Coordinate field to define. :param base: Cylinder base ellipse. It is an instance of class CylinderEnds. :param end: Cylinder end ellipse. It is an instance of class CylinderEnds. :param elementsCountAlong: Number of elements along the cylinder axis. :param cylinderShape: A value from enum CylinderMode specifying. """ self._centres = None self._majorAxis = None self._minorAxis = None self._majorRadii = None self._minorRadii = None self._base = base self._end = end self._shield = None self._elementsCountAcrossMinor = base._elementsCountAcrossMinor self._elementsCountAcrossMajor = base._elementsCountAcrossMajor self._elementsCountUp = base._elementsCountAcrossMajor // 2 \ if cylinderShape == CylinderShape.CYLINDER_SHAPE_FULL else base._elementsCountAcrossMajor self._elementsCountAlong = elementsCountAlong self._elementsCountAround = 2 * (self._elementsCountUp - 2) + self._elementsCountAcrossMinor self._startNodeIdentifier = 1 self._startElementIdentifier = 1 self._endNodeIdentifier = 1 self._endElementIdentifier = 1 self._cylinderShape = cylinderShape self._cylinderType = CylinderType.CYLINDER_STRAIGHT if (tapered is not None) or cylinderCentralPath: self._cylinderType = CylinderType.CYLINDER_TAPERED self._tapered = tapered self._useCrossDerivatives = useCrossDerivatives self._cylinderCentralPath = cylinderCentralPath if cylinderCentralPath: self.calculateEllipseParams( cylinderCentralPath=self._cylinderCentralPath) self._base = CylinderEnds(base._elementsCountAcrossMajor, base._elementsCountAcrossMinor, self._centres[0], None, self._majorAxis[0], self._minorRadii[0]) else: self._length = vector.magnitude(base._alongAxis) arcLengthAlong = self._length / elementsCountAlong self.calculateEllipseParams( arcLengthAlong, cylinderCentralPath=self._cylinderCentralPath) # generate the mesh self.createCylinderMesh3d(fieldModule, coordinates)
def computeCubicHermiteDerivativeScaling(v1, d1, v2, d2): ''' Compute scaling for d1, d2 which makes their sum twice the arc length. :return: Scale factor to multiply d1, d2 ''' origMag = 0.5*(vector.magnitude(d1) + vector.magnitude(d2)) scaling = 1.0 for iters in range(100): mag = origMag*scaling arcLength = getCubicHermiteArcLength(v1, [ d*scaling for d in d1 ], v2, [ d*scaling for d in d2 ]) if math.fabs(arcLength - mag) < 0.000001*arcLength: #print('compute scaling', v1, d1, v2, d2, '\n --> scaling',scaling) return scaling scaling *= arcLength/mag print('computeCubicHermiteDerivativeScaling: Max iters reached:', iters, ' mag', mag, 'arc', arcLength) return scaling
def __init__(self, mirrorPlane): ''' :param mirrorPlane: Plane ax+by+cd=d defined as a list p=[a,b,c,d]. ''' self._plane = mirrorPlane self._planeUnitNormalVector = self.getPlaneNormalVector() self._magNormal = vector.magnitude(mirrorPlane[:3]) self._planeDParam = mirrorPlane[3]
def calculate_surface_delta_xi(d1, d2, direction): ''' Calculate dxi1, dxi2 in 3-D vector direction. :param d1, d2: Derivatives of coordinate w.r.t. xi1, xi2. :param direction: 3-D vector. :return: delta_xi1, delta_xi2 ''' # overdetermined system (3-D vector, 2-D surface), solve least squares # A transpose A x = A transpose b a = [[0.0, 0.0], [0.0, 0.0]] b = [0.0, 0.0] dx_dxi = [d1, d2] for i in range(2): for j in range(2): for k in range(3): a[i][j] += dx_dxi[i][k] * dx_dxi[j][k] for k in range(3): b[i] += dx_dxi[i][k] * direction[k] # 2x2 matrix inverse deta = a[0][0] * a[1][1] - a[0][1] * a[1][0] if deta > 0.0: inva = [[a[1][1] / deta, -a[0][1] / deta], [-a[1][0] / deta, a[0][0] / deta]] delta_xi1 = inva[0][0] * b[0] + inva[0][1] * b[1] delta_xi2 = inva[1][0] * b[0] + inva[1][1] * b[1] else: # at pole: assume direction is inline with d1 or d2 and other is zero delta_xi2 = vector.dotproduct(d2, direction) if math.fabs(delta_xi2) > 0.0: delta_xi1 = 0.0 delta_xi2 = (1.0 if (delta_xi2 > 0.0) else -1.0 ) * vector.magnitude(direction) / vector.magnitude(d2) else: delta_xi1 = vector.dotproduct(d1, direction) if math.fabs(delta_xi1) > 0.0: delta_xi1 = (1.0 if (delta_xi1 > 0.0) else -1.0) * vector.magnitude( direction) / vector.magnitude(d1) delta_xi2 = 0.0 #delx = [ (delta_xi1*d1[c] + delta_xi2*d2[c]) for c in range(3) ] #print('delx', delx, 'dir', direction, 'diff', vector.magnitude([ (delx[c] - direction[c]) for c in range(3) ])) return delta_xi1, delta_xi2
def makeD2Normal(cls, region, options, editGroupName): if not options['D2 derivatives']: return d1, d2 = extractPathParametersFromRegion( region, [Node.VALUE_LABEL_D_DS1, Node.VALUE_LABEL_D_DS2]) for c in range(len(d1)): td2 = vector.vectorRejection(d2[c], d1[c]) d2[c] = vector.setMagnitude(td2, vector.magnitude(d2[c])) setPathParameters(region, [Node.VALUE_LABEL_D_DS2], [d2], editGroupName) return False, True # settings not changed, nodes changed
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 getCubicHermiteCurvature(v1, d1, v2, d2, radialVector, xi): """ :param radialVector: Radial direction, assumed unit normal to curve tangent at point. :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) #tangentVector = vector.normalise(tangent) #tangentCurvature = vector.dotproduct(dTangent, tangentVector) radialCurvature = vector.dotproduct(dTangent, radialVector) magTangent = vector.magnitude(tangent) curvature = radialCurvature / (magTangent * magTangent) return curvature
def getCubicHermiteCurvature(v1, d1, v2, d2, radialVector, 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 radialVector: Radial direction, assumed unit normal to curve tangent at point. :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) #tangentVector = vector.normalise(tangent) #tangentCurvature = vector.dotproduct(dTangent, tangentVector) radialCurvature = vector.dotproduct(dTangent, radialVector) magTangent = vector.magnitude(tangent) curvature = radialCurvature / (magTangent * magTangent) return curvature
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. ''' 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)]) ax3 = vector.crossproduct3(d1, d2) mag3 = vector.magnitude(ax3) if mag3 > 0.0: ax3 = [s / mag3 for s in ax3] ax2 = vector.normalise(vector.crossproduct3(ax3, ax1)) else: ax3 = [0.0, 0.0, 0.0] ax2 = [0.0, 0.0, 0.0] return ax1, ax2, ax3
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 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 smoothCubicHermiteCrossDerivativesLine(nx, nd1, nd2, nd12, fixStartDerivative=False, fixEndDerivative=False, instrument=False): """ Smooth derivatives of cross directions of hermite curves. Assumes initial nd12 derivatives are zero or reasonable. Where directions are smoothed the weighted/harmonic mean is used. :param nx: List of coordinates of nodes along curves. :param nd1: List of derivatives of nodes along curves. :param nd2: List of lateral direction vectors of nodes along curves. :param nd12: List of derivatives of lateral directions along curves. :param fixStartDerivative, fixEndDerivative: Set to True to fix derivative direction and magnitude at respective end. :return: Modified nd12 """ nodesCount = len(nx) elementsCount = nodesCount - 1 assert elementsCount > 0, 'smoothCubicHermiteCrossDerivativesLine. Too few nodes/elements' assert len( nd1 ) == nodesCount, 'smoothCubicHermiteCrossDerivativesLine. Mismatched number of derivatives' md12 = copy.copy(nd12) componentsCount = len(nx[0]) componentRange = range(componentsCount) # special case where equal derivatives at each end are sought if (elementsCount == 1) and not (fixStartDerivative or fixEndDerivative): delta = [(nd2[1][c] - nd2[0][c]) for c in componentRange] return [delta, copy.deepcopy(delta)] tol = 1.0E-6 arcLengths = [ getCubicHermiteArcLength(nx[e], nd1[e], nx[e + 1], nd1[e + 1]) for e in range(elementsCount) ] dtol = tol * sum(vector.magnitude(d) for d in nd2) if instrument: print('iter 0', md12) for iter in range(100): lastmd12 = copy.copy(md12) # start if not fixStartDerivative: md12[0] = interpolateLagrangeHermiteDerivative( nd2[0], nd2[1], lastmd12[1], 0.0) # middle for n in range(1, nodesCount - 1): nm = n - 1 # get mean of directions from point n to points (n - 1) and (n + 1) np = n + 1 dirm = [(nd2[n][c] - nd2[nm][c]) for c in componentRange] dirp = [(nd2[np][c] - nd2[n][c]) for c in componentRange] # mean weighted by fraction towards that end, equivalent to harmonic mean arcLengthmp = arcLengths[nm] + arcLengths[n] wm = arcLengths[n] / arcLengthmp wp = arcLengths[nm] / arcLengthmp md12[n] = [(wm * dirm[c] + wp * dirp[c]) for c in componentRange] # end if not fixEndDerivative: md12[-1] = interpolateHermiteLagrangeDerivative( nd2[-2], lastmd12[-2], nd2[-1], 1.0) if instrument: print('iter', iter + 1, md12) for n in range(nodesCount): for c in componentRange: if math.fabs(md12[n][c] - lastmd12[n][c]) > dtol: break else: continue break else: if instrument: print( 'smoothCubicHermiteCrossDerivativesLine converged after iter:', iter + 1) return md12 cmax = 0.0 for n in range(nodesCount): for c in componentRange: cmax = max(cmax, math.fabs(md12[n][c] - lastmd12[n][c])) closeness = cmax / dtol print('smoothCubicHermiteCrossDerivativesLine max iters reached:', iter + 1, ', cmax = ', round(closeness, 2), 'x tolerance') return md12
def smoothCubicHermiteDerivativesLine( nx, nd1, fixAllDirections=False, fixStartDerivative=False, fixEndDerivative=False, fixStartDirection=False, fixEndDirection=False, magnitudeScalingMode=DerivativeScalingMode.ARITHMETIC_MEAN, instrument=False): """ Modifies derivatives nd1 to be smoothly varying and near arc length. Values are treated as being in a line. Assumes initial derivatives are zero or reasonable. Where directions are smoothed the weighted/harmonic mean is used. :param nx: List of coordinates of nodes along curves. :param nd1: List of derivatives of nodes along curves. :param fixAllDirections: Set to True to only smooth magnitudes, otherwise both direction and magnitude are adjusted. :param fixStartDerivative, fixEndDerivative: Set to True to fix derivative direction and magnitude at respective end. :param fixStartDirection, fixEndDirection: Set to True to fix direction at respective end. Redundant if fixAllDirections or respective fixStart/EndDerivative is True. :param magnitudeScalingMode: A value from enum DerivativeScalingMode specifying expression used to get derivative magnitude from adjacent arc lengths. :return: Modified nd1 """ nodesCount = len(nx) elementsCount = nodesCount - 1 assert elementsCount > 0, 'smoothCubicHermiteDerivativesLine. Too few nodes/elements' assert len( nd1 ) == nodesCount, 'smoothCubicHermiteDerivativesLine. Mismatched number of derivatives' arithmeticMeanMagnitude = magnitudeScalingMode is DerivativeScalingMode.ARITHMETIC_MEAN assert arithmeticMeanMagnitude or (magnitudeScalingMode is DerivativeScalingMode.HARMONIC_MEAN), \ 'smoothCubicHermiteDerivativesLine. Invalid magnitude scaling mode' md1 = copy.copy(nd1) componentsCount = len(nx[0]) componentRange = range(componentsCount) if elementsCount == 1: # special cases for one element if not (fixStartDerivative or fixEndDerivative or fixStartDirection or fixEndDirection or fixAllDirections): # straight line delta = [(nx[1][c] - nx[0][c]) for c in componentRange] return [delta, copy.deepcopy(delta)] if fixAllDirections or (fixStartDirection and fixEndDirection): # fixed directions, equal magnitude arcLength = computeCubicHermiteArcLength(nx[0], nd1[0], nx[1], nd1[1], rescaleDerivatives=True) return [ vector.setMagnitude(nd1[0], arcLength), vector.setMagnitude(nd1[1], arcLength) ] tol = 1.0E-6 if instrument: print('iter 0', md1) for iter in range(100): lastmd1 = copy.copy(md1) arcLengths = [ getCubicHermiteArcLength(nx[e], md1[e], nx[e + 1], md1[e + 1]) for e in range(elementsCount) ] # start if not fixStartDerivative: if fixAllDirections or fixStartDirection: mag = 2.0 * arcLengths[0] - vector.magnitude(lastmd1[1]) md1[0] = vector.setMagnitude( nd1[0], mag) if (mag > 0.0) else [0.0, 0.0, 0.0] else: md1[0] = interpolateLagrangeHermiteDerivative( nx[0], nx[1], lastmd1[1], 0.0) # middle for n in range(1, nodesCount - 1): nm = n - 1 if not fixAllDirections: # get mean of directions from point n to points (n - 1) and (n + 1) np = n + 1 dirm = [(nx[n][c] - nx[nm][c]) for c in componentRange] dirp = [(nx[np][c] - nx[n][c]) for c in componentRange] # mean weighted by fraction towards that end, equivalent to harmonic mean arcLengthmp = arcLengths[nm] + arcLengths[n] wm = arcLengths[n] / arcLengthmp wp = arcLengths[nm] / arcLengthmp md1[n] = [(wm * dirm[c] + wp * dirp[c]) for c in componentRange] if arithmeticMeanMagnitude: mag = 0.5 * (arcLengths[nm] + arcLengths[n]) else: # harmonicMeanMagnitude mag = 2.0 / (1.0 / arcLengths[nm] + 1.0 / arcLengths[n]) md1[n] = vector.setMagnitude(md1[n], mag) # end if not fixEndDerivative: if fixAllDirections or fixEndDirection: mag = 2.0 * arcLengths[-1] - vector.magnitude(lastmd1[-2]) md1[-1] = vector.setMagnitude( nd1[-1], mag) if (mag > 0.0) else [0.0, 0.0, 0.0] else: md1[-1] = interpolateHermiteLagrangeDerivative( nx[-2], lastmd1[-2], nx[-1], 1.0) if instrument: print('iter', iter + 1, md1) dtol = tol * sum(arcLengths) / len(arcLengths) for n in range(nodesCount): for c in componentRange: if math.fabs(md1[n][c] - lastmd1[n][c]) > dtol: break else: continue break else: if instrument: print( 'smoothCubicHermiteDerivativesLine converged after iter:', iter + 1) return md1 cmax = 0.0 for n in range(nodesCount): for c in componentRange: cmax = max(cmax, math.fabs(md1[n][c] - lastmd1[n][c])) closeness = cmax / dtol print('smoothCubicHermiteDerivativesLine max iters reached:', iter + 1, ', cmax = ', round(closeness, 2), 'x tolerance') return md1
def sampleCubicHermiteCurvesSmooth(nx, nd1, elementsCountOut, derivativeMagnitudeStart=None, derivativeMagnitudeEnd=None): """ Get smoothly spaced points and derivatives over cubic Hermite interpolated curves with nodes nx and derivatives nd1. The first element uses the first two nodes. Gives smooth variation of element size to fit the supplied start and end derivative magnitudes. :param nx: Coordinates of nodes along curves. :param nd1: Derivatives of nodes along curves. :param derivativeMagnitudeStart, derivativeMagnitudeEnd: Optional magnitudes of start and end derivatives appropriate for elementsCountOut. If unspecified these are calculated from the other end or set to be equal for even spaced elements. :return: px[], pd1[], pe[], pxi[], psf[], where pe[] and pxi[] are lists of element indices and and xi locations in the 'in' elements to pass to partner interpolateSample functions. psf[] is a list of scale factors for converting derivatives from old to new xi coordinates: dxi(old)/dxi(new). """ elementsCountIn = len(nx) - 1 assert (elementsCountIn > 0) and (len(nd1) == (elementsCountIn + 1)) and (elementsCountOut > 0), \ 'sampleCubicHermiteCurvesSmooth. Invalid arguments' lengths = [0.0] length = 0.0 for e in range(elementsCountIn): length += getCubicHermiteArcLength(nx[e], nd1[e], nx[e + 1], nd1[e + 1]) lengths.append(length) if derivativeMagnitudeStart and derivativeMagnitudeEnd: pass elif derivativeMagnitudeEnd: derivativeMagnitudeStart = (2.0 * length - elementsCountOut * derivativeMagnitudeEnd) / elementsCountOut elif derivativeMagnitudeStart: derivativeMagnitudeEnd = (2.0 * length - elementsCountOut * derivativeMagnitudeStart) / elementsCountOut else: derivativeMagnitudeStart = derivativeMagnitudeEnd = length / elementsCountOut # sample over length to get distances to elements boundaries x1 = 0.0 d1 = derivativeMagnitudeStart * elementsCountOut x2 = length d2 = derivativeMagnitudeEnd * elementsCountOut nodeDistances = [] nodeDerivativeMagnitudes = [] for n in range(elementsCountOut + 1): xi = n / elementsCountOut f1, f2, f3, f4 = getCubicHermiteBasis(xi) distance = f1 * x1 + f2 * d1 + f3 * x2 + f4 * d2 nodeDistances.append(distance) f1, f2, f3, f4 = getCubicHermiteBasisDerivatives(xi) derivative = f1 * x1 + f2 * d1 + f3 * x2 + f4 * d2 nodeDerivativeMagnitudes.append(derivative / elementsCountOut) #print('nodeDerivativeMagnitudesIn ', [ vector.magnitude(d1) for d1 in nd1 ]) #print('nodeDerivativeMagnitudesOut', nodeDerivativeMagnitudes) px = [] pd1 = [] pe = [] pxi = [] psf = [] e = 0 for eOut in range(elementsCountOut): distance = nodeDistances[eOut] while e < elementsCountIn: if distance < lengths[e + 1]: partDistance = distance - lengths[e] x, d1, _, xi = getCubicHermiteCurvesPointAtArcDistance( nx[e:e + 2], nd1[e:e + 2], partDistance) sf = nodeDerivativeMagnitudes[eOut] / vector.magnitude(d1) px.append(x) pd1.append([sf * d for d in d1]) pe.append(e) pxi.append(xi) psf.append(sf) break e += 1 e = elementsCountIn eOut = elementsCountOut xi = 1.0 d1 = nd1[e] sf = nodeDerivativeMagnitudes[eOut] / vector.magnitude(d1) px.append(nx[e]) pd1.append([sf * d for d in d1]) pe.append(e - 1) pxi.append(xi) psf.append(sf) return px, pd1, pe, pxi, psf
def sampleCubicHermiteCurves(nx, nd1, elementsCountOut, addLengthStart=0.0, addLengthEnd=0.0, lengthFractionStart=1.0, lengthFractionEnd=1.0, elementLengthStartEndRatio=1.0, arcLengthDerivatives=False): """ Get systematically spaced points and derivatives over cubic Hermite interpolated curves with nodes nx and derivatives nd1. The first element uses the first two nodes. :param nx: Coordinates of nodes along curves. :param nd1: Derivatives of nodes along curves. :param addLengthStart, addLengthEnd: Extra length to add to start and end elements. :param lengthFractionStart, lengthFractionEnd: Fraction of mid element length for start and end elements. Can use in addition to AddLengths: If LengthFraction is 0.5 and AddLength is derivative/2.0 can blend into known derivative at start or end. :param elementLengthStartEndRatio: Start/end element length ratio, with lengths smoothly varying in between. Requires at least 2 elements. Applied in proportion to lengthFractionStart, lengthFractionEnd. :param arcLengthDerivatives: If True each cubic section is rescaled to arc length. If False (default), derivatives and distances are used as supplied. :return: px[], pd1[], pe[], pxi[], psf[], where pe[] and pxi[] are lists of element indices and and xi locations in the 'in' elements to pass to partner interpolateSample functions. psf[] is a list of scale factors for converting derivatives from old to new xi coordinates: dxi(old)/dxi(new). """ elementsCountIn = len(nx) - 1 assert (elementsCountIn > 0) and (len(nd1) == (elementsCountIn + 1)) and \ (elementsCountOut > 0), 'sampleCubicHermiteCurves. Invalid arguments' lengths = [0.0] nd1a = [] nd1b = [] length = 0.0 for e in range(elementsCountIn): if arcLengthDerivatives: arcLength = computeCubicHermiteArcLength(nx[e], nd1[e], nx[e + 1], nd1[e + 1], rescaleDerivatives=True) nd1a.append(vector.setMagnitude(nd1[e], arcLength)) nd1b.append(vector.setMagnitude(nd1[e + 1], arcLength)) else: arcLength = getCubicHermiteArcLength(nx[e], nd1[e], nx[e + 1], nd1[e + 1]) length += arcLength lengths.append(length) proportionEnd = 2.0 / (elementLengthStartEndRatio + 1) proportionStart = elementLengthStartEndRatio * proportionEnd if elementsCountOut == 1: elementLengthMid = length else: elementLengthMid = (length - addLengthStart - addLengthEnd) / \ (elementsCountOut - 2.0 + proportionStart*lengthFractionStart + proportionEnd*lengthFractionEnd) elementLengthProportionStart = proportionStart * lengthFractionStart * elementLengthMid elementLengthProportionEnd = proportionEnd * lengthFractionEnd * elementLengthMid # get smoothly varying element lengths, not accounting for start and end if (elementsCountOut == 1) or (elementLengthStartEndRatio == 1.0): elementLengths = [elementLengthMid] * elementsCountOut else: elementLengths = [] for eOut in range(elementsCountOut): xi = eOut / (elementsCountOut - 1) elementLengths.append( ((1.0 - xi) * proportionStart + xi * proportionEnd) * elementLengthMid) # get middle derivative magnitudes nodeDerivativeMagnitudes = [None] * (elementsCountOut + 1 ) # start and end determined below for n in range(1, elementsCountOut): nodeDerivativeMagnitudes[n] = 0.5 * (elementLengths[n - 1] + elementLengths[n]) # fix end lengths: elementLengths[0] = addLengthStart + elementLengthProportionStart elementLengths[-1] = addLengthEnd + elementLengthProportionEnd #print('\nsampleCubicHermiteCurves:') #print(' elementLengths', elementLengths, 'addLengthStart', addLengthStart, 'addLengthEnd', addLengthEnd) #print(' sum lengths', sum(elementLengths), 'vs. length', length, 'diff', sum(elementLengths) - length) # set end derivatives: if elementsCountOut == 1: nodeDerivativeMagnitudes[0] = nodeDerivativeMagnitudes[ 1] = elementLengths[0] else: nodeDerivativeMagnitudes[ 0] = elementLengths[0] * 2.0 - nodeDerivativeMagnitudes[1] nodeDerivativeMagnitudes[ -1] = elementLengths[-1] * 2.0 - nodeDerivativeMagnitudes[-2] px = [] pd1 = [] pe = [] pxi = [] psf = [] distance = 0.0 e = 0 for eOut in range(elementsCountOut): while e < elementsCountIn: if distance < lengths[e + 1]: partDistance = distance - lengths[e] if arcLengthDerivatives: xi = partDistance / (lengths[e + 1] - lengths[e]) x = interpolateCubicHermite(nx[e], nd1a[e], nx[e + 1], nd1b[e], xi) d1 = interpolateCubicHermiteDerivative( nx[e], nd1a[e], nx[e + 1], nd1b[e], xi) else: x, d1, _eIn, xi = getCubicHermiteCurvesPointAtArcDistance( nx[e:e + 2], nd1[e:e + 2], partDistance) sf = nodeDerivativeMagnitudes[eOut] / vector.magnitude(d1) px.append(x) pd1.append([sf * d for d in d1]) pe.append(e) pxi.append(xi) psf.append(sf) break e += 1 distance += elementLengths[eOut] e = elementsCountIn eOut = elementsCountOut xi = 1.0 d1 = nd1[e] sf = nodeDerivativeMagnitudes[eOut] / vector.magnitude(d1) px.append(nx[e]) pd1.append([sf * d for d in d1]) pe.append(e - 1) pxi.append(xi) psf.append(sf) return px, pd1, pe, pxi, psf
def generateBaseMesh(cls, region, options): """ Generate the base tricubic Hermite mesh. See also generateMesh(). :param region: Zinc region to define model in. Must be empty. :param options: Dict containing options. See getDefaultOptions(). :return: annotationGroups """ centralPath = options['Central path'] elementsCountAround = options['Number of elements around'] elementsCountAlong = options['Number of elements along'] elementsCountThroughWall = options['Number of elements through wall'] wallThickness = options['Wall thickness'] mucosaRelThickness = options['Mucosa relative thickness'] submucosaRelThickness = options['Submucosa relative thickness'] circularRelThickness = options[ 'Circular muscle layer relative thickness'] longitudinalRelThickness = options[ 'Longitudinal muscle layer relative thickness'] useCrossDerivatives = options['Use cross derivatives'] useCubicHermiteThroughWall = not (options['Use linear through wall']) firstNodeIdentifier = 1 firstElementIdentifier = 1 # Central path esophagusTermsAlong = [ None, 'cervical part of esophagus', 'thoracic part of esophagus', 'abdominal part of esophagus' ] arcLengthOfGroupsAlong = [] for i in range(len(esophagusTermsAlong)): tmpRegion = region.createRegion() centralPath.generate(tmpRegion) cxGroup, cd1Group, cd2Group, cd3Group, cd12Group, cd13Group = \ extractPathParametersFromRegion(tmpRegion, [Node.VALUE_LABEL_VALUE, Node.VALUE_LABEL_D_DS1, Node.VALUE_LABEL_D_DS2, Node.VALUE_LABEL_D_DS3, Node.VALUE_LABEL_D2_DS1DS2, Node.VALUE_LABEL_D2_DS1DS3], groupName=esophagusTermsAlong[i]) arcLength = 0.0 for e in range(len(cxGroup) - 1): arcLength += interp.getCubicHermiteArcLength( cxGroup[e], cd1Group[e], cxGroup[e + 1], cd1Group[e + 1]) arcLengthOfGroupsAlong.append(arcLength) if i == 0: cx = cxGroup cd1 = cd1Group cd2 = cd2Group cd3 = cd3Group cd12 = cd12Group cd13 = cd13Group del tmpRegion # Sample central path sx, sd1, se, sxi, ssf = interp.sampleCubicHermiteCurves( cx, cd1, elementsCountAlong) sd2, sd12 = interp.interpolateSampleCubicHermite( cd2, cd12, se, sxi, ssf) sd3, sd13 = interp.interpolateSampleCubicHermite( cd3, cd13, se, sxi, ssf) centralPathLength = arcLengthOfGroupsAlong[0] elementAlongLength = centralPathLength / elementsCountAlong elementsCountAlongGroups = [] groupLength = 0.0 e = 0 elementsCount = 1 length = elementAlongLength for i in range(1, len(esophagusTermsAlong)): groupLength += arcLengthOfGroupsAlong[i] if e == elementsCountAlong - 2: elementsCount += 1 elementsCountAlongGroups.append(elementsCount) else: while length < groupLength: elementsCount += 1 e += 1 length += elementAlongLength # check which end is grouplength closer to distToUpperEnd = abs(length - groupLength) distToLowerEnd = abs(groupLength - (length - elementsCountAlong)) if distToLowerEnd < distToUpperEnd: elementsCount -= 1 elementsCountAlongGroups.append(elementsCount) e -= 1 length -= elementAlongLength else: elementsCountAlongGroups.append(elementsCount) elementsCount = 0 majorRadiusElementList = sd2 minorRadiusElementList = sd3 # Create annotation groups along esophagus esophagusGroup = AnnotationGroup(region, get_esophagus_term("esophagus")) cervicalGroup = AnnotationGroup( region, get_esophagus_term("cervical part of esophagus")) thoracicGroup = AnnotationGroup( region, get_esophagus_term("thoracic part of esophagus")) abdominalGroup = AnnotationGroup( region, get_esophagus_term("abdominal part of esophagus")) annotationGroupAlong = [[esophagusGroup, cervicalGroup], [esophagusGroup, thoracicGroup], [esophagusGroup, abdominalGroup]] annotationGroupsAlong = [] for i in range(len(elementsCountAlongGroups)): elementsCount = elementsCountAlongGroups[i] for n in range(elementsCount): annotationGroupsAlong.append(annotationGroupAlong[i]) annotationGroupsAround = [] for i in range(elementsCountAround): annotationGroupsAround.append([]) # Groups through wall longitudinalMuscleGroup = AnnotationGroup( region, get_esophagus_term("esophagus smooth muscle longitudinal layer")) circularMuscleGroup = AnnotationGroup( region, get_esophagus_term("esophagus smooth muscle circular layer")) submucosaGroup = AnnotationGroup( region, get_esophagus_term("submucosa of esophagus")) mucosaGroup = AnnotationGroup(region, get_esophagus_term("esophagus mucosa")) if elementsCountThroughWall == 1: relativeThicknessList = [1.0] annotationGroupsThroughWall = [[]] else: relativeThicknessList = [ mucosaRelThickness, submucosaRelThickness, circularRelThickness, longitudinalRelThickness ] annotationGroupsThroughWall = [[mucosaGroup], [submucosaGroup], [circularMuscleGroup], [longitudinalMuscleGroup]] xToSample = [] d1ToSample = [] for n2 in range(elementsCountAlong + 1): # Create inner points cx = [0.0, 0.0, elementAlongLength * n2] axis1 = [vector.magnitude(majorRadiusElementList[n2]), 0.0, 0.0] axis2 = [0.0, vector.magnitude(minorRadiusElementList[n2]), 0.0] xInner, d1Inner = geometry.createEllipsePoints(cx, 2 * math.pi, axis1, axis2, elementsCountAround, startRadians=0.0) xToSample += xInner d1ToSample += d1Inner d2ToSample = [[0.0, 0.0, elementAlongLength] ] * (elementsCountAround * (elementsCountAlong + 1)) # Sample along length xInnerRaw = [] d2InnerRaw = [] xToWarp = [] d1ToWarp = [] d2ToWarp = [] flatWidthList = [] xiList = [] for n1 in range(elementsCountAround): xForSamplingAlong = [] d2ForSamplingAlong = [] for n2 in range(elementsCountAlong + 1): idx = n2 * elementsCountAround + n1 xForSamplingAlong.append(xToSample[idx]) d2ForSamplingAlong.append(d2ToSample[idx]) xSampled, d2Sampled = interp.sampleCubicHermiteCurves( xForSamplingAlong, d2ForSamplingAlong, elementsCountAlong, arcLengthDerivatives=True)[0:2] xInnerRaw.append(xSampled) d2InnerRaw.append(d2Sampled) # Re-arrange sample order & calculate dx_ds1 and dx_ds3 from dx_ds2 for n2 in range(elementsCountAlong + 1): xAround = [] d2Around = [] for n1 in range(elementsCountAround): x = xInnerRaw[n1][n2] d2 = d2InnerRaw[n1][n2] xAround.append(x) d2Around.append(d2) d1Around = [] for n1 in range(elementsCountAround): v1 = xAround[n1] v2 = xAround[(n1 + 1) % elementsCountAround] d1 = d2 = [v2[c] - v1[c] for c in range(3)] arcLengthAround = interp.computeCubicHermiteArcLength( v1, d1, v2, d2, True) dx_ds1 = [c * arcLengthAround for c in vector.normalise(d1)] d1Around.append(dx_ds1) d1Smoothed = interp.smoothCubicHermiteDerivativesLoop( xAround, d1Around) xToWarp += xAround d1ToWarp += d1Smoothed d2ToWarp += d2Around # Flat width and xi flatWidth = 0.0 xiFace = [] for n1 in range(elementsCountAround): v1 = xAround[n1] d1 = d1Smoothed[n1] v2 = xAround[(n1 + 1) % elementsCountAround] d2 = d1Smoothed[(n1 + 1) % elementsCountAround] flatWidth += interp.getCubicHermiteArcLength(v1, d1, v2, d2) flatWidthList.append(flatWidth) for n1 in range(elementsCountAround + 1): xi = 1.0 / elementsCountAround * n1 xiFace.append(xi) xiList.append(xiFace) # Project reference point for warping onto central path sxRefList, sd1RefList, sd2ProjectedListRef, zRefList = \ tubemesh.getPlaneProjectionOnCentralPath(xToWarp, elementsCountAround, elementsCountAlong, centralPathLength, sx, sd1, sd2, sd12) # Warp points segmentAxis = [0.0, 0.0, 1.0] closedProximalEnd = False innerRadiusAlong = [] for n2 in range(elementsCountAlong + 1): firstNodeAlong = xToWarp[n2 * elementsCountAround] midptSegmentAxis = [0.0, 0.0, elementAlongLength * n2] radius = vector.magnitude(firstNodeAlong[c] - midptSegmentAxis[c] for c in range(3)) innerRadiusAlong.append(radius) xWarpedList, d1WarpedList, d2WarpedList, d3WarpedUnitList = \ tubemesh.warpSegmentPoints(xToWarp, d1ToWarp, d2ToWarp, segmentAxis, sxRefList, sd1RefList, sd2ProjectedListRef, elementsCountAround, elementsCountAlong, zRefList, innerRadiusAlong, closedProximalEnd) # Create coordinates and derivatives transitElementList = [0] * elementsCountAround xList, d1List, d2List, d3List, curvatureList = \ tubemesh.getCoordinatesFromInner(xWarpedList, d1WarpedList, d2WarpedList, d3WarpedUnitList, [wallThickness]*(elementsCountAlong+1), relativeThicknessList, elementsCountAround, elementsCountAlong, elementsCountThroughWall, transitElementList) # Create flat coordinates xFlat, d1Flat, d2Flat = tubemesh.createFlatCoordinates( xiList, flatWidthList, length, wallThickness, relativeThicknessList, elementsCountAround, elementsCountAlong, elementsCountThroughWall, transitElementList) # Create nodes and elements xOrgan = [] d1Organ = [] d2Organ = [] nodeIdentifier, elementIdentifier, annotationGroups = \ tubemesh.createNodesAndElements(region, xList, d1List, d2List, d3List, xFlat, d1Flat, d2Flat, xOrgan, d1Organ, d2Organ, None, elementsCountAround, elementsCountAlong, elementsCountThroughWall, annotationGroupsAround, annotationGroupsAlong, annotationGroupsThroughWall, firstNodeIdentifier, firstElementIdentifier, useCubicHermiteThroughWall, useCrossDerivatives, closedProximalEnd) # annotation fiducial points fm = region.getFieldmodule() fm.beginChange() mesh = fm.findMeshByDimension(3) cache = fm.createFieldcache() markerGroup = findOrCreateFieldGroup(fm, "marker") markerName = findOrCreateFieldStoredString(fm, name="marker_name") markerLocation = findOrCreateFieldStoredMeshLocation( fm, mesh, name="marker_location") nodes = fm.findNodesetByFieldDomainType(Field.DOMAIN_TYPE_NODES) markerPoints = findOrCreateFieldNodeGroup(markerGroup, nodes).getNodesetGroup() markerTemplateInternal = nodes.createNodetemplate() markerTemplateInternal.defineField(markerName) markerTemplateInternal.defineField(markerLocation) markerNames = [ "proximodorsal midpoint on serosa of upper esophageal sphincter", "proximoventral midpoint on serosa of upper esophageal sphincter", "distal point of lower esophageal sphincter serosa on the greater curvature of stomach", "distal point of lower esophageal sphincter serosa on the lesser curvature of stomach" ] totalElements = elementIdentifier radPerElementAround = math.pi * 2.0 / elementsCountAround elementAroundHalfPi = int(0.25 * elementsCountAround) xi1HalfPi = (math.pi * 0.5 - radPerElementAround * elementAroundHalfPi) / radPerElementAround elementAroundPi = int(0.5 * elementsCountAround) xi1Pi = (math.pi - radPerElementAround * elementAroundPi) / radPerElementAround markerElementIdentifiers = [ elementsCountAround * elementsCountThroughWall - elementAroundHalfPi, elementAroundHalfPi + 1 + elementsCountAround * (elementsCountThroughWall - 1), totalElements - elementsCountAround, totalElements - elementsCountAround + elementAroundPi ] markerXis = [[1.0 - xi1HalfPi, 0.0, 1.0], [xi1HalfPi, 0.0, 1.0], [0.0, 1.0, 1.0], [xi1Pi, 1.0, 1.0]] for n in range(len(markerNames)): markerGroup = findOrCreateAnnotationGroupForTerm( annotationGroups, region, get_esophagus_term(markerNames[n])) markerElement = mesh.findElementByIdentifier( markerElementIdentifiers[n]) markerXi = markerXis[n] cache.setMeshLocation(markerElement, markerXi) markerPoint = markerPoints.createNode(nodeIdentifier, markerTemplateInternal) nodeIdentifier += 1 cache.setNode(markerPoint) markerName.assignString(cache, markerGroup.getName()) markerLocation.assignMeshLocation(cache, markerElement, markerXi) for group in [esophagusGroup, markerGroup]: group.getNodesetGroup(nodes).addNode(markerPoint) fm.endChange() return annotationGroups
def createCylinderMesh3d(self, fieldModule, coordinates): """ Create an extruded shape (ellipse/circle) mesh. Currently limited to ellipse or circle base with the alongAxis perpendicular to the base. :param fieldModule: Zinc fieldModule to create elements in. :param coordinates: Coordinate field to define. :return: Final values of nextNodeIdentifier, nextElementIdentifier. """ assert (self._elementsCountAlong > 0), 'createCylinderMesh3d: Invalid number of along elements' assert (self._elementsCountAcrossMinor > 3), 'createCylinderMesh3d: Invalid number of across elements' assert (self._elementsCountAcrossMinor % 2 == 0), 'createCylinderMesh3d: number of across elements' \ ' is not an even number' assert (self._elementsCountAcrossMajor > 1), 'createCylinderMesh3d: Invalid number of up elements' assert (self._cylinderShape in [self._cylinderShape.CYLINDER_SHAPE_FULL, self._cylinderShape.CYLINDER_SHAPE_LOWER_HALF]), \ 'createCylinderMesh3d: Invalid cylinder mode.' nodes = fieldModule.findNodesetByFieldDomainType( Field.DOMAIN_TYPE_NODES) mesh = fieldModule.findMeshByDimension(3) elementsCountRim = self._elementsCountAcrossRim shieldMode = ShieldShape2D.SHIELD_SHAPE_FULL if self._cylinderShape is self._cylinderShape.CYLINDER_SHAPE_FULL \ else ShieldShape2D.SHIELD_SHAPE_LOWER_HALF ellipseShape = EllipseShape.Ellipse_SHAPE_FULL \ if self._cylinderShape is self._cylinderShape.CYLINDER_SHAPE_FULL else EllipseShape.Ellipse_SHAPE_LOWER_HALF self._shield = ShieldMesh2D(self._elementsCountAcrossMinor, self._elementsCountAcrossMajor, elementsCountRim, None, self._elementsCountAlong, shieldMode, shieldType=ShieldRimDerivativeMode. SHIELD_RIM_DERIVATIVE_MODE_AROUND) # generate ellipses mesh along cylinder axis n3Count = 0 if self._cylinderType == CylinderType.CYLINDER_STRAIGHT else self._elementsCountAlong self._ellipses = [] for n3 in range(n3Count + 1): ellipse = Ellipse2D(self._centres[n3], self._majorAxis[n3], self._minorAxis[n3], self._elementsCountAcrossMajor, self._elementsCountAcrossMinor, self._elementsCountAcrossShell, self._elementsCountAcrossTransition, self._shellProportion, self._coreMajorRadii[n3], self._coreMinorRadii[n3], ellipseShape=ellipseShape) self._ellipses.append(ellipse) self.copyEllipsesNodesToShieldNodes(n3) for n3 in range(n3Count + 1): self.calculateD2Derivatives(n3, n3Count) if self._cylinderType == CylinderType.CYLINDER_TAPERED: self.smoothd2Derivatives() # The other ellipses for a straight cylinder. if self._cylinderType == CylinderType.CYLINDER_STRAIGHT: arcLengthAlong = vector.magnitude( self._base._alongAxis) / self._elementsCountAlong for n2 in range(self._elementsCountUp + 1): for n3 in range(self._elementsCountAlong + 1): for n1 in range(self._elementsCountAcrossMinor + 1): if self._shield.px[0][n2][n1]: temx = [ self._shield.px[0][n2][n1][c] + n3 * arcLengthAlong * vector.normalise(self._base._alongAxis)[c] for c in range(3) ] self._shield.px[n3][n2][n1] = temx self._shield.pd1[n3][n2][n1] = self._shield.pd1[0][ n2][n1] self._shield.pd2[n3][n2][n1] = self._shield.pd2[0][ n2][n1] self._shield.pd3[n3][n2][n1] = self._shield.pd3[0][ n2][n1] self.generateNodes(nodes, fieldModule, coordinates) self.generateElements(mesh, fieldModule, coordinates) if self._end is None: self._end = CylinderEnds( self._elementsCountAcrossMajor, self._elementsCountAcrossMinor, self._elementsCountAcrossShell, self._elementsCountAcrossTransition, self._shellProportion, self._centres[-1], self._shield.pd2[-1][0][1], vector.setMagnitude(self._base._majorAxis, self._majorRadii[-1]), self._minorRadii[-1]) self.setEndsNodes()