def createEftTetrahedronXi1Zero(self, nodeScaleFactorOffset0, nodeScaleFactorOffset1): ''' Create a bicubic hermite linear element field for a solid tetrahedron for the apex of cecum, with xi1 and xi3 collapsed on xi2 = 0, and xi3 collapsed on xi1 = 0, xi2 = 1. Each collapsed node on xi2 = 0 has 3 scale factors giving the cos, sin coefficients of the radial line from global derivatives, plus the arc subtended by the element in radians, so the circumferential direction is rounded. Need to create a new template for each sector around axis giving common nodeScaleFactorOffset values on common faces. Suggestion is to start at 0 and add 10000 for each radial line around axis. :param nodeScaleFactorOffset0: offset of node scale factors at axis on xi1=0 :param nodeScaleFactorOffset1: offset of node scale factors at axis on xi1=1 :return: Element field template ''' # start with full bicubic hermite linear eft = self._mesh.createElementfieldtemplate(self._basis) for n in [2, 3, 6, 7]: eft.setFunctionNumberOfTerms(n * 4 + 4, 0) # GRC: allow scale factor identifier for global -1.0 to be prescribed setEftScaleFactorIds(eft, [1], [ nodeScaleFactorOffset0 + 1, nodeScaleFactorOffset0 + 2, nodeScaleFactorOffset0 + 3, nodeScaleFactorOffset1 + 1, nodeScaleFactorOffset1 + 2, nodeScaleFactorOffset1 + 3 ]) # remap parameters on xi2 = 0 before collapsing nodes remapEftNodeValueLabel(eft, [1, 2, 5, 6], Node.VALUE_LABEL_D_DS1, []) for layer in range(2): soAround = 1 ln = layer * 4 + 1 # 2 terms for d/dxi2 via general linear map: remapEftNodeValueLabel(eft, [ln], Node.VALUE_LABEL_D_DS2, [(Node.VALUE_LABEL_D_DS1, [soAround + 1]), (Node.VALUE_LABEL_D_DS2, [soAround + 2])]) # 2 terms for cross derivative 1 2 to correct circular apex: cos(theta).phi, -sin(theta).phi remapEftNodeValueLabel( eft, [ln], Node.VALUE_LABEL_D2_DS1DS2, [(Node.VALUE_LABEL_D_DS1, [soAround + 2, soAround + 3]), (Node.VALUE_LABEL_D_DS2, [1, soAround + 1, soAround + 3])]) ln = layer * 4 + 2 # 2 terms for d/dxi2 via general linear map: remapEftNodeValueLabel(eft, [ln], Node.VALUE_LABEL_D_DS2, [(Node.VALUE_LABEL_D_DS1, [soAround + 4]), (Node.VALUE_LABEL_D_DS2, [soAround + 5])]) # 2 terms for cross derivative 1 2 to correct circular apex: cos(theta).phi, -sin(theta).phi remapEftNodeValueLabel( eft, [ln], Node.VALUE_LABEL_D2_DS1DS2, [(Node.VALUE_LABEL_D_DS1, [soAround + 5, soAround + 6]), (Node.VALUE_LABEL_D_DS2, [1, soAround + 4, soAround + 6])]) ln_map = [1, 1, 2, 3, 1, 1, 2, 4] remapEftLocalNodes(eft, 4, ln_map) assert eft.validate( ), 'eftfactory_bicubichermitelinear.createEftTetrahedronXi1Zero: Failed to validate eft' return eft
def createEftSplitXi1RightStraight(self): ''' Create an element field template suitable for the inner elements of the join between left and right chambers, with xi1 bifurcating to right. Straight through version. Only works with linearAxis 2. :return: Element field template ''' assert linearAxis == 2, 'eftfactory_bicubichermitelinear.createEftSplitXi1RightStraight: Not linearAxis 2' eft = self.createEftNoCrossDerivatives() setEftScaleFactorIds(eft, [1], []) remapEftNodeValueLabel(eft, [5, 7], self._d_ds1, [(self._d_ds1, []), (self._d_ds2, [1])]) remapEftNodeValueLabel(eft, [6, 8], self._d_ds1, [(self._d_ds1, []), (self._d_ds2, [])]) assert eft.validate( ), 'eftfactory_bicubichermitelinear.createEftSplitXi1RightStraight: Failed to validate eft' return eft
def _remapDefaultNodeDerivatives(self, eft): ''' Remap the Hermite node derivatives to those chosen in __init__. Use only on first create. :param eft: The element field template to remap. ''' # must do d_ds2 first! if self._d_ds2 != Node.VALUE_LABEL_D_DS2: remapEftNodeValueLabel(eft, range(1, 9), Node.VALUE_LABEL_D_DS2, [(self._d_ds2, [])]) if self._d_ds1 != Node.VALUE_LABEL_D_DS1: remapEftNodeValueLabel(eft, range(1, 9), Node.VALUE_LABEL_D_DS1, [(self._d_ds1, [])]) if self._d2_ds1ds2 != Node.VALUE_LABEL_D2_DS1DS2: remapEftNodeValueLabel(eft, range(1, 9), Node.VALUE_LABEL_D2_DS1DS2, [(self._d2_ds1ds2, [])])
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 createEftShellPoleBottom(self, nodeScaleFactorOffset0, nodeScaleFactorOffset1): ''' Create a bicubic hermite linear element field for closing bottom pole of a shell. Element is collapsed in xi1 on xi2 = 0. Each collapsed node has 3 scale factors giving the cos, sin coefficients of the radial line from global derivatives, plus the arc subtended by the element in radians, so the pole can be rounded. Need to create a new template for each sector around pole giving common nodeScaleFactorOffset values on common faces. Suggestion is to start at 0 and add 100 for each radial line around pole. :param nodeScaleFactorOffset0: offset of node scale factors at pole on xi1=0 :param nodeScaleFactorOffset1: offset of node scale factors at pole on xi1=1 :return: Element field template ''' # start with full bicubic hermite linear to remap D2_DS1DS2 at pole eft = self._mesh.createElementfieldtemplate(self._basis) if not self._useCrossDerivatives: for n in [2, 3, 6, 7]: eft.setFunctionNumberOfTerms(n * 4 + 4, 0) # GRC: allow scale factor identifier for global -1.0 to be prescribed setEftScaleFactorIds(eft, [1], [ nodeScaleFactorOffset0 + 1, nodeScaleFactorOffset0 + 2, nodeScaleFactorOffset0 + 3, nodeScaleFactorOffset1 + 1, nodeScaleFactorOffset1 + 2, nodeScaleFactorOffset1 + 3, nodeScaleFactorOffset0 + 1, nodeScaleFactorOffset0 + 2, nodeScaleFactorOffset0 + 3, nodeScaleFactorOffset1 + 1, nodeScaleFactorOffset1 + 2, nodeScaleFactorOffset1 + 3 ]) # remap parameters before collapsing nodes remapEftNodeValueLabel(eft, [1, 2, 5, 6], Node.VALUE_LABEL_D_DS1, []) for layer in range(2): so = layer * 6 + 1 ln = layer * 4 + 1 # 2 terms for d/dxi2 via general linear map: remapEftNodeValueLabel(eft, [ln], Node.VALUE_LABEL_D_DS2, [(Node.VALUE_LABEL_D_DS1, [so + 1]), (Node.VALUE_LABEL_D_DS2, [so + 2])]) # 2 terms for cross derivative 1 2 to correct circular pole: -sin(theta).phi, cos(theta).phi remapEftNodeValueLabel( eft, [ln], Node.VALUE_LABEL_D2_DS1DS2, [(Node.VALUE_LABEL_D_DS1, [so + 2, so + 3]), (Node.VALUE_LABEL_D_DS2, [1, so + 1, so + 3])]) ln = layer * 4 + 2 # 2 terms for d/dxi2 via general linear map: remapEftNodeValueLabel(eft, [ln], Node.VALUE_LABEL_D_DS2, [(Node.VALUE_LABEL_D_DS1, [so + 4]), (Node.VALUE_LABEL_D_DS2, [so + 5])]) # 2 terms for cross derivative 1 2 to correct circular pole: -sin(theta).phi, cos(theta).phi remapEftNodeValueLabel( eft, [ln], Node.VALUE_LABEL_D2_DS1DS2, [(Node.VALUE_LABEL_D_DS1, [so + 5, so + 6]), (Node.VALUE_LABEL_D_DS2, [1, so + 4, so + 6])]) ln_map = [1, 1, 2, 3, 4, 4, 5, 6] remapEftLocalNodes(eft, 6, ln_map) assert eft.validate( ), 'eftfactory_tricubichermite.createEftShellPoleBottom: Failed to validate eft' return eft
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: None """ parameterSetName = options['Base parameter set'] isDefault = 'Default' in parameterSetName isMouse = 'Mouse' in parameterSetName isMean = 'mean' in parameterSetName fm = region.getFieldmodule() nodes = fm.findNodesetByFieldDomainType(Field.DOMAIN_TYPE_NODES) coordinates = findOrCreateFieldCoordinates(fm) mesh = fm.findMeshByDimension(3) cache = fm.createFieldcache() 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) armCount = 3 elementLengthCentral = options['Element width central'] elementLengths = [ options['Element length along arm'], options['Element width across arm'], options['Element thickness'] ] elementsCountsAlongArms = options['Numbers of elements along arms'] elementsCount2 = 2 elementsCount3 = 1 useCrossDerivatives = False # arm group annotations for user armTerms, _ = getAutomaticArmFaceTerms(armCount) armGroups = [AnnotationGroup(region, armTerm) for armTerm in armTerms] stellateTerm = get_stellate_term( "cervicothoracic ganglion") if isMouse else ("stellate", None) stellateGroup = AnnotationGroup(region, stellateTerm) annotationGroups = [stellateGroup] + armGroups armMeshGroups = [a.getMeshGroup(mesh) for a in armGroups] stellateMeshGroup = stellateGroup.getMeshGroup(mesh) # markers with element number and xi position allMarkers = {} if isMouse: xProportion = {} xProportion['ICN'] = 0.9 xProportion['VA'] = 0.9 xProportion['DA'] = 0.9 xProportion['C8'] = 0.9 xProportion['T1'] = 0.25 xProportion['T2'] = 0.5 xProportion['T3'] = 0.75 xProportion['TST'] = 1 armNumber = {} armNumber['ICN'] = 2 armNumber['VA'] = 2 armNumber['DA'] = 3 armNumber['C8'] = 3 armNumber['T1'] = 1 armNumber['T2'] = 1 armNumber['T3'] = 1 armNumber['TST'] = 1 nerveAbbrev = list(xProportion.keys()) elementIndex = {} xi1 = {} for nerve in nerveAbbrev: elementIndex[nerve] = int( xProportion[nerve] * elementsCountsAlongArms[armNumber[nerve] - 1]) xi1[nerve] = 1 if xProportion[nerve] == 1 else xProportion[ nerve] * elementsCountsAlongArms[armNumber[nerve] - 1] - elementIndex[nerve] elementIndex[nerve] += 1 if xProportion[nerve] < 1 else 0 allMarkers = { "Inferior cardiac nerve": { "elementID": elementIndex['ICN'] + 2 * elementsCountsAlongArms[0], "xi": [xi1['ICN'], 0.0, 0.5] }, "Ventral ansa subclavia": { "elementID": elementIndex['VA'] + 2 * elementsCountsAlongArms[0] + elementsCountsAlongArms[1], "xi": [xi1['VA'], 1.0, 0.5] }, "Dorsal ansa subclavia": { "elementID": elementIndex['DA'] + 2 * (elementsCountsAlongArms[0] + elementsCountsAlongArms[1]), "xi": [xi1['DA'], 0.0, 0.5] }, "Cervical spinal nerve 8": { "elementID": elementIndex['C8'] + 2 * (elementsCountsAlongArms[0] + elementsCountsAlongArms[1]) + elementsCountsAlongArms[2], "xi": [xi1['C8'], 1.0, 0.5] }, "Thoracic spinal nerve 1": { "elementID": elementIndex['T1'], "xi": [xi1['T1'], 0.0, 0.5] }, "Thoracic spinal nerve 2": { "elementID": elementIndex['T2'], "xi": [xi1['T2'], 0.0, 0.5] }, "Thoracic spinal nerve 3": { "elementID": elementIndex['T3'], "xi": [xi1['T3'], 0.0, 0.5] }, "Thoracic sympathetic nerve trunk": { "elementID": elementIndex['TST'], "xi": [xi1['TST'], 1.0, 0.5] }, } markerGroup = findOrCreateFieldGroup(fm, "marker") markerName = findOrCreateFieldStoredString(fm, name="marker_name") markerLocation = findOrCreateFieldStoredMeshLocation( fm, mesh, name="marker_location") markerPoints = findOrCreateFieldNodeGroup(markerGroup, nodes).getNodesetGroup() markerTemplateInternal = nodes.createNodetemplate() markerTemplateInternal.defineField(markerName) markerTemplateInternal.defineField(markerLocation) # Create nodes nodeIdentifier = 1 minArmAngle = 2 * math.pi / armCount halfArmArcAngleRadians = minArmAngle / 2 if not isMean: dipMultiplier = 1 for na in range(armCount): elementsCount_i = [ elementsCountsAlongArms[na], elementsCount2, elementsCount3 ] x, ds1, ds2, nWheelEdge = createArm(halfArmArcAngleRadians, elementLengths, elementLengthCentral, elementsCount_i, dipMultiplier, armCount, na) for ix in range(len(x)): if na == 0 or ix not in nWheelEdge: node = nodes.createNode(nodeIdentifier, nodetemplate) cache.setNode(node) coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_VALUE, 1, x[ix]) coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS1, 1, ds1[ix]) coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS2, 1, ds2[ix]) nodeIdentifier += 1 else: x_dx_all = cls.mouseMeanMesh['meshEdits'] xyz_all = [x[0] for x in x_dx_all] dxyz = [[x[1], x[2]] for x in x_dx_all] nodeIdentifier = 1 for i, nx in enumerate(xyz_all): node = nodes.createNode(nodeIdentifier, nodetemplate) cache.setNode(node) coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_VALUE, 1, nx) coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS1, 1, dxyz[i][0]) coordinates.setNodeParameters(cache, -1, Node.VALUE_LABEL_D_DS2, 1, dxyz[i][1]) nodeIdentifier += 1 nodesCountsPerArm = [0] + [((elementsCount2 + 1) * e + 1) * 2 for e in elementsCountsAlongArms] # Create elements bicubichermitelinear = eftfactory_bicubichermitelinear( mesh, useCrossDerivatives) eft = bicubichermitelinear.createEftNoCrossDerivatives( ) #createEftBasic() elementtemplate = mesh.createElementtemplate() elementtemplate.setElementShapeType(Element.SHAPE_TYPE_CUBE) elementtemplate.defineField(coordinates, -1, eft) elementtemplateX = mesh.createElementtemplate() elementtemplateX.setElementShapeType(Element.SHAPE_TYPE_CUBE) elementIdentifier = 1 cumNodesCountsPerArm = [ sum(nodesCountsPerArm[:i + 1]) for i in range(len(nodesCountsPerArm)) ] nCentre = [ elementsCountsAlongArms[0] + 1, int(nodesCountsPerArm[1] / 2) + elementsCountsAlongArms[0] + 1 ] for na in range(armCount): for e3 in range(elementsCount3): for e2 in range(elementsCount2): for e1 in range(elementsCountsAlongArms[na]): scalefactors = None ### NODES ### no2 = (elementsCountsAlongArms[na] + 1) no3 = (elementsCount2 + 1) * no2 - 2 offset = (cumNodesCountsPerArm[na]) bni = e3 * no3 + e2 * no2 + e1 + 1 + offset if e2 == 0: if e1 == 0 and na > 0: # and na < armCount -1: # wheelSouth nWh = cumNodesCountsPerArm[na - 1] + ( 2 * elementsCountsAlongArms[na - 1]) + 2 nplUq = int( nodesCountsPerArm[na + 1] / 2 ) - elementsCountsAlongArms[ na] # unused nodes at centre and shared edge npl = int( nodesCountsPerArm[na + 1] / 2) # nodes at centre and shared edge if na < armCount - 1: cn = cumNodesCountsPerArm[ na] + elementsCountsAlongArms[na] - 2 no2 = cumNodesCountsPerArm[na] em = elementsCountsAlongArms[na] nwPrev = [ nWh, nWh + int(nodesCountsPerArm[na] / 2) ] # previous arm's edge, depends on armCount. nodeIdentifiers = [ nwPrev[0], no2 + 1, nCentre[0], no2 + em, nwPrev[1], no2 + em - 1 + nplUq, nCentre[1], bni + (4 * em) - 2 ] else: nplPrev = int( nodesCountsPerArm[na] / 2) - 2 no2 = elementsCountsAlongArms[na] - 1 no3 = int( nodesCountsPerArm[na + 1] / 2) - 3 nwPrev = [ cumNodesCountsPerArm[na - 1] + 2 * (elementsCountsAlongArms[na - 1]), cumNodesCountsPerArm[na - 1] + 2 * (elementsCountsAlongArms[na - 1]) + nplPrev ] start = cumNodesCountsPerArm[na] - 3 nodeIdentifiers = [ nwPrev[0], start, nCentre[0], start + no2, nwPrev[1], start + no3, nCentre[1], start + no2 + no3 ] elif e1 == elementsCountsAlongArms[ na] - 1: # armEnd, south if na == 0: nodeIdentifiers = [ bni, bni + no2 - 1, bni + no2, bni + no3, bni + no2 + no3 - 1, bni + no2 + no3 ] else: no3 = armCount * elementsCountsAlongArms[ na] - 1 no2 = elementsCountsAlongArms[na] if na > 1: bni -= 4 no3 -= 1 nodeIdentifiers = [ bni - 1, bni + no2 - 2, bni + no2 - 1, bni + no3 - 1, bni + no2 - 2 + no3, bni + no2 + no3 - 1 ] elif na > 0 and e1 > 0: # [na=1+, e1=1+, e2=0] for len=3+ bni -= 1 + ((armCount + 1) * (na - 1)) no2 = elementsCountsAlongArms[na] no3 = armCount * no2 - (na - 1) - 1 nodeIdentifiers = [ bni, bni + 1, bni + no2 - 1, bni + no2, bni + no3, bni + no3 + 1, bni + no2 + no3 - 1, bni + no2 + no3 ] else: nodeIdentifiers = [ bni, bni + 1, bni + no2 - 1, bni + no2, bni + no3, bni + no3 + 1, bni + no2 + no3 - 1, bni + no2 + no3 ] else: if e1 == 0 and na > 0: # and na < armCount -1: # wheelNorth if na < armCount - 1: bni -= armCount npl = int( nodesCountsPerArm[na + 1] / 2) - 2 no2 = elementsCountsAlongArms[na] nodeIdentifiers = [ nCentre[0], bni + 1, bni + no2 + 1, bni + no2 + 2, nCentre[1], bni + npl + 1, bni + npl + no2 + 1, bni + npl + no2 + 2 ] else: # last arm bni = cumNodesCountsPerArm[na] - 2 - ( armCount - elementsCountsAlongArms[na]) nodeIdentifiers = [ nCentre[0], bni + 1, 1, bni + no2, nCentre[1], bni + no3 - 2, int(nodesCountsPerArm[1] / 2) + 1, bni + no2 + no3 - armCount ] elif e1 == elementsCountsAlongArms[ na] - 1: # armEnd north if na > 0: no2 = elementsCountsAlongArms[na] nplUq = int( nodesCountsPerArm[na + 1] / 2) - 2 if na > 1: adj = na - 1 bni -= armCount * na + ( armCount - elementsCountsAlongArms[na]) + 1 if elementsCountsAlongArms[na] < 3: bni += 1 if elementsCountsAlongArms[na] > 3: bni -= elementsCountsAlongArms[ na] - 3 no2 += 1 - adj no3 = nplUq - adj nodeIdentifiers = [ bni, bni + 1, bni + no2, bni + no3, bni + no3 + 1, bni + no2 + no3 ] else: bni -= armCount nodeIdentifiers = [ bni, bni + 1, bni + no2 + 1, bni + nplUq, bni + nplUq + 1, bni + no2 + nplUq + 1 ] else: nodeIdentifiers = [ bni - 1, bni, bni + no2 - 1, bni + no3 - 1, bni + no3, bni + no2 + no3 - 1 ] elif na > 0 and e1 > 0: # [na=1+, e1=1+, e2=1] for len=3+ adj = na - 1 bni -= armCount * na + adj no2 -= adj k = armCount * elementsCountsAlongArms[na] - na nodeIdentifiers = [ bni, bni + 1, bni + no2, bni + no2 + 1, bni + k, bni + k + 1, bni + no2 + k, bni + no2 + k + 1 ] else: nodeIdentifiers = [ bni - 1, bni, bni + no2 - 1, bni + no2, bni + no3 - 1, bni + no3, bni + no2 + no3 - 1, bni + no2 + no3 ] if e1 == 0: # wheel eft1 = bicubichermitelinear.createEftNoCrossDerivatives( ) if armCount == 3: if e2 == 0: setEftScaleFactorIds(eft1, [1], []) scalefactors = [-1.0] scaleEftNodeValueLabels( eft1, [1, 5], [ Node.VALUE_LABEL_D_DS1, Node.VALUE_LABEL_D_DS2 ], [1]) ns = [3, 7] else: ns = [1, 5] if na == 0: remapEftNodeValueLabel( eft1, ns, Node.VALUE_LABEL_D_DS1, [(Node.VALUE_LABEL_D_DS1, []), (Node.VALUE_LABEL_D_DS2, [])]) if e2 == 0: setEftScaleFactorIds(eft1, [1], []) scalefactors = [-1.0] remapEftNodeValueLabel( eft1, ns, Node.VALUE_LABEL_D_DS2, [(Node.VALUE_LABEL_D_DS1, [1])]) elif na == 1: setEftScaleFactorIds(eft1, [1], []) scalefactors = [-1.0] remapEftNodeValueLabel( eft1, ns, Node.VALUE_LABEL_D_DS1, [(Node.VALUE_LABEL_D_DS1, [1])]) if e2 == 0: remapEftNodeValueLabel( eft1, ns, Node.VALUE_LABEL_D_DS2, [(Node.VALUE_LABEL_D_DS2, [1])]) elif e2 == 1: remapEftNodeValueLabel( eft1, ns, Node.VALUE_LABEL_D_DS2, [(Node.VALUE_LABEL_D_DS1, [1]), (Node.VALUE_LABEL_D_DS2, [1])]) elif na == 2: setEftScaleFactorIds(eft1, [1], []) scalefactors = [-1.0] remapEftNodeValueLabel( eft1, ns, Node.VALUE_LABEL_D_DS1, [(Node.VALUE_LABEL_D_DS2, [1])]) if e2 == 0: remapEftNodeValueLabel( eft1, ns, Node.VALUE_LABEL_D_DS2, [(Node.VALUE_LABEL_D_DS1, []), (Node.VALUE_LABEL_D_DS2, [])]) elif e2 == 1: remapEftNodeValueLabel( eft1, ns, Node.VALUE_LABEL_D_DS2, [(Node.VALUE_LABEL_D_DS1, [])]) elif armCount == 4: if e2 == 0: setEftScaleFactorIds(eft1, [1], []) scalefactors = [-1.0] scaleEftNodeValueLabels( eft1, [1, 5], [ Node.VALUE_LABEL_D_DS1, Node.VALUE_LABEL_D_DS2 ], [1]) ns = [3, 7] else: ns = [1, 5] if na == 0: remapEftNodeValueLabel( eft1, ns, Node.VALUE_LABEL_D_DS1, [(Node.VALUE_LABEL_D_DS1, []), (Node.VALUE_LABEL_D_DS2, [])]) if e2 == 0: setEftScaleFactorIds(eft1, [1], []) scalefactors = [-1.0] remapEftNodeValueLabel( eft1, ns, Node.VALUE_LABEL_D_DS2, [(Node.VALUE_LABEL_D_DS1, [1])]) elif na == 1: setEftScaleFactorIds(eft1, [1], []) scalefactors = [-1.0] remapEftNodeValueLabel( eft1, ns, Node.VALUE_LABEL_D_DS1, [(Node.VALUE_LABEL_D_DS1, [1]), (Node.VALUE_LABEL_D_DS2, [])]) if e2 == 0: remapEftNodeValueLabel( eft1, ns, Node.VALUE_LABEL_D_DS2, [(Node.VALUE_LABEL_D_DS2, [1])]) else: remapEftNodeValueLabel( eft1, ns, Node.VALUE_LABEL_D_DS2, [(Node.VALUE_LABEL_D_DS1, [1])]) elif na == 2: setEftScaleFactorIds(eft1, [1], []) scalefactors = [-1.0] remapEftNodeValueLabel( eft1, ns, Node.VALUE_LABEL_D_DS1, [(Node.VALUE_LABEL_D_DS1, [1]), (Node.VALUE_LABEL_D_DS2, [1])]) if e2 == 0: remapEftNodeValueLabel( eft1, ns, Node.VALUE_LABEL_D_DS2, [(Node.VALUE_LABEL_D_DS1, [])]) else: remapEftNodeValueLabel( eft1, ns, Node.VALUE_LABEL_D_DS2, [(Node.VALUE_LABEL_D_DS2, [1])]) elif na == 3: setEftScaleFactorIds(eft1, [1], []) scalefactors = [-1.0] remapEftNodeValueLabel( eft1, ns, Node.VALUE_LABEL_D_DS1, [(Node.VALUE_LABEL_D_DS1, []), (Node.VALUE_LABEL_D_DS2, [1])]) if e2 == 0: remapEftNodeValueLabel( eft1, ns, Node.VALUE_LABEL_D_DS2, [(Node.VALUE_LABEL_D_DS2, [])]) else: remapEftNodeValueLabel( eft1, ns, Node.VALUE_LABEL_D_DS2, [(Node.VALUE_LABEL_D_DS1, [])]) elif e1 < (elementsCountsAlongArms[na] - 1): eft1 = eft elementtemplate1 = elementtemplate else: # rounded ends of arms. Collapse xi2 at xi1 = 1 eft1 = bicubichermitelinear.createEftNoCrossDerivatives( ) remapEftNodeValueLabel(eft1, [2, 4, 6, 8], Node.VALUE_LABEL_D_DS2, []) if e2 == 0: remapEftNodeValueLabel( eft1, [2, 6], Node.VALUE_LABEL_D_DS1, [(Node.VALUE_LABEL_D_DS2, [])]) nodeIdentifiers = [ nodeIdentifiers[0], nodeIdentifiers[2], nodeIdentifiers[1], nodeIdentifiers[3], nodeIdentifiers[5], nodeIdentifiers[4] ] else: # e2 == 1 setEftScaleFactorIds(eft1, [1], []) scalefactors = [-1.0] remapEftNodeValueLabel( eft1, [4, 8], Node.VALUE_LABEL_D_DS1, [(Node.VALUE_LABEL_D_DS2, [1])]) ln_map = [1, 2, 3, 2, 4, 5, 6, 5] remapEftLocalNodes(eft1, 6, ln_map) if eft1 is not eft: elementtemplateX.defineField(coordinates, -1, eft1) elementtemplate1 = elementtemplateX element = mesh.createElement(elementIdentifier, elementtemplate1) result = element.setNodesByIdentifier( eft1, nodeIdentifiers) result3 = element.setScaleFactors( eft1, scalefactors) if scalefactors else None # add to meshGroup stellateMeshGroup.addElement(element) armMeshGroups[na].addElement(element) elementIdentifier += 1 # annotation fiducial points if isMouse: for key in allMarkers: xi = allMarkers[key]["xi"] addMarker = {"name": key, "xi": allMarkers[key]["xi"]} markerPoint = markerPoints.createNode(nodeIdentifier, markerTemplateInternal) nodeIdentifier += 1 cache.setNode(markerPoint) markerName.assignString(cache, addMarker["name"]) elementID = allMarkers[key]["elementID"] element = mesh.findElementByIdentifier(elementID) markerLocation.assignMeshLocation(cache, element, addMarker["xi"]) return annotationGroups
def make_tube_bifurcation_elements_2d(region, coordinates, elementIdentifier, paNodeId, paStartIndex, c1NodeId, c1StartIndex, c2NodeId, c2StartIndex, roNodeId, coNodeId, useCrossDerivatives=False): ''' Creates elements from parent, ring/row, crotch/column, child1 and child2 nodes. Assumes client has active ChangeManager(fieldmodule). :param paNodeId, paStartIndex, c1NodeId, c1StartIndex, c2NodeId, c2StartIndex: Lists of parent, child1, child2 nodes and their starting index to be at hex2 :param roNodeId, coNodeId: Lists of ring/row and crotch/column nodes, starting at hex2 and between hex1 and hex2, respectively. :return next elementIdentifier. ''' paCount = len(paNodeId) c1Count = len(c1NodeId) c2Count = len(c2NodeId) pac1Count, pac2Count, c1c2Count = get_tube_bifurcation_connection_elements_counts( paCount, c1Count, c2Count) fieldmodule = region.getFieldmodule() mesh = fieldmodule.findMeshByDimension(2) elementtemplateStd = mesh.createElementtemplate() elementtemplateStd.setElementShapeType(Element.SHAPE_TYPE_SQUARE) bicubicHermiteBasis = fieldmodule.createElementbasis( 2, Elementbasis.FUNCTION_TYPE_CUBIC_HERMITE) eftStd = mesh.createElementfieldtemplate(bicubicHermiteBasis) if not useCrossDerivatives: for n in range(4): eftStd.setFunctionNumberOfTerms(n * 4 + 4, 0) elementtemplateStd.defineField(coordinates, -1, eftStd) elementtemplateMod = mesh.createElementtemplate() elementtemplateMod.setElementShapeType(Element.SHAPE_TYPE_SQUARE) for e1 in range(paCount): eft = eftStd elementtemplate = elementtemplateStd np = e1 + paStartIndex nids = [ paNodeId[np % paCount], paNodeId[(np + 1) % paCount], roNodeId[e1], roNodeId[(e1 + 1) % paCount] ] scalefactors = None meshGroups = [] if e1 in (0, pac1Count - 1, pac1Count, paCount - 1): eft = mesh.createElementfieldtemplate(bicubicHermiteBasis) if not useCrossDerivatives: for n in range(4): eft.setFunctionNumberOfTerms(n * 4 + 4, 0) if e1 in (0, pac1Count): scalefactors = [-1.0] setEftScaleFactorIds(eft, [1], []) remapEftNodeValueLabel(eft, [3], Node.VALUE_LABEL_D_DS1, [(Node.VALUE_LABEL_D_DS2, [1])]) remapEftNodeValueLabel(eft, [3], Node.VALUE_LABEL_D_DS2, [(Node.VALUE_LABEL_D_DS1, []), (Node.VALUE_LABEL_D_DS2, [])]) elif e1 in (pac1Count - 1, paCount - 1): remapEftNodeValueLabel(eft, [4], Node.VALUE_LABEL_D_DS2, [(Node.VALUE_LABEL_D_DS1, []), (Node.VALUE_LABEL_D_DS2, [])]) elementtemplateMod.defineField(coordinates, -1, eft) elementtemplate = elementtemplateMod element = mesh.createElement(elementIdentifier, elementtemplate) result2 = element.setNodesByIdentifier(eft, nids) if scalefactors: result3 = element.setScaleFactors(eft, scalefactors) else: result3 = '-' #print('create element tube bifurcation pa', element.isValid(), elementIdentifier, result2, result3, nids) elementIdentifier += 1 for meshGroup in meshGroups: meshGroup.addElement(element) for e1 in range(c1Count): eft = eftStd elementtemplate = elementtemplateStd nr = e1 nc = e1 + c1StartIndex nids = [ roNodeId[nr % paCount], roNodeId[(nr + 1) % paCount], c1NodeId[nc % c1Count], c1NodeId[(nc + 1) % c1Count] ] if e1 >= pac1Count: nids[1] = roNodeId[0] if (e1 == (c1Count - 1)) else coNodeId[e1 - pac1Count] if e1 > pac1Count: #nids[0] = coNodeId[(e1 - pac1Count - 1)] nids[0] = coNodeId[(e1 - pac1Count - 1) % c1Count] scalefactors = None meshGroups = [] if e1 in (0, pac1Count, c1Count - 1): eft = mesh.createElementfieldtemplate(bicubicHermiteBasis) if not useCrossDerivatives: for n in range(4): eft.setFunctionNumberOfTerms(n * 4 + 4, 0) if e1 == 0: scalefactors = [-1.0] setEftScaleFactorIds(eft, [1], []) remapEftNodeValueLabel(eft, [1], Node.VALUE_LABEL_D_DS1, [(Node.VALUE_LABEL_D_DS2, [1])]) remapEftNodeValueLabel(eft, [1], Node.VALUE_LABEL_D_DS2, [(Node.VALUE_LABEL_D_DS1, [])]) elif e1 == pac1Count: remapEftNodeValueLabel(eft, [1], Node.VALUE_LABEL_D_DS1, [(Node.VALUE_LABEL_D_DS1, []), (Node.VALUE_LABEL_D_DS2, [])]) elif e1 == (c1Count - 1): scalefactors = [-1.0] setEftScaleFactorIds(eft, [1], []) remapEftNodeValueLabel(eft, [2], Node.VALUE_LABEL_D_DS1, [(Node.VALUE_LABEL_D_DS1, [1]), (Node.VALUE_LABEL_D_DS2, [1])]) remapEftNodeValueLabel(eft, [2], Node.VALUE_LABEL_D_DS2, [(Node.VALUE_LABEL_D_DS1, [])]) elementtemplateMod.defineField(coordinates, -1, eft) elementtemplate = elementtemplateMod element = mesh.createElement(elementIdentifier, elementtemplate) result2 = element.setNodesByIdentifier(eft, nids) if scalefactors: result3 = element.setScaleFactors(eft, scalefactors) else: result3 = '-' #print('create element tube bifurcation c1', element.isValid(), elementIdentifier, result2, result3, nids) elementIdentifier += 1 for meshGroup in meshGroups: meshGroup.addElement(element) for e1 in range(c2Count): eft = eftStd elementtemplate = elementtemplateStd nr = 0 if (e1 == 0) else (paCount - c2Count + e1) nc = e1 + c2StartIndex nids = [ roNodeId[nr % paCount], roNodeId[(nr + 1) % paCount], c2NodeId[nc % c2Count], c2NodeId[(nc + 1) % c2Count] ] if 0 <= e1 < (c1c2Count - 1): nids[1] = coNodeId[c1c2Count - e1 - 2] if 0 < e1 <= (c1c2Count - 1): nids[0] = coNodeId[c1c2Count - e1 - 1] scalefactors = None meshGroups = [] if e1 <= c1c2Count: eft = mesh.createElementfieldtemplate(bicubicHermiteBasis) if not useCrossDerivatives: for n in range(4): eft.setFunctionNumberOfTerms(n * 4 + 4, 0) scalefactors = [-1.0] setEftScaleFactorIds(eft, [1], []) if e1 == 0: remapEftNodeValueLabel(eft, [1], Node.VALUE_LABEL_D_DS1, [(Node.VALUE_LABEL_D_DS1, []), (Node.VALUE_LABEL_D_DS2, [])]) scaleEftNodeValueLabels( eft, [2], [Node.VALUE_LABEL_D_DS1, Node.VALUE_LABEL_D_DS2], [1]) elif e1 < (c1c2Count - 1): scaleEftNodeValueLabels( eft, [1, 2], [Node.VALUE_LABEL_D_DS1, Node.VALUE_LABEL_D_DS2], [1]) elif e1 == (c1c2Count - 1): scaleEftNodeValueLabels( eft, [1], [Node.VALUE_LABEL_D_DS1, Node.VALUE_LABEL_D_DS2], [1]) remapEftNodeValueLabel(eft, [2], Node.VALUE_LABEL_D_DS1, [(Node.VALUE_LABEL_D_DS1, [1]), (Node.VALUE_LABEL_D_DS2, [1])]) remapEftNodeValueLabel(eft, [2], Node.VALUE_LABEL_D_DS2, [(Node.VALUE_LABEL_D_DS1, [])]) elif e1 == c1c2Count: remapEftNodeValueLabel(eft, [1], Node.VALUE_LABEL_D_DS1, [(Node.VALUE_LABEL_D_DS2, [1])]) remapEftNodeValueLabel(eft, [1], Node.VALUE_LABEL_D_DS2, [(Node.VALUE_LABEL_D_DS1, [])]) elementtemplateMod.defineField(coordinates, -1, eft) elementtemplate = elementtemplateMod element = mesh.createElement(elementIdentifier, elementtemplate) result2 = element.setNodesByIdentifier(eft, nids) if scalefactors: result3 = element.setScaleFactors(eft, scalefactors) else: result3 = '-' #print('create element tube bifurcation c2', element.isValid(), elementIdentifier, result2, result3, nids) elementIdentifier += 1 for meshGroup in meshGroups: meshGroup.addElement(element) return elementIdentifier
def generateBaseMesh(cls, region, options): """ Generate the base tricubic Hermite mesh. :param region: Zinc region to define model in. Must be empty. :param options: Dict containing options. See getDefaultOptions(). :return: list of AnnotationGroup """ # set dependent outer diameter used in atria2 options['Aorta outer plus diameter'] = options[ 'LV outlet inner diameter'] + 2.0 * options[ 'LV outlet wall thickness'] elementsCountAroundAtrialSeptum = options[ 'Number of elements around atrial septum'] elementsCountAroundLeftAtriumFreeWall = options[ 'Number of elements around left atrium free wall'] elementsCountAroundLeftAtrium = elementsCountAroundLeftAtriumFreeWall + elementsCountAroundAtrialSeptum elementsCountAroundRightAtriumFreeWall = options[ 'Number of elements around right atrium free wall'] elementsCountAroundRightAtrium = elementsCountAroundRightAtriumFreeWall + elementsCountAroundAtrialSeptum useCrossDerivatives = False fm = region.getFieldmodule() coordinates = findOrCreateFieldCoordinates(fm) cache = fm.createFieldcache() mesh = fm.findMeshByDimension(3) # generate heartventriclesbase1 model and put atria1 on it ventriclesAnnotationGroups = MeshType_3d_heartventriclesbase1.generateBaseMesh( region, options) atriaAnnotationGroups = MeshType_3d_heartatria1.generateBaseMesh( region, options) annotationGroups = mergeAnnotationGroups(ventriclesAnnotationGroups, atriaAnnotationGroups) lFibrousRingGroup = findOrCreateAnnotationGroupForTerm( annotationGroups, region, get_heart_term("left fibrous ring")) rFibrousRingGroup = findOrCreateAnnotationGroupForTerm( annotationGroups, region, get_heart_term("right fibrous ring")) # annotation fiducial points 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) ############## # Create nodes ############## nodeIdentifier = max(1, getMaximumNodeIdentifier(nodes) + 1) # discover left and right fibrous ring nodes from ventricles and atria # because nodes are iterated in identifier order, the lowest and first are on the lv outlet cfb, right and left on lower outer layers # left fibrous ring lavNodeId = [[[], []], [[], []]] # [n3][n2][n1] iter = lFibrousRingGroup.getNodesetGroup(nodes).createNodeiterator() # left fibrous ring, bottom row cfbNodeId = iter.next().getIdentifier() cfbLeftNodeId = iter.next().getIdentifier() for n1 in range(elementsCountAroundLeftAtrium): lavNodeId[0][0].append(iter.next().getIdentifier()) lavNodeId[1][0].append(cfbNodeId) lavNodeId[1][0].append(cfbLeftNodeId) for n1 in range(elementsCountAroundLeftAtriumFreeWall - 1): lavNodeId[1][0].append(iter.next().getIdentifier()) for n1 in range(elementsCountAroundAtrialSeptum - 1): lavNodeId[1][0].append(None) # no outer node on interatrial septum # left fibrous ring, top row for n1 in range(elementsCountAroundLeftAtrium): lavNodeId[0][1].append(iter.next().getIdentifier()) for n1 in range(elementsCountAroundLeftAtriumFreeWall + 1): lavNodeId[1][1].append(iter.next().getIdentifier()) for n1 in range(elementsCountAroundAtrialSeptum - 1): lavNodeId[1][1].append(None) # no outer node on interatrial septum # right fibrous ring ravNodeId = [[[], []], [[], []]] # [n3][n2][n1] iter = rFibrousRingGroup.getNodesetGroup(nodes).createNodeiterator() cfbNodeId = iter.next().getIdentifier() cfbRightNodeId = iter.next().getIdentifier() # right fibrous ring, bottom row for n1 in range(elementsCountAroundRightAtrium): ravNodeId[0][0].append(iter.next().getIdentifier()) for n1 in range(elementsCountAroundRightAtriumFreeWall - 1): ravNodeId[1][0].append(iter.next().getIdentifier()) ravNodeId[1][0].append(cfbRightNodeId) ravNodeId[1][0].append(cfbNodeId) for n1 in range(elementsCountAroundAtrialSeptum - 1): ravNodeId[1][0].append(None) # no outer node on interatrial septum # right fibrous ring, top row for n1 in range(elementsCountAroundRightAtrium): ravNodeId[0][1].append(iter.next().getIdentifier()) cfbUpperNodeId = iter.next().getIdentifier( ) # cfb from left will be first for n1 in range(elementsCountAroundRightAtriumFreeWall): ravNodeId[1][1].append(iter.next().getIdentifier()) ravNodeId[1][1].append(cfbUpperNodeId) for n1 in range(elementsCountAroundAtrialSeptum - 1): ravNodeId[1][1].append(None) # no outer node on interatrial septum #for n2 in range(2): # print('n2', n2) # print('lavNodeId[0]', lavNodeId[0][n2]) # print('lavNodeId[1]', lavNodeId[1][n2]) # print('ravNodeId[0]', ravNodeId[0][n2]) # print('ravNodeId[1]', ravNodeId[1][n2]) ################# # Create elements ################# lFibrousRingMeshGroup = lFibrousRingGroup.getMeshGroup(mesh) rFibrousRingMeshGroup = rFibrousRingGroup.getMeshGroup(mesh) elementIdentifier = getMaximumElementIdentifier(mesh) + 1 elementtemplate1 = mesh.createElementtemplate() elementtemplate1.setElementShapeType(Element.SHAPE_TYPE_CUBE) # create fibrous ring elements bicubichermitelinear = eftfactory_bicubichermitelinear( mesh, useCrossDerivatives, linearAxis=2, d_ds1=Node.VALUE_LABEL_D_DS1, d_ds2=Node.VALUE_LABEL_D_DS3) eftFibrousRing = bicubichermitelinear.createEftBasic() # left fibrous ring, starting at crux / collapsed posterior interatrial sulcus cruxElementId = None for e in range(-1, elementsCountAroundLeftAtriumFreeWall): eft1 = eftFibrousRing n1 = e nids = [ lavNodeId[0][0][n1], lavNodeId[0][0][n1 + 1], lavNodeId[0][1][n1], lavNodeId[0][1][n1 + 1], lavNodeId[1][0][n1], lavNodeId[1][0][n1 + 1], lavNodeId[1][1][n1], lavNodeId[1][1][n1 + 1] ] scalefactors = None meshGroups = [lFibrousRingMeshGroup] if e == -1: # interatrial groove straddles left and right atria, collapsed to 6 node wedge nids[0] = ravNodeId[0][0][ elementsCountAroundRightAtriumFreeWall] nids[2] = ravNodeId[0][1][ elementsCountAroundRightAtriumFreeWall] nids.pop(6) nids.pop(4) eft1 = bicubichermitelinear.createEftNoCrossDerivatives() setEftScaleFactorIds(eft1, [1], []) scalefactors = [-1.0] remapEftNodeValueLabel(eft1, [1, 3], Node.VALUE_LABEL_D_DS1, [(Node.VALUE_LABEL_D_DS1, []), (Node.VALUE_LABEL_D_DS3, [])]) remapEftNodeValueLabel(eft1, [2, 4], Node.VALUE_LABEL_D_DS1, [(Node.VALUE_LABEL_D_DS1, []), (Node.VALUE_LABEL_D_DS3, [1])]) remapEftNodeValueLabel(eft1, [5, 6, 7, 8], Node.VALUE_LABEL_D_DS1, []) # reverse d3 on cfb: remapEftNodeValueLabel(eft1, [5], Node.VALUE_LABEL_D_DS3, [(Node.VALUE_LABEL_D_DS1, [1]), (Node.VALUE_LABEL_D_DS3, [1])]) remapEftNodeValueLabel(eft1, [6], Node.VALUE_LABEL_D_DS3, [(Node.VALUE_LABEL_D_DS1, [0]), (Node.VALUE_LABEL_D_DS3, [1])]) remapEftNodeValueLabel(eft1, [7], Node.VALUE_LABEL_D_DS3, [(Node.VALUE_LABEL_D_DS1, []), (Node.VALUE_LABEL_D_DS3, [])]) remapEftNodeValueLabel(eft1, [8], Node.VALUE_LABEL_D_DS3, [(Node.VALUE_LABEL_D_DS1, [1]), (Node.VALUE_LABEL_D_DS3, [])]) ln_map = [1, 2, 3, 4, 5, 5, 6, 6] remapEftLocalNodes(eft1, 6, ln_map) meshGroups += [rFibrousRingMeshGroup] elif e == 0: # general linear map d3 adjacent to collapsed sulcus eft1 = bicubichermitelinear.createEftNoCrossDerivatives() setEftScaleFactorIds(eft1, [1], []) scalefactors = [-1.0] # reverse d1, d3 on cfb, left cfb: scaleEftNodeValueLabels( eft1, [6], [Node.VALUE_LABEL_D_DS1, Node.VALUE_LABEL_D_DS3], [1]) remapEftNodeValueLabel(eft1, [5], Node.VALUE_LABEL_D_DS1, [(Node.VALUE_LABEL_D_DS1, [1])]) remapEftNodeValueLabel(eft1, [5], Node.VALUE_LABEL_D_DS3, [(Node.VALUE_LABEL_D_DS1, []), (Node.VALUE_LABEL_D_DS3, [1])]) remapEftNodeValueLabel(eft1, [7], Node.VALUE_LABEL_D_DS3, [(Node.VALUE_LABEL_D_DS1, [1]), (Node.VALUE_LABEL_D_DS3, [])]) elif e == 1: # reverse d1, d3 on left cfb: eft1 = bicubichermitelinear.createEftNoCrossDerivatives() setEftScaleFactorIds(eft1, [1], []) scalefactors = [-1.0] remapEftNodeValueLabel(eft1, [5], Node.VALUE_LABEL_D_DS1, [(Node.VALUE_LABEL_D_DS1, [1]), (Node.VALUE_LABEL_D_DS3, [])]) remapEftNodeValueLabel(eft1, [5], Node.VALUE_LABEL_D_DS3, [(Node.VALUE_LABEL_D_DS3, [1])]) elif e == (elementsCountAroundLeftAtriumFreeWall - 1): # general linear map d3 adjacent to collapsed sulcus eft1 = bicubichermitelinear.createEftNoCrossDerivatives() setEftScaleFactorIds(eft1, [1], []) scalefactors = [-1.0] remapEftNodeValueLabel(eft1, [6, 8], Node.VALUE_LABEL_D_DS3, [(Node.VALUE_LABEL_D_DS1, []), (Node.VALUE_LABEL_D_DS3, [])]) result = elementtemplate1.defineField(coordinates, -1, eft1) element = mesh.createElement(elementIdentifier, elementtemplate1) result2 = element.setNodesByIdentifier(eft1, nids) result3 = element.setScaleFactors( eft1, scalefactors) if scalefactors else None #print('create element fibrous ring left', elementIdentifier, result, result2, result3, nids) elementIdentifier += 1 for meshGroup in meshGroups: meshGroup.addElement(element) # right fibrous ring, starting at crux / collapsed posterior interatrial sulcus for e in range(-1, elementsCountAroundRightAtriumFreeWall): eft1 = eftFibrousRing n1 = e nids = [ ravNodeId[0][0][n1], ravNodeId[0][0][n1 + 1], ravNodeId[0][1][n1], ravNodeId[0][1][n1 + 1], ravNodeId[1][0][n1], ravNodeId[1][0][n1 + 1], ravNodeId[1][1][n1], ravNodeId[1][1][n1 + 1] ] scalefactors = None meshGroups = [rFibrousRingMeshGroup] if e == -1: # interatrial groove straddles left and right atria, collapsed to 6 node wedge nids[0] = lavNodeId[0][0][ elementsCountAroundLeftAtriumFreeWall] nids[2] = lavNodeId[0][1][ elementsCountAroundLeftAtriumFreeWall] nids.pop(6) nids.pop(4) eft1 = bicubichermitelinear.createEftNoCrossDerivatives() setEftScaleFactorIds(eft1, [1], []) scalefactors = [-1.0] remapEftNodeValueLabel(eft1, [1, 3], Node.VALUE_LABEL_D_DS1, [(Node.VALUE_LABEL_D_DS1, []), (Node.VALUE_LABEL_D_DS3, [])]) remapEftNodeValueLabel(eft1, [2, 4], Node.VALUE_LABEL_D_DS1, [(Node.VALUE_LABEL_D_DS1, []), (Node.VALUE_LABEL_D_DS3, [1])]) remapEftNodeValueLabel(eft1, [5, 6, 7, 8], Node.VALUE_LABEL_D_DS1, []) remapEftNodeValueLabel(eft1, [5, 7], Node.VALUE_LABEL_D_DS3, [(Node.VALUE_LABEL_D_DS1, []), (Node.VALUE_LABEL_D_DS3, [])]) remapEftNodeValueLabel(eft1, [6, 8], Node.VALUE_LABEL_D_DS3, [(Node.VALUE_LABEL_D_DS1, [1]), (Node.VALUE_LABEL_D_DS3, [])]) ln_map = [1, 2, 3, 4, 5, 5, 6, 6] remapEftLocalNodes(eft1, 6, ln_map) meshGroups += [lFibrousRingMeshGroup] cruxElementId = elementIdentifier elif e == 0: # general linear map d3 adjacent to collapsed crux/posterior sulcus eft1 = bicubichermitelinear.createEftNoCrossDerivatives() setEftScaleFactorIds(eft1, [1], []) scalefactors = [-1.0] remapEftNodeValueLabel(eft1, [5, 7], Node.VALUE_LABEL_D_DS3, [(Node.VALUE_LABEL_D_DS1, [1]), (Node.VALUE_LABEL_D_DS3, [])]) elif e == (elementsCountAroundRightAtriumFreeWall - 2): # reverse d1, d3 on right cfb: eft1 = bicubichermitelinear.createEftNoCrossDerivatives() setEftScaleFactorIds(eft1, [1], []) scalefactors = [-1.0] remapEftNodeValueLabel(eft1, [6], Node.VALUE_LABEL_D_DS1, [(Node.VALUE_LABEL_D_DS1, [1]), (Node.VALUE_LABEL_D_DS3, [1])]) remapEftNodeValueLabel(eft1, [6], Node.VALUE_LABEL_D_DS3, [(Node.VALUE_LABEL_D_DS3, [1])]) elif e == (elementsCountAroundRightAtriumFreeWall - 1): # general linear map d3 adjacent to collapsed cfb/anterior sulcus eft1 = bicubichermitelinear.createEftNoCrossDerivatives() setEftScaleFactorIds(eft1, [1], []) scalefactors = [-1.0] # reverse d1, d3 on right cfb, cfb: scaleEftNodeValueLabels( eft1, [5], [Node.VALUE_LABEL_D_DS1, Node.VALUE_LABEL_D_DS3], [1]) remapEftNodeValueLabel(eft1, [6], Node.VALUE_LABEL_D_DS1, [(Node.VALUE_LABEL_D_DS1, [1])]) remapEftNodeValueLabel(eft1, [6], Node.VALUE_LABEL_D_DS3, [(Node.VALUE_LABEL_D_DS1, [1]), (Node.VALUE_LABEL_D_DS3, [1])]) remapEftNodeValueLabel(eft1, [8], Node.VALUE_LABEL_D_DS3, [(Node.VALUE_LABEL_D_DS1, []), (Node.VALUE_LABEL_D_DS3, [])]) result = elementtemplate1.defineField(coordinates, -1, eft1) element = mesh.createElement(elementIdentifier, elementtemplate1) result2 = element.setNodesByIdentifier(eft1, nids) result3 = element.setScaleFactors( eft1, scalefactors) if scalefactors else None #print('create element fibrous ring right', elementIdentifier, result, result2, result3, nids) elementIdentifier += 1 for meshGroup in meshGroups: meshGroup.addElement(element) # fibrous ring septum: meshGroups = [lFibrousRingMeshGroup, rFibrousRingMeshGroup] for e in range(elementsCountAroundAtrialSeptum): eft1 = eftFibrousRing nlm = e - elementsCountAroundAtrialSeptum nlp = nlm + 1 nrm = -e nrp = nrm - 1 nids = [ lavNodeId[0][0][nlm], lavNodeId[0][0][nlp], lavNodeId[0][1][nlm], lavNodeId[0][1][nlp], ravNodeId[0][0][nrm], ravNodeId[0][0][nrp], ravNodeId[0][1][nrm], ravNodeId[0][1][nrp] ] eft1 = bicubichermitelinear.createEftNoCrossDerivatives() setEftScaleFactorIds(eft1, [1], []) scalefactors = [-1.0] if e == 0: # general linear map d3 adjacent to collapsed posterior interventricular sulcus scaleEftNodeValueLabels(eft1, [5, 6, 7, 8], [Node.VALUE_LABEL_D_DS1], [1]) scaleEftNodeValueLabels(eft1, [6, 8], [Node.VALUE_LABEL_D_DS3], [1]) remapEftNodeValueLabel(eft1, [1, 3], Node.VALUE_LABEL_D_DS3, [(Node.VALUE_LABEL_D_DS1, []), (Node.VALUE_LABEL_D_DS3, [])]) remapEftNodeValueLabel(eft1, [5, 7], Node.VALUE_LABEL_D_DS3, [(Node.VALUE_LABEL_D_DS1, []), (Node.VALUE_LABEL_D_DS3, [1])]) elif e == (elementsCountAroundAtrialSeptum - 1): # general linear map d3 adjacent to cfb scaleEftNodeValueLabels(eft1, [5, 6, 7, 8], [Node.VALUE_LABEL_D_DS1], [1]) scaleEftNodeValueLabels(eft1, [5, 7], [Node.VALUE_LABEL_D_DS3], [1]) remapEftNodeValueLabel(eft1, [2, 4], Node.VALUE_LABEL_D_DS3, [(Node.VALUE_LABEL_D_DS1, [1]), (Node.VALUE_LABEL_D_DS3, [])]) remapEftNodeValueLabel(eft1, [6, 8], Node.VALUE_LABEL_D_DS3, [(Node.VALUE_LABEL_D_DS1, [1]), (Node.VALUE_LABEL_D_DS3, [1])]) else: scaleEftNodeValueLabels( eft1, [5, 6, 7, 8], [Node.VALUE_LABEL_D_DS1, Node.VALUE_LABEL_D_DS3], [1]) result = elementtemplate1.defineField(coordinates, -1, eft1) element = mesh.createElement(elementIdentifier, elementtemplate1) result2 = element.setNodesByIdentifier(eft1, nids) result3 = element.setScaleFactors( eft1, scalefactors) if scalefactors else None #print('create element fibrous ring septum', elementIdentifier, result, result2, result3, nids) elementIdentifier += 1 for meshGroup in meshGroups: meshGroup.addElement(element) # annotation fiducial points cruxElement = mesh.findElementByIdentifier(cruxElementId) cruxXi = [0.5, 0.5, 1.0] cache.setMeshLocation(cruxElement, cruxXi) result, cruxCoordinates = coordinates.evaluateReal(cache, 3) markerPoint = markerPoints.createNode(nodeIdentifier, markerTemplateInternal) nodeIdentifier += 1 cache.setNode(markerPoint) markerName.assignString(cache, "crux of heart") markerLocation.assignMeshLocation(cache, cruxElement, cruxXi) return annotationGroups
def createEftWedgeCollapseXi2Quadrant(self, collapseNodes): ''' Create a bicubic hermite linear element field for a wedge element collapsed in xi2. :param collapseNodes: As the element can be collapsed in xi2 at either ends of xi1 or xi3, collapseNodes are the local indices of nodes whose d1 (for elements collapse at either ends of xi1) or d3 (for elements collapse at either ends of xi3) are remapped with d2 before collapsing the nodes. :return: Element field template ''' eft = self.createEftBasic() valid = True if collapseNodes in [[1, 2], [3, 4]]: # xi3 = 0 nodes = [1, 2, 3, 4] remapEftNodeValueLabel(eft, nodes, Node.VALUE_LABEL_D_DS2, []) ln_map = [1, 2, 1, 2, 3, 4, 5, 6] elif collapseNodes in [[5, 6], [7, 8]]: # xi3 = 1 nodes = [5, 6, 7, 8] remapEftNodeValueLabel(eft, nodes, Node.VALUE_LABEL_D_DS2, []) ln_map = [1, 2, 3, 4, 5, 6, 5, 6] elif collapseNodes in [[3, 7]]: nodes = [1, 3, 5, 7] # remap parameters on xi1 = 0 before collapsing nodes if collapseNodes == [3, 7]: remapEftNodeValueLabel(eft, nodes, Node.VALUE_LABEL_D_DS2, []) remapEftNodeValueLabel(eft, collapseNodes, Node.VALUE_LABEL_D_DS1, [(Node.VALUE_LABEL_D_DS2, [])]) else: valid = False ln_map = [1, 2, 1, 3, 4, 5, 4, 6] elif collapseNodes in [[2, 6], [4, 8]]: nodes = [2, 4, 6, 8] # remap parameters on xi1 = 1 before collapsing nodes if collapseNodes == [2, 6]: remapEftNodeValueLabel(eft, nodes, Node.VALUE_LABEL_D_DS2, []) remapEftNodeValueLabel(eft, collapseNodes, Node.VALUE_LABEL_D_DS1, [(Node.VALUE_LABEL_D_DS2, [])]) elif collapseNodes == [4, 8]: setEftScaleFactorIds(eft, [1], []) remapEftNodeValueLabel(eft, collapseNodes, Node.VALUE_LABEL_D_DS1, [(Node.VALUE_LABEL_D_DS2, [1])]) remapEftNodeValueLabel(eft, nodes, Node.VALUE_LABEL_D_DS2, []) else: valid = False ln_map = [1, 2, 3, 2, 4, 5, 6, 5] else: valid = False if not valid: assert False, "createEftWedgeCollapseXi2Quadrant. Not implemented for collapse nodes " + str( collapseNodes) # zero cross derivative parameters remapEftNodeValueLabel(eft, nodes, Node.VALUE_LABEL_D2_DS1DS2, []) remapEftLocalNodes(eft, 6, ln_map) if not eft.validate(): print( 'eftfactory_bicubichermitelinear.createEftWedgeCollapseXi2Quadrant: Failed to validate eft for collapseNodes', collapseNodes) return eft
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 generateElements(self, fieldmodule, coordinates, startElementIdentifier, meshGroups=[]): """ Create shield elements from nodes. :param fieldmodule: Zinc fieldmodule to create elements in. :param coordinates: Coordinate field to define. :param startElementIdentifier: First element identifier to use. :param meshGroups: Zinc mesh groups to add elements to. :return: next elementIdentifier. """ elementIdentifier = startElementIdentifier useCrossDerivatives = False mesh = fieldmodule.findMeshByDimension(3) tricubichermite = eftfactory_tricubichermite(mesh, useCrossDerivatives) eft = tricubichermite.createEftNoCrossDerivatives() elementtemplate = mesh.createElementtemplate() elementtemplate.setElementShapeType(Element.SHAPE_TYPE_CUBE) elementtemplate.defineField(coordinates, -1, eft) elementtemplate1 = mesh.createElementtemplate() elementtemplate1.setElementShapeType(Element.SHAPE_TYPE_CUBE) isEven = (self.elementsCountAcross % 2) == 0 e1a = self.elementsCountRim e1b = e1a + 1 e1z = self.elementsCountAcross - 1 - self.elementsCountRim e1y = e1z - 1 e2a = self.elementsCountRim e2b = self.elementsCountRim + 1 e2c = self.elementsCountRim + 2 e2d = 2 * self.elementsCountUp - 1 for e3 in range(self.elementsCountAlong): for e2 in range(self.elementsCountUpFull): for e1 in range(self.elementsCountAcross): eft1 = eft scalefactors = None if self._type == ShieldRimDerivativeMode.SHIELD_RIM_DERIVATIVE_MODE_AROUND: nids = [ self.nodeId[e3][e2][e1], self.nodeId[e3][e2 + 1][e1], self.nodeId[e3 + 1][e2][e1], self.nodeId[e3 + 1][e2 + 1][e1], self.nodeId[e3][e2][e1 + 1], self.nodeId[e3][e2 + 1][e1 + 1], self.nodeId[e3 + 1][e2][e1 + 1], self.nodeId[e3 + 1][e2 + 1][e1 + 1] ] elif self._type == ShieldRimDerivativeMode.SHIELD_RIM_DERIVATIVE_MODE_REGULAR: nids = [ self.nodeId[0][e2][e1], self.nodeId[0][e2][e1 + 1], self.nodeId[0][e2 + 1][e1], self.nodeId[0][e2 + 1][e1 + 1], self.nodeId[1][e2][e1], self.nodeId[1][e2][e1 + 1], self.nodeId[1][e2 + 1][e1], self.nodeId[1][e2 + 1][e1 + 1] ] if (e2 < e2b) or (e2 == e2d): if (e1 < e1b) or (e1 > e1y): continue # no element due to triple point closure if (e2 == e2a) or (e2 == e2d): # bottom and top row elements if self._type == ShieldRimDerivativeMode.SHIELD_RIM_DERIVATIVE_MODE_AROUND: if e2 == e2a: eft1 = tricubichermite.createEftNoCrossDerivatives( ) setEftScaleFactorIds(eft1, [1], []) scalefactors = [-1.0] remapEftNodeValueLabel( eft1, [1, 3, 5, 7], Node.VALUE_LABEL_D_DS1, [(Node.VALUE_LABEL_D_DS3, [1])]) remapEftNodeValueLabel( eft1, [1, 3, 5, 7], Node.VALUE_LABEL_D_DS3, [(Node.VALUE_LABEL_D_DS1, [])]) if (e1 == e1b) or (e1 == e1y): # map bottom triple point element if e1 == e1b: remapEftNodeValueLabel( eft1, [2, 4], Node.VALUE_LABEL_D_DS1, [(Node.VALUE_LABEL_D_DS1, []), (Node.VALUE_LABEL_D_DS3, [])]) else: remapEftNodeValueLabel( eft1, [6, 8], Node.VALUE_LABEL_D_DS1, [(Node.VALUE_LABEL_D_DS1, []), (Node.VALUE_LABEL_D_DS3, [1]) ]) elif e2 == e2d: eft1 = tricubichermite.createEftNoCrossDerivatives( ) setEftScaleFactorIds(eft1, [1], []) scalefactors = [-1.0] remapEftNodeValueLabel( eft1, [2, 4, 6, 8], Node.VALUE_LABEL_D_DS3, [(Node.VALUE_LABEL_D_DS1, [1])]) remapEftNodeValueLabel( eft1, [2, 4, 6, 8], Node.VALUE_LABEL_D_DS1, [(Node.VALUE_LABEL_D_DS3, [])]) if (e1 == e1b) or (e1 == e1y): # map top triple point element if e1 == e1b: remapEftNodeValueLabel( eft1, [1, 3], Node.VALUE_LABEL_D_DS1, [(Node.VALUE_LABEL_D_DS1, []), (Node.VALUE_LABEL_D_DS3, [1]) ]) else: remapEftNodeValueLabel( eft1, [5, 7], Node.VALUE_LABEL_D_DS1, [(Node.VALUE_LABEL_D_DS1, []), (Node.VALUE_LABEL_D_DS3, [])]) elif self._type == ShieldRimDerivativeMode.SHIELD_RIM_DERIVATIVE_MODE_REGULAR: if (e1 == e1b) or (e1 == e1y): # map bottom triple point element eft1 = tricubichermite.createEftNoCrossDerivatives( ) setEftScaleFactorIds(eft1, [1], []) scalefactors = [-1.0] if e1 == e1b: remapEftNodeValueLabel( eft1, [3, 7], Node.VALUE_LABEL_D_DS2, [(Node.VALUE_LABEL_D_DS1, []), (Node.VALUE_LABEL_D_DS2, [])]) else: remapEftNodeValueLabel( eft1, [4, 8], Node.VALUE_LABEL_D_DS2, [(Node.VALUE_LABEL_D_DS1, [1]), (Node.VALUE_LABEL_D_DS2, [])]) elif (e2 == e2b) or (e2 == e2d - e2b): if (e1 <= e1a) or (e1 >= e1z): # map top 2 triple point elements eft1 = tricubichermite.createEftNoCrossDerivatives( ) setEftScaleFactorIds(eft1, [1], []) scalefactors = [-1.0] if e1 < e1a: e2r = e1 nids[0] = self.nodeId[0][e2r][e1b] nids[1] = self.nodeId[0][e2r + 1][e1b] nids[4] = self.nodeId[1][e2r][e1b] nids[5] = self.nodeId[1][e2r + 1][e1b] remapEftNodeValueLabel( eft1, [1, 2, 5, 6], Node.VALUE_LABEL_D_DS2, [(Node.VALUE_LABEL_D_DS1, [1])]) remapEftNodeValueLabel( eft1, [1, 2, 5, 6], Node.VALUE_LABEL_D_DS1, [(Node.VALUE_LABEL_D_DS2, [])]) elif e1 == e1a: if self._type == ShieldRimDerivativeMode.SHIELD_RIM_DERIVATIVE_MODE_AROUND: if e2 == e2b: nids[0] = self.nodeId[e3][e2a][e1b] nids[2] = self.nodeId[e3 + 1][e2a][e1b] tripleN = [5, 7] remapEftNodeValueLabel( eft1, tripleN, Node.VALUE_LABEL_D_DS3, [(Node.VALUE_LABEL_D_DS1, []), (Node.VALUE_LABEL_D_DS3, [])]) elif e2 == e2d - e2b: nids[1] = self.nodeId[e3][e2d + 1][e1b] nids[3] = self.nodeId[e3 + 1][e2d + 1][e1b] tripleN = [6, 8] remapEftNodeValueLabel( eft1, tripleN, Node.VALUE_LABEL_D_DS3, [(Node.VALUE_LABEL_D_DS1, [1]), (Node.VALUE_LABEL_D_DS3, [])]) remapEftNodeValueLabel( eft1, [1, 2, 3, 4], Node.VALUE_LABEL_D_DS1, [(Node.VALUE_LABEL_D_DS1, [1])]) remapEftNodeValueLabel( eft1, [1, 2, 3, 4], Node.VALUE_LABEL_D_DS3, [(Node.VALUE_LABEL_D_DS3, [1])]) elif self._type == ShieldRimDerivativeMode.SHIELD_RIM_DERIVATIVE_MODE_REGULAR: nids[0] = self.nodeId[0][e2a][e1b] nids[4] = self.nodeId[1][e2a][e1b] remapEftNodeValueLabel( eft1, [1, 5], Node.VALUE_LABEL_D_DS2, [(Node.VALUE_LABEL_D_DS1, [1])]) remapEftNodeValueLabel( eft1, [1, 5], Node.VALUE_LABEL_D_DS1, [(Node.VALUE_LABEL_D_DS2, [])]) remapEftNodeValueLabel( eft1, [2, 6], Node.VALUE_LABEL_D_DS1, [(Node.VALUE_LABEL_D_DS1, []), (Node.VALUE_LABEL_D_DS2, [])]) elif e1 == e1z: if self._type == ShieldRimDerivativeMode.SHIELD_RIM_DERIVATIVE_MODE_AROUND: if e2 == e2b: nids[4] = self.nodeId[e3][e2a][e1z] nids[6] = self.nodeId[e3 + 1][e2a][e1z] remapEftNodeValueLabel( eft1, [1, 3], Node.VALUE_LABEL_D_DS3, [(Node.VALUE_LABEL_D_DS1, [1]), (Node.VALUE_LABEL_D_DS3, [])]) elif e2 == e2d - e2b: nids[5] = self.nodeId[e3][e2d + 1][e1z] nids[7] = self.nodeId[e3 + 1][e2d + 1][e1z] remapEftNodeValueLabel( eft1, [2, 4], Node.VALUE_LABEL_D_DS3, [(Node.VALUE_LABEL_D_DS1, []), (Node.VALUE_LABEL_D_DS3, [])]) elif self._type == ShieldRimDerivativeMode.SHIELD_RIM_DERIVATIVE_MODE_REGULAR: nids[1] = self.nodeId[0][e2a][e1z] nids[5] = self.nodeId[1][e2a][e1z] remapEftNodeValueLabel( eft1, [1, 5], Node.VALUE_LABEL_D_DS1, [(Node.VALUE_LABEL_D_DS1, []), (Node.VALUE_LABEL_D_DS2, [1])]) remapEftNodeValueLabel( eft1, [2, 6], Node.VALUE_LABEL_D_DS1, [(Node.VALUE_LABEL_D_DS2, [1])]) remapEftNodeValueLabel( eft1, [2, 6], Node.VALUE_LABEL_D_DS2, [(Node.VALUE_LABEL_D_DS1, [])]) elif e1 > e1z: e2r = self.elementsCountAcross - e1 nids[0] = self.nodeId[0][e2r][e1z] nids[1] = self.nodeId[0][e2r - 1][e1z] nids[4] = self.nodeId[1][e2r][e1z] nids[5] = self.nodeId[1][e2r - 1][e1z] remapEftNodeValueLabel( eft1, [1, 2, 5, 6], Node.VALUE_LABEL_D_DS1, [(Node.VALUE_LABEL_D_DS2, [1])]) remapEftNodeValueLabel( eft1, [1, 2, 5, 6], Node.VALUE_LABEL_D_DS2, [(Node.VALUE_LABEL_D_DS1, [])]) else: if self._type == ShieldRimDerivativeMode.SHIELD_RIM_DERIVATIVE_MODE_AROUND: if (e1 <= e1a): # map left column elements eft1 = tricubichermite.createEftNoCrossDerivatives( ) setEftScaleFactorIds(eft1, [1], []) scalefactors = [-1.0] remapEftNodeValueLabel( eft1, [1, 2, 3, 4], Node.VALUE_LABEL_D_DS1, [(Node.VALUE_LABEL_D_DS1, [1])]) remapEftNodeValueLabel( eft1, [1, 2, 3, 4], Node.VALUE_LABEL_D_DS3, [(Node.VALUE_LABEL_D_DS3, [1])]) if eft1 is not eft: elementtemplate1.defineField(coordinates, -1, eft1) element = mesh.createElement(elementIdentifier, elementtemplate1) else: element = mesh.createElement(elementIdentifier, elementtemplate) result2 = element.setNodesByIdentifier(eft1, nids) if scalefactors: result3 = element.setScaleFactors(eft1, scalefactors) else: result3 = 7 #print('create element shield', elementIdentifier, result2, result3, nids) self.elementId[e2][e1] = elementIdentifier elementIdentifier += 1 for meshGroup in meshGroups: meshGroup.addElement(element) return elementIdentifier
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